Merge "Require a more specific intent" 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 cd34f98..e8f6e6b 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -84,6 +84,24 @@
     },
 }
 
+java_defaults {
+    name: "framework-module-stubs-lib-defaults-publicapi",
+    installable: false,
+    sdk_version: "module_current",
+}
+
+java_defaults {
+    name: "framework-module-stubs-lib-defaults-systemapi",
+    installable: false,
+    sdk_version: "module_current",
+}
+
+java_defaults {
+    name: "framework-module-stubs-lib-defaults-module_libs_api",
+    installable: false,
+    sdk_version: "module_current",
+}
+
 // The defaults for module_libs comes in two parts - defaults for API checks
 // and defaults for stub generation. This is because we want the API txt
 // files to *only* include the module_libs_api, but the stubs to include
diff --git a/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
index 8fbfb1d..d99830dc4 100644
--- a/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
@@ -7,6 +7,7 @@
       ],
       "options": [
         {"include-filter": "com.android.server.DeviceIdleControllerTest"},
+        {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
         {"exclude-annotation": "androidx.test.filters.FlakyTest"}
       ]
     }
diff --git a/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING
index bc7a7d3..b76c582 100644
--- a/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING
@@ -4,6 +4,7 @@
       "name": "FrameworksMockingServicesTests",
       "options": [
         {"include-filter": "com.android.server.DeviceIdleControllerTest"},
+        {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
         {"exclude-annotation": "androidx.test.filters.FlakyTest"}
       ]
     }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
index e2e1180..484fec3 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
@@ -3,6 +3,7 @@
         {
             "name": "CtsJobSchedulerTestCases",
             "options": [
+                {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
                 {"exclude-annotation": "androidx.test.filters.FlakyTest"},
                 {"exclude-annotation": "androidx.test.filters.LargeTest"}
             ]
@@ -11,6 +12,7 @@
             "name": "FrameworksMockingServicesTests",
             "options": [
                 {"include-filter": "com.android.server.job"},
+                {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
                 {"exclude-annotation": "androidx.test.filters.FlakyTest"}
             ]
         },
@@ -18,6 +20,7 @@
             "name": "FrameworksServicesTests",
             "options": [
                 {"include-filter": "com.android.server.job"},
+                {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
                 {"exclude-annotation": "androidx.test.filters.FlakyTest"}
             ]
         }
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
index ba7572a..c5dc51c 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
@@ -4,6 +4,7 @@
       "name": "CtsUsageStatsTestCases",
       "options": [
         {"include-filter": "android.app.usage.cts.UsageStatsTest"},
+        {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
         {"exclude-annotation": "androidx.test.filters.FlakyTest"}
       ]
     },
@@ -11,6 +12,7 @@
       "name": "FrameworksServicesTests",
       "options": [
         {"include-filter": "com.android.server.usage"},
+        {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
         {"exclude-annotation": "androidx.test.filters.FlakyTest"}
       ]
     }
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index 821dd9e..99e82e7 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -132,19 +132,19 @@
 java_library {
     name: "framework-media-stubs-publicapi",
     srcs: [":framework-media-stubs-srcs-publicapi"],
-    sdk_version: "current",
+    defaults: ["framework-module-stubs-lib-defaults-publicapi"],
 }
 
 java_library {
     name: "framework-media-stubs-systemapi",
     srcs: [":framework-media-stubs-srcs-systemapi"],
-    sdk_version: "system_current",
+    defaults: ["framework-module-stubs-lib-defaults-systemapi"],
 }
 
 java_library {
     name: "framework-media-stubs-module_libs_api",
     srcs: [":framework-media-stubs-srcs-module_libs_api"],
-    sdk_version: "system_current",
+    defaults: ["framework-module-stubs-lib-defaults-module_libs_api"],
 }
 
 java_library {
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/sdkextensions/framework/Android.bp b/apex/sdkextensions/framework/Android.bp
index 86f4ab7..707113b 100644
--- a/apex/sdkextensions/framework/Android.bp
+++ b/apex/sdkextensions/framework/Android.bp
@@ -86,7 +86,7 @@
 java_library {
     name: "framework-sdkextensions-stubs-publicapi",
     srcs: [":framework-sdkextensions-stubs-srcs-publicapi"],
-    sdk_version: "current",
+    defaults: ["framework-module-stubs-lib-defaults-publicapi"],
     visibility: [
         "//frameworks/base", // Framework
         "//frameworks/base/apex/sdkextensions", // sdkextensions SDK
@@ -96,7 +96,7 @@
 java_library {
     name: "framework-sdkextensions-stubs-systemapi",
     srcs: [":framework-sdkextensions-stubs-srcs-systemapi"],
-    sdk_version: "system_current",
+    defaults: ["framework-module-stubs-lib-defaults-systemapi"],
     visibility: [
         "//frameworks/base", // Framework
         "//frameworks/base/apex/sdkextensions", // sdkextensions SDK
@@ -106,7 +106,7 @@
 java_library {
     name: "framework-sdkextensions-stubs-module_libs_api",
     srcs: [":framework-sdkextensions-stubs-srcs-module_libs_api"],
-    sdk_version: "system_current",
+    defaults: ["framework-module-stubs-lib-defaults-module_libs_api"],
     visibility: [
         "//frameworks/base", // Framework
         "//frameworks/base/apex/sdkextensions", // sdkextensions SDK
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 680b224..eb16b5e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -278,7 +278,7 @@
     field public static final int activityCloseExitAnimation = 16842939; // 0x10100bb
     field public static final int activityOpenEnterAnimation = 16842936; // 0x10100b8
     field public static final int activityOpenExitAnimation = 16842937; // 0x10100b9
-    field public static final int actor = 16844313; // 0x1010619
+    field public static final int actor = 16844312; // 0x1010618
     field public static final int addPrintersActivity = 16843750; // 0x10103e6
     field public static final int addStatesFromChildren = 16842992; // 0x10100f0
     field public static final int adjustViewBounds = 16843038; // 0x101011e
@@ -289,7 +289,6 @@
     field public static final int alignmentMode = 16843642; // 0x101037a
     field public static final int allContactsName = 16843468; // 0x10102cc
     field public static final int allowAudioPlaybackCapture = 16844289; // 0x1010601
-    field public static final int allowAutoRevokePermissionsExemption = 16844309; // 0x1010615
     field public static final int allowBackup = 16843392; // 0x1010280
     field public static final int allowClearUserData = 16842757; // 0x1010005
     field public static final int allowEmbedded = 16843765; // 0x10103f5
@@ -329,6 +328,7 @@
     field public static final int autoLink = 16842928; // 0x10100b0
     field public static final int autoMirrored = 16843754; // 0x10103ea
     field public static final int autoRemoveFromRecents = 16843847; // 0x1010447
+    field public static final int autoRevokePermissions = 16844308; // 0x1010614
     field public static final int autoSizeMaxTextSize = 16844102; // 0x1010546
     field public static final int autoSizeMinTextSize = 16844088; // 0x1010538
     field public static final int autoSizePresetSizes = 16844087; // 0x1010537
@@ -710,7 +710,7 @@
     field public static final int gravity = 16842927; // 0x10100af
     field public static final int gridViewStyle = 16842865; // 0x1010071
     field public static final int groupIndicator = 16843019; // 0x101010b
-    field public static final int gwpAsanMode = 16844312; // 0x1010618
+    field public static final int gwpAsanMode = 16844311; // 0x1010617
     field public static final int hand_hour = 16843011; // 0x1010103
     field public static final int hand_minute = 16843012; // 0x1010104
     field public static final int handle = 16843354; // 0x101025a
@@ -955,7 +955,7 @@
     field public static final int mediaRouteButtonStyle = 16843693; // 0x10103ad
     field public static final int mediaRouteTypes = 16843694; // 0x10103ae
     field public static final int menuCategory = 16843230; // 0x10101de
-    field public static final int mimeGroup = 16844311; // 0x1010617
+    field public static final int mimeGroup = 16844310; // 0x1010616
     field public static final int mimeType = 16842790; // 0x1010026
     field public static final int min = 16844089; // 0x1010539
     field public static final int minAspectRatio = 16844187; // 0x101059b
@@ -1084,7 +1084,7 @@
     field public static final int preferenceScreenStyle = 16842891; // 0x101008b
     field public static final int preferenceStyle = 16842894; // 0x101008e
     field public static final int presentationTheme = 16843712; // 0x10103c0
-    field public static final int preserveLegacyExternalStorage = 16844310; // 0x1010616
+    field public static final int preserveLegacyExternalStorage = 16844309; // 0x1010615
     field public static final int previewImage = 16843482; // 0x10102da
     field public static final int primaryContentAlpha = 16844114; // 0x1010552
     field public static final int priority = 16842780; // 0x101001c
@@ -1141,7 +1141,6 @@
     field public static final int reqKeyboardType = 16843304; // 0x1010228
     field public static final int reqNavigation = 16843306; // 0x101022a
     field public static final int reqTouchScreen = 16843303; // 0x1010227
-    field public static final int requestAutoRevokePermissionsExemption = 16844308; // 0x1010614
     field public static final int requestLegacyExternalStorage = 16844291; // 0x1010603
     field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
     field public static final int required = 16843406; // 0x101028e
@@ -25341,11 +25340,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 0bd8a19..ba6feed 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -535,6 +535,7 @@
     method public void setWindowingMode(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int ACTIVITY_TYPE_ASSISTANT = 4; // 0x4
+    field public static final int ACTIVITY_TYPE_DREAM = 5; // 0x5
     field public static final int ACTIVITY_TYPE_HOME = 2; // 0x2
     field public static final int ACTIVITY_TYPE_RECENTS = 3; // 0x3
     field public static final int ACTIVITY_TYPE_STANDARD = 1; // 0x1
@@ -1833,7 +1834,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..86a78f09 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/Android.bp b/cmds/statsd/Android.bp
index 4529dff..6fe098c 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -378,55 +378,55 @@
 // statsd micro benchmark
 //#############################
 
-//cc_benchmark {
-//    name: "statsd_benchmark",
-//    defaults: ["statsd_defaults"],
-//
-//    srcs: [
-//        // atom_field_options.proto needs field_options.proto, but that is
-//        // not included in libprotobuf-cpp-lite, so compile it here.
-//        ":libprotobuf-internal-protos",
-//
-//        "benchmark/duration_metric_benchmark.cpp",
-//        "benchmark/filter_value_benchmark.cpp",
-//        "benchmark/get_dimensions_for_condition_benchmark.cpp",
-//        "benchmark/hello_world_benchmark.cpp",
-//        "benchmark/log_event_benchmark.cpp",
-//        "benchmark/main.cpp",
-//        "benchmark/metric_util.cpp",
-//        "benchmark/stats_write_benchmark.cpp",
-//        "src/atom_field_options.proto",
-//        "src/atoms.proto",
-//        "src/stats_log.proto",
-//    ],
-//
-//    proto: {
-//        type: "lite",
-//        include_dirs: ["external/protobuf/src"],
-//    },
-//
-//    cflags: [
-//        "-Wall",
-//        "-Werror",
-//        "-Wno-unused-parameter",
-//        "-Wno-unused-variable",
-//        "-Wno-unused-function",
-//
-//        // Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374
-//        "-Wno-varargs",
-//    ],
-//
-//    static_libs: [
-//        "libplatformprotos",
-//    ],
-//
-//    shared_libs: [
-//        "libgtest_prod",
-//        "libprotobuf-cpp-lite",
-//        "libstatslog",
-//        "libstatssocket",
-//    ],
-//}
+cc_benchmark {
+    name: "statsd_benchmark",
+    defaults: ["statsd_defaults"],
+
+    srcs: [
+        // atom_field_options.proto needs field_options.proto, but that is
+        // not included in libprotobuf-cpp-lite, so compile it here.
+        ":libprotobuf-internal-protos",
+
+        "benchmark/duration_metric_benchmark.cpp",
+        "benchmark/filter_value_benchmark.cpp",
+        "benchmark/get_dimensions_for_condition_benchmark.cpp",
+        "benchmark/hello_world_benchmark.cpp",
+        "benchmark/log_event_benchmark.cpp",
+        "benchmark/main.cpp",
+        "benchmark/metric_util.cpp",
+        "benchmark/stats_write_benchmark.cpp",
+        "src/atom_field_options.proto",
+        "src/atoms.proto",
+        "src/stats_log.proto",
+    ],
+
+    proto: {
+        type: "lite",
+        include_dirs: ["external/protobuf/src"],
+    },
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+        "-Wno-unused-variable",
+        "-Wno-unused-function",
+
+        // Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374
+        "-Wno-varargs",
+    ],
+
+    static_libs: [
+        "libplatformprotos",
+        "libstatssocket_private",
+    ],
+
+    shared_libs: [
+        "libgtest_prod",
+        "libprotobuf-cpp-lite",
+        "libstatslog",
+    ],
+}
 
 // ====  java proto device library (for test only)  ==============================
 java_library {
diff --git a/cmds/statsd/benchmark/duration_metric_benchmark.cpp b/cmds/statsd/benchmark/duration_metric_benchmark.cpp
index 2631009..2d315d9 100644
--- a/cmds/statsd/benchmark/duration_metric_benchmark.cpp
+++ b/cmds/statsd/benchmark/duration_metric_benchmark.cpp
@@ -137,77 +137,74 @@
     int64_t bucketSizeNs =
             TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
 
-
-    std::vector<AttributionNodeInternal> attributions1 = {
-            CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
-            CreateAttribution(222, "GMSCoreModule2")};
-
-    std::vector<AttributionNodeInternal> attributions2 = {
-            CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
-            CreateAttribution(555, "GMSCoreModule2")};
-
     std::vector<std::unique_ptr<LogEvent>> events;
 
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                   bucketStartTimeNs + 11));
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                   bucketStartTimeNs + 40));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 11,
+                                                   android::view::DISPLAY_STATE_OFF));
+    events.push_back(
+            CreateScreenStateChangedEvent(bucketStartTimeNs + 40, android::view::DISPLAY_STATE_ON));
 
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                   bucketStartTimeNs + 102));
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                   bucketStartTimeNs + 450));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 102,
+                                                   android::view::DISPLAY_STATE_OFF));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 450,
+                                                   android::view::DISPLAY_STATE_ON));
 
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                   bucketStartTimeNs + 650));
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                   bucketStartTimeNs + bucketSizeNs + 100));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 650,
+                                                   android::view::DISPLAY_STATE_OFF));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 100,
+                                                   android::view::DISPLAY_STATE_ON));
 
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                   bucketStartTimeNs + bucketSizeNs + 640));
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                   bucketStartTimeNs + bucketSizeNs + 650));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 640,
+                                                   android::view::DISPLAY_STATE_OFF));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 650,
+                                                   android::view::DISPLAY_STATE_ON));
 
-    events.push_back(CreateStartScheduledJobEvent(
-            {CreateAttribution(9999, "")}, "job0", bucketStartTimeNs + 2));
-    events.push_back(CreateFinishScheduledJobEvent(
-            {CreateAttribution(9999, "")}, "job0",bucketStartTimeNs + 101));
+    vector<int> attributionUids1 = {9999};
+    vector<string> attributionTags1 = {""};
+    events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 2, attributionUids1,
+                                                  attributionTags1, "job0"));
+    events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 101, attributionUids1,
+                                                   attributionTags1, "job0"));
 
-    events.push_back(CreateStartScheduledJobEvent(
-            {CreateAttribution(9999, "")}, "job2", bucketStartTimeNs + 201));
-    events.push_back(CreateFinishScheduledJobEvent(
-            {CreateAttribution(9999, "")}, "job2",bucketStartTimeNs + 500));
+    events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 201, attributionUids1,
+                                                  attributionTags1, "job2"));
+    events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 500, attributionUids1,
+                                                   attributionTags1, "job2"));
 
-    events.push_back(CreateStartScheduledJobEvent(
-            {CreateAttribution(8888, "")}, "job2", bucketStartTimeNs + 600));
-    events.push_back(CreateFinishScheduledJobEvent(
-            {CreateAttribution(8888, "")}, "job2",bucketStartTimeNs + bucketSizeNs + 850));
+    vector<int> attributionUids2 = {8888};
+    events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 600, attributionUids2,
+                                                  attributionTags1, "job2"));
+    events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 850,
+                                                   attributionUids2, attributionTags1, "job2"));
 
-    events.push_back(CreateStartScheduledJobEvent(
-            {CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 600));
-    events.push_back(CreateFinishScheduledJobEvent(
-            {CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 900));
+    events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 600,
+                                                  attributionUids2, attributionTags1, "job1"));
+    events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 900,
+                                                   attributionUids2, attributionTags1, "job1"));
 
-    events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
-                                          bucketStartTimeNs + 10));
-    events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
-                                        bucketStartTimeNs + 50));
+    vector<int> attributionUids3 = {111, 222, 222};
+    vector<string> attributionTags3 = {"App1", "GMSCoreModule1", "GMSCoreModule2"};
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 10, attributionUids3,
+                                          attributionTags3, "ReadEmail"));
+    events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 50, attributionUids3, attributionTags3,
+                                        "ReadEmail"));
 
-    events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
-                                          bucketStartTimeNs + 200));
-    events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
-                                        bucketStartTimeNs + bucketSizeNs + 300));
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 200, attributionUids3,
+                                          attributionTags3, "ReadEmail"));
+    events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 300, attributionUids3,
+                                        attributionTags3, "ReadEmail"));
 
-    events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc",
-                                          bucketStartTimeNs + 400));
-    events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
-                                        bucketStartTimeNs + bucketSizeNs - 1));
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 400, attributionUids3,
+                                          attributionTags3, "ReadDoc"));
+    events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids3,
+                                        attributionTags3, "ReadDoc"));
 
-    events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
-                                          bucketStartTimeNs + 401));
-    events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
-                                        bucketStartTimeNs + bucketSizeNs + 700));
-
+    vector<int> attributionUids4 = {333, 222, 555};
+    vector<string> attributionTags4 = {"App2", "GMSCoreModule1", "GMSCoreModule2"};
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 401, attributionUids4,
+                                          attributionTags4, "ReadEmail"));
+    events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 700, attributionUids4,
+                                        attributionTags4, "ReadEmail"));
     sortLogEventsByTimestamp(&events);
 
     while (state.KeepRunning()) {
@@ -230,78 +227,75 @@
     int64_t bucketSizeNs =
             TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
 
-    std::vector<AttributionNodeInternal> attributions1 = {
-            CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
-            CreateAttribution(222, "GMSCoreModule2")};
-
-    std::vector<AttributionNodeInternal> attributions2 = {
-            CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
-            CreateAttribution(555, "GMSCoreModule2")};
-
-    std::vector<AttributionNodeInternal> attributions3 = {
-            CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"),
-            CreateAttribution(555, "GMSCoreModule2")};
-
     std::vector<std::unique_ptr<LogEvent>> events;
 
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                   bucketStartTimeNs + 55));
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                   bucketStartTimeNs + 120));
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                   bucketStartTimeNs + 121));
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                   bucketStartTimeNs + 450));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 55,
+                                                   android::view::DISPLAY_STATE_OFF));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 120,
+                                                   android::view::DISPLAY_STATE_ON));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 121,
+                                                   android::view::DISPLAY_STATE_OFF));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 450,
+                                                   android::view::DISPLAY_STATE_ON));
 
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                   bucketStartTimeNs + 501));
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                   bucketStartTimeNs + bucketSizeNs + 100));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 501,
+                                                   android::view::DISPLAY_STATE_OFF));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 100,
+                                                   android::view::DISPLAY_STATE_ON));
 
-    events.push_back(CreateStartScheduledJobEvent(
-            {CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1));
-    events.push_back(CreateFinishScheduledJobEvent(
-            {CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101));
+    vector<int> attributionUids1 = {111};
+    vector<string> attributionTags1 = {"App1"};
+    events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 1, attributionUids1,
+                                                  attributionTags1, "job1"));
+    events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 101, attributionUids1,
+                                                   attributionTags1, "job1"));
 
-    events.push_back(CreateStartScheduledJobEvent(
-            {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201));
-    events.push_back(CreateFinishScheduledJobEvent(
-            {CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500));
-    events.push_back(CreateStartScheduledJobEvent(
-            {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600));
-    events.push_back(CreateFinishScheduledJobEvent(
-            {CreateAttribution(333, "App2")}, "job2",
-            bucketStartTimeNs + bucketSizeNs + 850));
+    vector<int> attributionUids2 = {333};
+    vector<string> attributionTags2 = {"App2"};
+    events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 201, attributionUids2,
+                                                  attributionTags2, "job2"));
+    events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 500, attributionUids2,
+                                                   attributionTags2, "job2"));
+    events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 600, attributionUids2,
+                                                  attributionTags2, "job2"));
+    events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 850,
+                                                   attributionUids2, attributionTags2, "job2"));
 
-    events.push_back(
-        CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
-                                     bucketStartTimeNs + bucketSizeNs - 2));
-    events.push_back(
-        CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
-                                      bucketStartTimeNs + bucketSizeNs + 900));
+    vector<int> attributionUids3 = {444};
+    vector<string> attributionTags3 = {"App3"};
+    events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + bucketSizeNs - 2,
+                                                  attributionUids3, attributionTags3, "job3"));
+    events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 900,
+                                                   attributionUids3, attributionTags3, "job3"));
 
-    events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
-                                          bucketStartTimeNs + 50));
-    events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
-                                        bucketStartTimeNs + 110));
+    vector<int> attributionUids4 = {111, 222, 222};
+    vector<string> attributionTags4 = {"App1", "GMSCoreModule1", "GMSCoreModule2"};
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 50, attributionUids4,
+                                          attributionTags4, "ReadEmail"));
+    events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 110, attributionUids4, attributionTags4,
+                                        "ReadEmail"));
 
-    events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
-                                          bucketStartTimeNs + 300));
-    events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
-                                        bucketStartTimeNs + bucketSizeNs + 700));
-    events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc",
-                                          bucketStartTimeNs + 400));
-    events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc",
-                                        bucketStartTimeNs + bucketSizeNs - 1));
+    vector<int> attributionUids5 = {333, 222, 555};
+    vector<string> attributionTags5 = {"App2", "GMSCoreModule1", "GMSCoreModule2"};
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 300, attributionUids5,
+                                          attributionTags5, "ReadEmail"));
+    events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 700, attributionUids5,
+                                        attributionTags5, "ReadEmail"));
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 400, attributionUids5,
+                                          attributionTags5, "ReadDoc"));
+    events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids5,
+                                        attributionTags5, "ReadDoc"));
 
-    events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
-                                          bucketStartTimeNs + 550));
-    events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
-                                        bucketStartTimeNs + 800));
-    events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
-                                          bucketStartTimeNs + bucketSizeNs - 1));
-    events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
-                                        bucketStartTimeNs + bucketSizeNs + 700));
+    vector<int> attributionUids6 = {444, 222, 555};
+    vector<string> attributionTags6 = {"App3", "GMSCoreModule1", "GMSCoreModule2"};
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 550, attributionUids6,
+                                          attributionTags6, "ReadDoc"));
+    events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 800, attributionUids6, attributionTags6,
+                                        "ReadDoc"));
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids6,
+                                          attributionTags6, "ReadDoc"));
+    events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 700, attributionUids6,
+                                        attributionTags6, "ReadDoc"));
     sortLogEventsByTimestamp(&events);
 
     while (state.KeepRunning()) {
diff --git a/cmds/statsd/benchmark/filter_value_benchmark.cpp b/cmds/statsd/benchmark/filter_value_benchmark.cpp
index cfe477d..28bf21a 100644
--- a/cmds/statsd/benchmark/filter_value_benchmark.cpp
+++ b/cmds/statsd/benchmark/filter_value_benchmark.cpp
@@ -19,6 +19,7 @@
 #include "HashableDimensionKey.h"
 #include "logd/LogEvent.h"
 #include "stats_log_util.h"
+#include "stats_event.h"
 
 namespace android {
 namespace os {
@@ -26,17 +27,31 @@
 
 using std::vector;
 
-static void createLogEventAndMatcher(LogEvent* event, FieldMatcher *field_matcher) {
-    AttributionNodeInternal node;
-    node.set_uid(100);
-    node.set_tag("LOCATION");
+static void createLogEventAndMatcher(LogEvent* event, FieldMatcher* field_matcher) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, 1);
+    AStatsEvent_overwriteTimestamp(statsEvent, 100000);
 
-    std::vector<AttributionNodeInternal> nodes = {node, node};
-    event->write(nodes);
-    event->write(3.2f);
-    event->write("LOCATION");
-    event->write((int64_t)990);
-    event->init();
+    std::vector<int> attributionUids = {100, 100};
+    std::vector<string> attributionTags = {"LOCATION", "LOCATION"};
+
+    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_writeFloat(statsEvent, 3.2f);
+    AStatsEvent_writeString(statsEvent, "LOCATION");
+    AStatsEvent_writeInt64(statsEvent, 990);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    event->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
 
     field_matcher->set_field(1);
     auto child = field_matcher->add_child();
@@ -46,7 +61,7 @@
 }
 
 static void BM_FilterValue(benchmark::State& state) {
-    LogEvent event(1, 100000);
+    LogEvent event(/*uid=*/0, /*pid=*/0);
     FieldMatcher field_matcher;
     createLogEventAndMatcher(&event, &field_matcher);
 
diff --git a/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp b/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp
index 2a4403e..c7d01cc 100644
--- a/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp
+++ b/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp
@@ -19,6 +19,7 @@
 #include "HashableDimensionKey.h"
 #include "logd/LogEvent.h"
 #include "stats_log_util.h"
+#include "stats_event.h"
 
 namespace android {
 namespace os {
@@ -27,16 +28,30 @@
 using std::vector;
 
 static void createLogEventAndLink(LogEvent* event, Metric2Condition *link) {
-    AttributionNodeInternal node;
-    node.set_uid(100);
-    node.set_tag("LOCATION");
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, 1);
+    AStatsEvent_overwriteTimestamp(statsEvent, 100000);
 
-    std::vector<AttributionNodeInternal> nodes = {node, node};
-    event->write(nodes);
-    event->write(3.2f);
-    event->write("LOCATION");
-    event->write((int64_t)990);
-    event->init();
+    std::vector<int> attributionUids = {100, 100};
+    std::vector<string> attributionTags = {"LOCATION", "LOCATION"};
+
+    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_writeFloat(statsEvent, 3.2f);
+    AStatsEvent_writeString(statsEvent, "LOCATION");
+    AStatsEvent_writeInt64(statsEvent, 990);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    event->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
 
     link->conditionId = 1;
 
@@ -54,7 +69,7 @@
 
 static void BM_GetDimensionInCondition(benchmark::State& state) {
     Metric2Condition link;
-    LogEvent event(1, 100000);
+    LogEvent event(/*uid=*/0, /*pid=*/0);
     createLogEventAndLink(&event, &link);
 
     while (state.KeepRunning()) {
diff --git a/cmds/statsd/benchmark/log_event_benchmark.cpp b/cmds/statsd/benchmark/log_event_benchmark.cpp
index 8b68743..057e00b 100644
--- a/cmds/statsd/benchmark/log_event_benchmark.cpp
+++ b/cmds/statsd/benchmark/log_event_benchmark.cpp
@@ -39,7 +39,8 @@
     uint8_t msg[LOGGER_ENTRY_MAX_PAYLOAD];
     size_t size = createAndParseStatsEvent(msg);
     while (state.KeepRunning()) {
-        benchmark::DoNotOptimize(LogEvent(msg, size, /*uid=*/ 1000, /*pid=*/ 1001));
+        LogEvent event(/*uid=*/ 1000, /*pid=*/ 1001);
+        benchmark::DoNotOptimize(event.parseBuffer(msg, size));
     }
 }
 BENCHMARK(BM_LogEventCreation);
diff --git a/cmds/statsd/benchmark/metric_util.cpp b/cmds/statsd/benchmark/metric_util.cpp
index cca6d52..4bce89f 100644
--- a/cmds/statsd/benchmark/metric_util.cpp
+++ b/cmds/statsd/benchmark/metric_util.cpp
@@ -14,6 +14,8 @@
 
 #include "metric_util.h"
 
+#include "stats_event.h"
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -246,117 +248,112 @@
 }
 
 std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
-    const android::view::DisplayStateEnum state, uint64_t timestampNs) {
-    auto event = std::make_unique<LogEvent>(android::util::SCREEN_STATE_CHANGED, timestampNs);
-    event->write(state);
-    event->init();
-    return event;
-}
+        uint64_t timestampNs, const android::view::DisplayStateEnum state) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, util::SCREEN_STATE_CHANGED);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
 
-std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(
-    int level, uint64_t timestampNs) {
-    auto event = std::make_unique<LogEvent>(android::util::SCREEN_BRIGHTNESS_CHANGED, timestampNs);
-    (event->write(level));
-    event->init();
-    return event;
+    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> CreateScheduledJobStateChangedEvent(
-        const std::vector<AttributionNodeInternal>& attributions, const string& jobName,
-        const ScheduledJobStateChanged::State state, uint64_t timestampNs) {
-    auto event = std::make_unique<LogEvent>(android::util::SCHEDULED_JOB_STATE_CHANGED, timestampNs);
-    event->write(attributions);
-    event->write(jobName);
-    event->write(state);
-    event->init();
-    return event;
+        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(
-    const std::vector<AttributionNodeInternal>& attributions,
-    const string& name, uint64_t timestampNs) {
-    return CreateScheduledJobStateChangedEvent(
-            attributions, name, ScheduledJobStateChanged::STARTED, timestampNs);
+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(
-    const std::vector<AttributionNodeInternal>& attributions,
-    const string& name, uint64_t timestampNs) {
-    return CreateScheduledJobStateChangedEvent(
-            attributions, name, ScheduledJobStateChanged::FINISHED, timestampNs);
+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(
-        const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
-        const WakelockStateChanged::State state, uint64_t timestampNs) {
-    auto event = std::make_unique<LogEvent>(android::util::WAKELOCK_STATE_CHANGED, timestampNs);
-    event->write(attributions);
-    event->write(android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK);
-    event->write(wakelockName);
-    event->write(state);
-    event->init();
-    return event;
+std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(uint64_t timestampNs,
+                                                      const vector<int>& attributionUids,
+                                                      const vector<string>& attributionTags,
+                                                      const string& name,
+                                                      const SyncStateChanged::State state) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, util::SYNC_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, name.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> CreateAcquireWakelockEvent(
-        const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
-        uint64_t timestampNs) {
-    return CreateWakelockStateChangedEvent(
-        attributions, wakelockName, WakelockStateChanged::ACQUIRE, timestampNs);
+std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs,
+                                               const vector<int>& attributionUids,
+                                               const vector<string>& attributionTags,
+                                               const string& name) {
+    return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name,
+                                       SyncStateChanged::ON);
 }
 
-std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(
-        const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
-        uint64_t timestampNs) {
-    return CreateWakelockStateChangedEvent(
-        attributions, wakelockName, WakelockStateChanged::RELEASE, timestampNs);
-}
-
-std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent(
-    const int uid, const ActivityForegroundStateChanged::State state, uint64_t timestampNs) {
-    auto event = std::make_unique<LogEvent>(
-        android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, timestampNs);
-    event->write(uid);
-    event->write("pkg_name");
-    event->write("class_name");
-    event->write(state);
-    event->init();
-    return event;
-}
-
-std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs) {
-    return CreateActivityForegroundStateChangedEvent(
-        uid, ActivityForegroundStateChanged::BACKGROUND, timestampNs);
-}
-
-std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs) {
-    return CreateActivityForegroundStateChangedEvent(
-        uid, ActivityForegroundStateChanged::FOREGROUND, timestampNs);
-}
-
-std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(
-        const std::vector<AttributionNodeInternal>& attributions, const string& name,
-        const SyncStateChanged::State state, uint64_t timestampNs) {
-    auto event = std::make_unique<LogEvent>(android::util::SYNC_STATE_CHANGED, timestampNs);
-    event->write(attributions);
-    event->write(name);
-    event->write(state);
-    event->init();
-    return event;
-}
-
-std::unique_ptr<LogEvent> CreateSyncStartEvent(
-        const std::vector<AttributionNodeInternal>& attributions, const string& name,
-        uint64_t timestampNs) {
-    return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::ON, timestampNs);
-}
-
-std::unique_ptr<LogEvent> CreateSyncEndEvent(
-        const std::vector<AttributionNodeInternal>& attributions, const string& name,
-        uint64_t timestampNs) {
-    return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::OFF, timestampNs);
+std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs,
+                                             const vector<int>& attributionUids,
+                                             const vector<string>& attributionTags,
+                                             const string& name) {
+    return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name,
+                                       SyncStateChanged::OFF);
 }
 
 sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
diff --git a/cmds/statsd/benchmark/metric_util.h b/cmds/statsd/benchmark/metric_util.h
index 9b28d60..6199fa9 100644
--- a/cmds/statsd/benchmark/metric_util.h
+++ b/cmds/statsd/benchmark/metric_util.h
@@ -94,55 +94,31 @@
 
 // Create log event for screen state changed.
 std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
-    const android::view::DisplayStateEnum state, uint64_t timestampNs);
-
-// Create log event for screen brightness state changed.
-std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(
-   int level, uint64_t timestampNs);
+        uint64_t timestampNs, const android::view::DisplayStateEnum state);
 
 // 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);
-
-// Create log event for app moving to background.
-std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs);
-
-// Create log event for app moving to foreground.
-std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, 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 the app sync starts.
-std::unique_ptr<LogEvent> CreateSyncStartEvent(
-        const std::vector<AttributionNodeInternal>& attributions, const string& name,
-        uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs,
+                                               const vector<int>& attributionUids,
+                                               const vector<string>& attributionTags,
+                                               const string& name);
 
 // Create log event when the app sync ends.
-std::unique_ptr<LogEvent> CreateSyncEndEvent(
-        const std::vector<AttributionNodeInternal>& attributions, const string& name,
-        uint64_t timestampNs);
-
-// Create log event when the app sync ends.
-std::unique_ptr<LogEvent> CreateAppCrashEvent(
-    const int uid, uint64_t timestampNs);
-
-// Create log event for acquiring wakelock.
-std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(
-        const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
-        uint64_t timestampNs);
-
-// Create log event for releasing wakelock.
-std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(
-        const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
-        uint64_t timestampNs);
-
-// Create log event for releasing wakelock.
-std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent(
-    int isolatedUid, int hostUid, bool is_create, uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs,
+                                             const vector<int>& attributionUids,
+                                             const vector<string>& attributionTags,
+                                             const string& name);
 
 // Helper function to create an AttributionNodeInternal proto.
 AttributionNodeInternal CreateAttribution(const int& uid, const string& tag);
@@ -158,4 +134,4 @@
 
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto
index 40a24dc..afee79d 100644
--- a/cmds/statsd/src/atom_field_options.proto
+++ b/cmds/statsd/src/atom_field_options.proto
@@ -117,4 +117,6 @@
     optional bool allow_from_any_uid = 50003 [default = false];
 
     repeated string module = 50004;
+
+    optional bool truncate_timestamp = 50005 [default = false];
 }
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 5f2f546..f8b2749 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 =
@@ -93,7 +94,8 @@
                 10 [(module) = "framework", (module) = "statsdtest"];
         LongPartialWakelockStateChanged long_partial_wakelock_state_changed =
                 11 [(module) = "framework"];
-        MobileRadioPowerStateChanged mobile_radio_power_state_changed = 12 [(module) = "framework"];
+        MobileRadioPowerStateChanged mobile_radio_power_state_changed =
+                12 [(module) = "framework", (truncate_timestamp) = true];
         WifiRadioPowerStateChanged wifi_radio_power_state_changed = 13 [(module) = "framework"];
         ActivityManagerSleepStateChanged activity_manager_sleep_state_changed =
                 14 [(module) = "framework"];
@@ -106,7 +108,8 @@
                 20 [(module) = "framework", (module) = "statsdtest"];
         DeviceIdleModeStateChanged device_idle_mode_state_changed = 21 [(module) = "framework"];
         DeviceIdlingModeStateChanged device_idling_mode_state_changed = 22 [(module) = "framework"];
-        AudioStateChanged audio_state_changed = 23 [(module) = "framework"];
+        AudioStateChanged audio_state_changed =
+                23 [(module) = "framework", (truncate_timestamp) = true];
         MediaCodecStateChanged media_codec_state_changed = 24 [(module) = "framework"];
         CameraStateChanged camera_state_changed = 25 [(module) = "framework"];
         FlashlightStateChanged flashlight_state_changed = 26 [(module) = "framework"];
@@ -127,7 +130,8 @@
         WifiLockStateChanged wifi_lock_state_changed = 37 [(module) = "wifi"];
         WifiSignalStrengthChanged wifi_signal_strength_changed = 38 [(module) = "wifi"];
         WifiScanStateChanged wifi_scan_state_changed = 39 [(module) = "wifi"];
-        PhoneSignalStrengthChanged phone_signal_strength_changed = 40 [(module) = "framework"];
+        PhoneSignalStrengthChanged phone_signal_strength_changed =
+                40 [(module) = "framework", (truncate_timestamp) = true];
         SettingChanged setting_changed = 41 [(module) = "framework"];
         ActivityForegroundStateChanged activity_foreground_state_changed =
                 42 [(module) = "framework", (module) = "statsdtest"];
@@ -153,7 +157,8 @@
                 59 [(module) = "framework", (module) = "statsdtest"];
         ForegroundServiceStateChanged foreground_service_state_changed
                 = 60 [(module) = "framework"];
-        CallStateChanged call_state_changed = 61 [(module) = "telecom"];
+        CallStateChanged call_state_changed =
+                61 [(module) = "telecom", (truncate_timestamp) = true];
         KeyguardStateChanged keyguard_state_changed = 62 [(module) = "sysui"];
         KeyguardBouncerStateChanged keyguard_bouncer_state_changed = 63 [(module) = "sysui"];
         KeyguardBouncerPasswordEntered keyguard_bouncer_password_entered = 64 [(module) = "sysui"];
@@ -419,8 +424,10 @@
     oneof pulled {
         WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"];
         WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"];
-        MobileBytesTransfer mobile_bytes_transfer = 10002 [(module) = "framework"];
-        MobileBytesTransferByFgBg mobile_bytes_transfer_by_fg_bg = 10003 [(module) = "framework"];
+        MobileBytesTransfer mobile_bytes_transfer =
+                10002 [(module) = "framework", (truncate_timestamp) = true];
+        MobileBytesTransferByFgBg mobile_bytes_transfer_by_fg_bg =
+                10003 [(module) = "framework", (truncate_timestamp) = true];
         BluetoothBytesTransfer bluetooth_bytes_transfer = 10006 [(module) = "framework"];
         KernelWakelock kernel_wakelock = 10004 [(module) = "framework"];
         SubsystemSleepState subsystem_sleep_state = 10005 [(module) = "statsdtest"];
@@ -434,7 +441,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"];
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/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index ae6769e..c1d4693 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -1582,9 +1582,9 @@
             ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
 
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    auto it = valueProducer->mCurrentSlicedBucket.begin();
-    auto& interval1 = it->second[0];
-    auto& baseInfo1 =
+    const auto& it = valueProducer->mCurrentSlicedBucket.begin();
+    ValueMetricProducer::Interval& interval1 = it->second[0];
+    ValueMetricProducer::BaseInfo& baseInfo1 =
             valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())->second[0];
     EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo1.hasBase);
@@ -1611,16 +1611,9 @@
             break;
         }
     }
-    // auto itBase = valueProducer->mCurrentBaseInfo.begin();
-    // for (; itBase != valueProducer->mCurrentBaseInfo.end(); it++) {
-    //     if (itBase != iterBase) {
-    //         break;
-    //     }
-    // }
     EXPECT_TRUE(it2 != it);
-    // EXPECT_TRUE(itBase != iterBase);
-    auto& interval2 = it2->second[0];
-    auto& baseInfo2 =
+    ValueMetricProducer::Interval& interval2 = it2->second[0];
+    ValueMetricProducer::BaseInfo& baseInfo2 =
             valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())->second[0];
     EXPECT_EQ(2, it2->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo2.hasBase);
@@ -1647,23 +1640,28 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs);
 
     EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
-    it = valueProducer->mCurrentSlicedBucket.begin();
-    it2 = std::next(valueProducer->mCurrentSlicedBucket.begin());
-    interval1 = it->second[0];
-    interval2 = it2->second[0];
-    baseInfo1 = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())->second[0];
-    baseInfo2 = valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())->second[0];
+    // Get new references now that entries have been deleted from the map
+    const auto& it3 = valueProducer->mCurrentSlicedBucket.begin();
+    const auto& it4 = std::next(valueProducer->mCurrentSlicedBucket.begin());
+    EXPECT_EQ(it3->second.size(), 1);
+    EXPECT_EQ(it4->second.size(), 1);
+    ValueMetricProducer::Interval& interval3 = it3->second[0];
+    ValueMetricProducer::Interval& interval4 = it4->second[0];
+    ValueMetricProducer::BaseInfo& baseInfo3 =
+            valueProducer->mCurrentBaseInfo.find(it3->first.getDimensionKeyInWhat())->second[0];
+    ValueMetricProducer::BaseInfo& baseInfo4 =
+            valueProducer->mCurrentBaseInfo.find(it4->first.getDimensionKeyInWhat())->second[0];
 
-    EXPECT_EQ(true, baseInfo1.hasBase);
-    EXPECT_EQ(5, baseInfo1.base.long_value);
-    EXPECT_EQ(false, interval1.hasValue);
-    EXPECT_EQ(5, interval1.value.long_value);
+    EXPECT_EQ(true, baseInfo3.hasBase);
+    EXPECT_EQ(5, baseInfo3.base.long_value);
+    EXPECT_EQ(false, interval3.hasValue);
+    EXPECT_EQ(5, interval3.value.long_value);
     EXPECT_EQ(true, valueProducer->mHasGlobalBase);
 
-    EXPECT_EQ(true, baseInfo2.hasBase);
-    EXPECT_EQ(13, baseInfo2.base.long_value);
-    EXPECT_EQ(false, interval2.hasValue);
-    EXPECT_EQ(8, interval2.value.long_value);
+    EXPECT_EQ(true, baseInfo4.hasBase);
+    EXPECT_EQ(13, baseInfo4.base.long_value);
+    EXPECT_EQ(false, interval4.hasValue);
+    EXPECT_EQ(8, interval4.value.long_value);
 
     EXPECT_EQ(2UL, valueProducer->mPastBuckets.size());
 }
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..21b56d3 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;
@@ -4973,10 +4974,8 @@
     ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
             int configChanges, boolean getNonConfigInstance, String reason) {
         ActivityClientRecord r = mActivities.get(token);
-        Class<? extends Activity> activityClass = null;
         if (localLOGV) Slog.v(TAG, "Performing finish of " + r);
         if (r != null) {
-            activityClass = r.activity.getClass();
             r.activity.mConfigChangeFlags |= configChanges;
             if (finishing) {
                 r.activity.mFinished = true;
@@ -5029,7 +5028,6 @@
         synchronized (mResourcesManager) {
             mActivities.remove(token);
         }
-        StrictMode.decrementExpectedActivityCount(activityClass);
         return r;
     }
 
@@ -5049,6 +5047,7 @@
         ActivityClientRecord r = performDestroyActivity(token, finishing,
                 configChanges, getNonConfigInstance, reason);
         if (r != null) {
+            Class<? extends Activity> activityClass = r.activity.getClass();
             cleanUpPendingRemoveWindows(r, finishing);
             WindowManager wm = r.activity.getWindowManager();
             View v = r.activity.mDecor;
@@ -5073,14 +5072,14 @@
                 }
                 if (wtoken != null && r.mPendingRemoveWindow == null) {
                     WindowManagerGlobal.getInstance().closeAll(wtoken,
-                            r.activity.getClass().getName(), "Activity");
+                            activityClass.getName(), "Activity");
                 } else if (r.mPendingRemoveWindow != null) {
                     // We're preserving only one window, others should be closed so app views
                     // will be detached before the final tear down. It should be done now because
                     // some components (e.g. WebView) rely on detach callbacks to perform receiver
                     // unregister and other cleanup.
                     WindowManagerGlobal.getInstance().closeAllExceptView(token, v,
-                            r.activity.getClass().getName(), "Activity");
+                            activityClass.getName(), "Activity");
                 }
                 r.activity.mDecor = null;
             }
@@ -5092,18 +5091,23 @@
                 // about leaking windows, because that is a bug, so if they are
                 // using this recreate facility then they get to live with leaks.
                 WindowManagerGlobal.getInstance().closeAll(token,
-                        r.activity.getClass().getName(), "Activity");
+                        activityClass.getName(), "Activity");
             }
 
             // Mocked out contexts won't be participating in the normal
             // process lifecycle, but if we're running with a proper
             // ApplicationContext we need to have it tear down things
             // cleanly.
-            Context c = r.activity.getBaseContext();
-            if (c instanceof ContextImpl) {
-                ((ContextImpl) c).scheduleFinalCleanup(
-                        r.activity.getClass().getName(), "Activity");
+            final ContextImpl impl = ContextImpl.getImpl(r.activity);
+            if (impl != null) {
+                impl.scheduleFinalCleanup(activityClass.getName(), "Activity");
             }
+
+            r.activity = null;
+            r.window = null;
+            r.hideForNow = false;
+            r.nextIdle = null;
+            StrictMode.decrementExpectedActivityCount(activityClass);
         }
         if (finishing) {
             try {
@@ -5333,10 +5337,6 @@
 
         handleDestroyActivity(r.token, false, configChanges, true, reason);
 
-        r.activity = null;
-        r.window = null;
-        r.hideForNow = false;
-        r.nextIdle = null;
         // Merge any pending results and pending intents; don't just replace them
         if (pendingResults != null) {
             if (r.pendingResults == null) {
@@ -7413,8 +7413,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/LoadedApk.java b/core/java/android/app/LoadedApk.java
index d650801..10f7835 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -801,6 +801,11 @@
         makePaths(mActivityThread, isBundledApp, mApplicationInfo, zipPaths, libPaths);
 
         String libraryPermittedPath = mDataDir;
+        if (mActivityThread == null) {
+            // In a zygote context where mActivityThread is null we can't access the app data dir
+            // and including this in libraryPermittedPath would cause SELinux denials.
+            libraryPermittedPath = "";
+        }
 
         if (isBundledApp) {
             // For bundled apps, add the base directory of the app (e.g.,
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/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 6b40890..37e07de 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -141,6 +141,8 @@
     public static final int ACTIVITY_TYPE_RECENTS = 3;
     /** Assistant activity type. */
     public static final int ACTIVITY_TYPE_ASSISTANT = 4;
+    /** Dream activity type. */
+    public static final int ACTIVITY_TYPE_DREAM = 5;
 
     /** @hide */
     @IntDef(prefix = { "ACTIVITY_TYPE_" }, value = {
@@ -149,6 +151,7 @@
             ACTIVITY_TYPE_HOME,
             ACTIVITY_TYPE_RECENTS,
             ACTIVITY_TYPE_ASSISTANT,
+            ACTIVITY_TYPE_DREAM,
     })
     public @interface ActivityType {}
 
@@ -746,9 +749,11 @@
      * @hide
      */
     public boolean isAlwaysOnTop() {
-        return mWindowingMode == WINDOWING_MODE_PINNED || (mAlwaysOnTop == ALWAYS_ON_TOP_ON
-                && (mWindowingMode == WINDOWING_MODE_FREEFORM
-                    || mWindowingMode == WINDOWING_MODE_MULTI_WINDOW));
+        if (mWindowingMode == WINDOWING_MODE_PINNED) return true;
+        if (mActivityType == ACTIVITY_TYPE_DREAM) return true;
+        if (mAlwaysOnTop != ALWAYS_ON_TOP_ON) return false;
+        return mWindowingMode == WINDOWING_MODE_FREEFORM
+                    || mWindowingMode == WINDOWING_MODE_MULTI_WINDOW;
     }
 
     /**
@@ -798,7 +803,7 @@
 
     /** @hide */
     public static boolean supportSplitScreenWindowingMode(int activityType) {
-        return activityType != ACTIVITY_TYPE_ASSISTANT;
+        return activityType != ACTIVITY_TYPE_ASSISTANT && activityType != ACTIVITY_TYPE_DREAM;
     }
 
     /** @hide */
@@ -823,6 +828,7 @@
             case ACTIVITY_TYPE_HOME: return "home";
             case ACTIVITY_TYPE_RECENTS: return "recents";
             case ACTIVITY_TYPE_ASSISTANT: return "assistant";
+            case ACTIVITY_TYPE_DREAM: return "dream";
         }
         return String.valueOf(applicationType);
     }
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/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index e0674d7..fa62a02 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -379,6 +379,7 @@
     public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
             @ConnectionPolicy int connectionPolicy) {
         if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
+        verifyDeviceNotNull(device, "setConnectionPolicy");
         final IBluetoothHearingAid service = getService();
         try {
             if (service != null && isEnabled()
@@ -428,6 +429,7 @@
     @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
         if (VDBG) log("getConnectionPolicy(" + device + ")");
+        verifyDeviceNotNull(device, "getConnectionPolicy");
         final IBluetoothHearingAid service = getService();
         try {
             if (service != null && isEnabled()
@@ -504,6 +506,7 @@
         if (VDBG) {
             log("getHiSyncId(" + device + ")");
         }
+        verifyDeviceNotNull(device, "getConnectionPolicy");
         final IBluetoothHearingAid service = getService();
         try {
             if (service == null) {
@@ -577,6 +580,13 @@
         return false;
     }
 
+    private void verifyDeviceNotNull(BluetoothDevice device, String methodName) {
+        if (device == null) {
+            Log.e(TAG, methodName + ": device param is null");
+            throw new IllegalArgumentException("Device cannot be null");
+        }
+    }
+
     private boolean isValidDevice(BluetoothDevice device) {
         if (device == null) return false;
 
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 65eb642..31c3232 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -47,6 +47,7 @@
 import android.os.CancellationSignal;
 import android.os.IBinder;
 import android.os.ICancellationSignal;
+import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteCallback;
@@ -303,7 +304,23 @@
         @Override
         public void getTypeAsync(Uri uri, RemoteCallback callback) {
             final Bundle result = new Bundle();
-            result.putString(ContentResolver.REMOTE_CALLBACK_RESULT, getType(uri));
+            try {
+                result.putString(ContentResolver.REMOTE_CALLBACK_RESULT, getType(uri));
+            } catch (Exception e) {
+                Parcel parcel = Parcel.obtain();
+                try {
+                    try {
+                        parcel.writeException(e);
+                    } catch (Exception ex) {
+                        // getType threw an unparcelable exception. Wrap the message into
+                        // a parcelable exception type
+                        parcel.writeException(new IllegalStateException(e.getMessage()));
+                    }
+                    result.putByteArray(ContentResolver.REMOTE_CALLBACK_ERROR, parcel.marshall());
+                } finally {
+                    parcel.recycle();
+                }
+            }
             callback.sendResult(result);
         }
 
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 7510ce7..59862ae 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -55,6 +55,7 @@
 import android.os.IBinder;
 import android.os.ICancellationSignal;
 import android.os.OperationCanceledException;
+import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
@@ -735,6 +736,9 @@
     /** @hide */
     public static final String REMOTE_CALLBACK_RESULT = "result";
 
+    /** @hide */
+    public static final String REMOTE_CALLBACK_ERROR = "error";
+
     /**
      * How long we wait for an attached process to publish its content providers
      * before we decide it must be hung.
@@ -874,6 +878,9 @@
                 final StringResultListener resultListener = new StringResultListener();
                 provider.getTypeAsync(url, new RemoteCallback(resultListener));
                 resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS);
+                if (resultListener.exception != null) {
+                    throw resultListener.exception;
+                }
                 return resultListener.result;
             } catch (RemoteException e) {
                 // Arbitrary and not worth documenting, as Activity
@@ -898,6 +905,9 @@
                     resolveUserId(url),
                     new RemoteCallback(resultListener));
             resultListener.waitForResult(REMOTE_CONTENT_PROVIDER_TIMEOUT_MILLIS);
+            if (resultListener.exception != null) {
+                throw resultListener.exception;
+            }
             return resultListener.result;
         } catch (RemoteException e) {
             // We just failed to send a oneway request to the System Server. Nothing to do.
@@ -915,15 +925,41 @@
         @GuardedBy("this")
         public T result;
 
+        @GuardedBy("this")
+        public RuntimeException exception;
+
         @Override
         public void onResult(Bundle result) {
             synchronized (this) {
-                this.result = getResultFromBundle(result);
+                this.exception = getExceptionFromBundle(result);
+                if (this.exception == null) {
+                    this.result = getResultFromBundle(result);
+                }
                 done = true;
                 notifyAll();
             }
         }
 
+        private RuntimeException getExceptionFromBundle(Bundle result) {
+            byte[] bytes = result.getByteArray(REMOTE_CALLBACK_ERROR);
+            if (bytes == null) {
+                return null;
+            }
+
+            Parcel parcel = Parcel.obtain();
+            try {
+                parcel.unmarshall(bytes, 0, bytes.length);
+                parcel.setDataPosition(0);
+                parcel.readException();
+            } catch (RuntimeException ex) {
+                return ex;
+            } finally {
+                parcel.recycle();
+            }
+
+            return null;
+        }
+
         protected abstract T getResultFromBundle(Bundle result);
 
         public void waitForResult(long timeout) {
@@ -1250,6 +1286,9 @@
             provider.canonicalizeAsync(mPackageName, mAttributionTag, url,
                     new RemoteCallback(resultListener));
             resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS);
+            if (resultListener.exception != null) {
+                throw resultListener.exception;
+            }
             return resultListener.result;
         } catch (RemoteException e) {
             // Arbitrary and not worth documenting, as Activity
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/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 7d5fca4..b6706011 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -752,6 +752,30 @@
     public @interface ApplicationInfoPrivateFlags {}
 
     /**
+     * Constant corresponding to <code>allowed</code> in the
+     * {@link android.R.attr#autoRevokePermissions} attribute.
+     *
+     * @hide
+     */
+    public static final int AUTO_REVOKE_ALLOWED = 0;
+
+    /**
+     * Constant corresponding to <code>discouraged</code> in the
+     * {@link android.R.attr#autoRevokePermissions} attribute.
+     *
+     * @hide
+     */
+    public static final int AUTO_REVOKE_DISCOURAGED = 1;
+
+    /**
+     * Constant corresponding to <code>disallowed</code> in the
+     * {@link android.R.attr#autoRevokePermissions} attribute.
+     *
+     * @hide
+     */
+    public static final int AUTO_REVOKE_DISALLOWED = 2;
+
+    /**
      * Private/hidden flags. See {@code PRIVATE_FLAG_...} constants.
      * @hide
      */
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/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 39f2858..4c95532 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -192,9 +192,7 @@
 
     ParsingPackage setAllowNativeHeapPointerTagging(boolean allowNativeHeapPointerTagging);
 
-    ParsingPackage setDontAutoRevokePermissions(boolean dontAutoRevokePermissions);
-
-    ParsingPackage setAllowDontAutoRevokePermissions(boolean allowDontAutoRevokePermissions);
+    ParsingPackage setAutoRevokePermissions(int autoRevokePermissions);
 
     ParsingPackage setPreserveLegacyExternalStorage(boolean preserveLegacyExternalStorage);
 
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index f2ab60a..079a470 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -30,7 +30,6 @@
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageParser;
-import android.content.pm.ProcessInfo;
 import android.content.pm.parsing.component.ParsedActivity;
 import android.content.pm.parsing.component.ParsedAttribution;
 import android.content.pm.parsing.component.ParsedComponent;
@@ -405,8 +404,7 @@
     private boolean hasFragileUserData;
     private boolean cantSaveState;
     private boolean allowNativeHeapPointerTagging;
-    private boolean dontAutoRevokePermissions;
-    private boolean allowDontAutoRevokePermissions;
+    private int autoRevokePermissions;
     private boolean preserveLegacyExternalStorage;
 
     protected int gwpAsanMode;
@@ -1089,8 +1087,7 @@
         dest.writeBoolean(this.hasFragileUserData);
         dest.writeBoolean(this.cantSaveState);
         dest.writeBoolean(this.allowNativeHeapPointerTagging);
-        dest.writeBoolean(this.dontAutoRevokePermissions);
-        dest.writeBoolean(this.allowDontAutoRevokePermissions);
+        dest.writeInt(this.autoRevokePermissions);
         dest.writeBoolean(this.preserveLegacyExternalStorage);
         dest.writeArraySet(this.mimeGroups);
         dest.writeInt(this.gwpAsanMode);
@@ -1249,8 +1246,7 @@
         this.hasFragileUserData = in.readBoolean();
         this.cantSaveState = in.readBoolean();
         this.allowNativeHeapPointerTagging = in.readBoolean();
-        this.dontAutoRevokePermissions = in.readBoolean();
-        this.allowDontAutoRevokePermissions = in.readBoolean();
+        this.autoRevokePermissions = in.readInt();
         this.preserveLegacyExternalStorage = in.readBoolean();
         this.mimeGroups = (ArraySet<String>) in.readArraySet(boot);
         this.gwpAsanMode = in.readInt();
@@ -2026,13 +2022,8 @@
     }
 
     @Override
-    public boolean isDontAutoRevokePermmissions() {
-        return dontAutoRevokePermissions;
-    }
-
-    @Override
-    public boolean isAllowDontAutoRevokePermmissions() {
-        return allowDontAutoRevokePermissions;
+    public int getAutoRevokePermissions() {
+        return autoRevokePermissions;
     }
 
     @Override
@@ -2506,14 +2497,8 @@
     }
 
     @Override
-    public ParsingPackageImpl setDontAutoRevokePermissions(boolean value) {
-        dontAutoRevokePermissions = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setAllowDontAutoRevokePermissions(boolean value) {
-        allowDontAutoRevokePermissions = value;
+    public ParsingPackageImpl setAutoRevokePermissions(int value) {
+        autoRevokePermissions = value;
         return this;
     }
 
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index e700c6a5..687bc23 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -771,11 +771,7 @@
     /** @see ApplicationInfo#PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING */
     boolean isAllowNativeHeapPointerTagging();
 
-    /** @see ApplicationInfo#PRIVATE_FLAG2_DONT_AUTO_REVOKE_PERMISSIONS */
-    boolean isDontAutoRevokePermmissions();
-
-    /** @see ApplicationInfo#PRIVATE_FLAG2_ALLOW_DONT_AUTO_REVOKE_PERMISSIONS */
-    boolean isAllowDontAutoRevokePermmissions();
+    int getAutoRevokePermissions();
 
     boolean hasPreserveLegacyExternalStorage();
 
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 789904a..ec77128 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -1829,8 +1829,7 @@
                 .setUseEmbeddedDex(bool(false, R.styleable.AndroidManifestApplication_useEmbeddedDex, sa))
                 .setUsesNonSdkApi(bool(false, R.styleable.AndroidManifestApplication_usesNonSdkApi, sa))
                 .setVmSafeMode(bool(false, R.styleable.AndroidManifestApplication_vmSafeMode, sa))
-                .setDontAutoRevokePermissions(bool(false, R.styleable.AndroidManifestApplication_requestAutoRevokePermissionsExemption, sa))
-                .setAllowDontAutoRevokePermissions(bool(false, R.styleable.AndroidManifestApplication_allowAutoRevokePermissionsExemption, sa))
+                .setAutoRevokePermissions(anInt(R.styleable.AndroidManifestApplication_autoRevokePermissions, sa))
                 // targetSdkVersion gated
                 .setAllowAudioPlaybackCapture(bool(targetSdk >= Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture, sa))
                 .setBaseHardwareAccelerated(bool(targetSdk >= Build.VERSION_CODES.ICE_CREAM_SANDWICH, R.styleable.AndroidManifestApplication_hardwareAccelerated, sa))
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 34a40ae..eb6901f6 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/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 7c34ddc..6daded4 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1249,6 +1249,7 @@
                 WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
         mWindow.getWindow().getAttributes().setFitInsetsTypes(statusBars() | navigationBars());
         mWindow.getWindow().getAttributes().setFitInsetsSides(Side.all() & ~Side.BOTTOM);
+        mWindow.getWindow().getAttributes().setFitInsetsIgnoringVisibility(true);
 
         // IME layout should always be inset by navigation bar, no matter its current visibility,
         // unless automotive requests it, since automotive may hide the navigation bar.
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/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index fcfcebd..af9414c 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -415,6 +415,20 @@
             | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY);
 
     /**
+     * Capabilities that are allowed for test networks. This list must be set so that it is safe
+     * for an unprivileged user to create a network with these capabilities via shell. As such,
+     * it must never contain capabilities that are generally useful to the system, such as
+     * INTERNET, IMS, SUPL, etc.
+     */
+    private static final long TEST_NETWORKS_ALLOWED_CAPABILITIES =
+            (1 << NET_CAPABILITY_NOT_METERED)
+            | (1 << NET_CAPABILITY_NOT_RESTRICTED)
+            | (1 << NET_CAPABILITY_NOT_VPN)
+            | (1 << NET_CAPABILITY_NOT_ROAMING)
+            | (1 << NET_CAPABILITY_NOT_CONGESTED)
+            | (1 << NET_CAPABILITY_NOT_SUSPENDED);
+
+    /**
      * Adds the given capability to this {@code NetworkCapability} instance.
      * Note that when searching for a network to satisfy a request, all capabilities
      * requested must be satisfied.
@@ -646,6 +660,21 @@
     }
 
     /**
+     * Test networks have strong restrictions on what capabilities they can have. Enforce these
+     * restrictions.
+     * @hide
+     */
+    public void restrictCapabilitesForTestNetwork() {
+        final long originalCapabilities = mNetworkCapabilities;
+        final NetworkSpecifier originalSpecifier = mNetworkSpecifier;
+        clearAll();
+        // Reset the transports to only contain TRANSPORT_TEST.
+        mTransportTypes = (1 << TRANSPORT_TEST);
+        mNetworkCapabilities = originalCapabilities & TEST_NETWORKS_ALLOWED_CAPABILITIES;
+        mNetworkSpecifier = originalSpecifier;
+    }
+
+    /**
      * Representing the transport type.  Apps should generally not care about transport.  A
      * request for a fast internet connection could be satisfied by a number of different
      * transports.  If any are specified here it will be satisfied a Network that matches
@@ -2000,7 +2029,7 @@
      */
     @SystemApi
     @TestApi
-    public static class Builder {
+    public static final class Builder {
         private final NetworkCapabilities mCaps;
 
         /**
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/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index bbc936d..99bdfd1 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -194,4 +194,5 @@
     boolean needsCheckpoint() = 86;
     void abortChanges(in String message, boolean retry) = 87;
     void clearUserKeyAuth(int userId, int serialNumber, in byte[] token, in byte[] secret) = 88;
+    void fixupAppDir(in String path) = 89;
 }
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 1454aac..a68cc3d 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -2470,6 +2470,36 @@
         }
     }
 
+    /**
+     * Asks StorageManager to fixup the permissions of an application-private directory.
+     *
+     * On devices without sdcardfs, filesystem permissions aren't magically fixed up. This
+     * is problematic mostly in application-private directories, which are owned by the
+     * application itself; if another process with elevated permissions creates a file
+     * in these directories, the UID will be wrong, and the owning package won't be able
+     * to access the files.
+     *
+     * This API can be used to recursively fix up the permissions on the passed in path.
+     * The default platform user of this API is the DownloadProvider, which can download
+     * things in application-private directories on their behalf.
+     *
+     * This API doesn't require any special permissions, because it merely changes the
+     * permissions of a directory to what they should anyway be.
+     *
+     * @param path the path for which we should fix up the permissions
+     *
+     * @hide
+     */
+    public void fixupAppDir(@NonNull File path) {
+        try {
+            mStorageManager.fixupAppDir(path.getCanonicalPath());
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to get canonical path for " + path.getPath(), e);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /** {@hide} */
     private static void setCacheBehavior(File path, String name, boolean enabled)
             throws IOException {
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/InsetsController.java b/core/java/android/view/InsetsController.java
index 43f80f1..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;
@@ -467,7 +464,7 @@
         if (!localStateChanged && mLastDispachedState.equals(state)) {
             return false;
         }
-        mState.set(state);
+        updateState(state);
         mLastDispachedState.set(state, true /* copySources */);
         applyLocalVisibilityOverride();
         if (localStateChanged) {
@@ -479,6 +476,20 @@
         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
      */
@@ -858,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;
             }
         }
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/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index cd22ad6..fe70ff7 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -192,6 +192,7 @@
         final WindowManager.LayoutParams lp =
                 new WindowManager.LayoutParams(width, height,
                         WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT);
+        lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
         setView(view, lp);
     }
 
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 59fc6e9..c89e0c9 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -176,6 +176,7 @@
     boolean mUseAlpha = false;
     float mSurfaceAlpha = 1f;
     boolean mClipSurfaceToBounds;
+    int mBackgroundColor = Color.BLACK;
 
     @UnsupportedAppUsage
     boolean mHaveFrame = false;
@@ -828,6 +829,12 @@
         }
     }
 
+    private Transaction updateBackgroundColor(Transaction t) {
+        final float[] colorComponents = new float[] { Color.red(mBackgroundColor) / 255.f,
+                Color.green(mBackgroundColor) / 255.f, Color.blue(mBackgroundColor) / 255.f };
+        t.setColor(mBackgroundControl, colorComponents);
+        return t;
+    }
 
     private void releaseSurfaces() {
         mSurfaceAlpha = 1f;
@@ -1000,6 +1007,7 @@
                     }
 
                     updateBackgroundVisibility(mTmpTransaction);
+                    updateBackgroundColor(mTmpTransaction);
                     if (mUseAlpha) {
                         mTmpTransaction.setAlpha(mSurfaceControl, alpha);
                         mSurfaceAlpha = alpha;
@@ -1399,10 +1407,8 @@
             return;
         }
 
-        final float[] colorComponents = new float[] { Color.red(bgColor) / 255.f,
-                Color.green(bgColor) / 255.f, Color.blue(bgColor) / 255.f };
-
-        mTmpTransaction.setColor(mBackgroundControl, colorComponents).apply();
+        mBackgroundColor = bgColor;
+        updateBackgroundColor(mTmpTransaction).apply();
     }
 
     @UnsupportedAppUsage
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/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index c83cab7..31527e8 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -227,7 +227,7 @@
         }
 
         final int oldCollapsibleHeight = mCollapsibleHeight;
-        mCollapsibleHeight = Math.max(mCollapsibleHeight, getMaxCollapsedHeight());
+        mCollapsibleHeight = Math.min(mCollapsibleHeight, getMaxCollapsedHeight());
 
         if (updateCollapseOffset(oldCollapsibleHeight, !isDragging())) {
             return;
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/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index d6e200a..28eb98b 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1827,19 +1827,11 @@
 
         <attr name="gwpAsanMode" />
 
-        <!-- If {@code true} allow requesting that its permissions don't get automatically
-             revoked when the app is unused for an extended amount of time.
-
-             The default value is {@code false}. -->
-        <attr name="requestAutoRevokePermissionsExemption" format="boolean" />
-
-        <!-- If {@code true} its permissions shouldn't get automatically
-             revoked when the app is unused for an extended amount of time.
-
-             This implies {@code requestDontAutoRevokePermissions=true}
-
-             The default value is {@code false}. -->
-        <attr name="allowAutoRevokePermissionsExemption" format="boolean" />
+        <attr name="autoRevokePermissions">
+            <enum name="allowed" value="0" />
+            <enum name="discouraged" value="1" />
+            <enum name="disallowed" value="2" />
+        </attr>
     </declare-styleable>
 
     <!-- An attribution is a logical part of an app and is identified by a tag.
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index f02d54f..e694e16 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3014,8 +3014,7 @@
       <!-- @hide @SystemApi -->
       <public name="minExtensionVersion" />
       <public name="allowNativeHeapPointerTagging" />
-      <public name="requestAutoRevokePermissionsExemption" />
-      <public name="allowAutoRevokePermissionsExemption" />
+      <public name="autoRevokePermissions" />
       <public name="preserveLegacyExternalStorage" />
       <public name="mimeGroup" />
       <public name="gwpAsanMode" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 2ef0c92..88f9fc2 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -702,7 +702,7 @@
     </style>
 
     <style name="Theme.Dream">
-        <item name="windowBackground">@null</item>
+        <item name="windowBackground">@color/black</item>
         <item name="windowDisablePreview">true</item>
     </style>
 
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index 1737bd0..57e5dd8 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -20,6 +20,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -236,6 +237,16 @@
     }
 
     @Test
+    public void testGetType_providerException() {
+        try {
+            mResolver.getType(Uri.parse("content://android.content.FakeProviderRemote/error"));
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // Expected
+        }
+    }
+
+    @Test
     public void testCanonicalize() {
         Uri canonical = mResolver.canonicalize(
                 Uri.parse("content://android.content.FakeProviderRemote/something"));
diff --git a/core/tests/coretests/src/android/content/FakeProviderRemote.java b/core/tests/coretests/src/android/content/FakeProviderRemote.java
index 1d7ba5d..a320094 100644
--- a/core/tests/coretests/src/android/content/FakeProviderRemote.java
+++ b/core/tests/coretests/src/android/content/FakeProviderRemote.java
@@ -37,6 +37,9 @@
 
     @Override
     public String getType(Uri uri) {
+        if (uri.getPath() != null && uri.getPath().contains("error")) {
+            throw new IllegalArgumentException("Expected exception");
+        }
         return "fake/remote";
     }
 
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 34a1016..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;
@@ -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/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/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml
index 5c89da0..98f5824 100644
--- a/data/etc/hiddenapi-package-whitelist.xml
+++ b/data/etc/hiddenapi-package-whitelist.xml
@@ -61,7 +61,6 @@
   <hidden-api-whitelisted-app package="com.android.terminal" />
   <hidden-api-whitelisted-app package="com.android.wallpaper" />
   <hidden-api-whitelisted-app package="jp.co.omronsoft.openwnn" />
-  <!-- STOPSHIP: Remove this when fixing all @hide usage for tethering.-->
-  <hidden-api-whitelisted-app package="com.android.networkstack.tethering" />
+  <!-- TODO: Remove NetworkStack whitelisting -->
   <hidden-api-whitelisted-app package="com.android.networkstack" />
 </config>
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/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index d17f242..a1fba4a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -174,7 +174,7 @@
 
     @Override
     public boolean isEnabled(BluetoothDevice device) {
-        if (mService == null) {
+        if (mService == null || device == null) {
             return false;
         }
         return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
@@ -182,7 +182,7 @@
 
     @Override
     public int getConnectionPolicy(BluetoothDevice device) {
-        if (mService == null) {
+        if (mService == null || device == null) {
             return CONNECTION_POLICY_FORBIDDEN;
         }
         return mService.getConnectionPolicy(device);
@@ -191,7 +191,7 @@
     @Override
     public boolean setEnabled(BluetoothDevice device, boolean enabled) {
         boolean isEnabled = false;
-        if (mService == null) {
+        if (mService == null || device == null) {
             return false;
         }
         if (enabled) {
@@ -213,7 +213,7 @@
     }
 
     public long getHiSyncId(BluetoothDevice device) {
-        if (mService == null) {
+        if (mService == null || device == null) {
             return BluetoothHearingAid.HI_SYNC_ID_INVALID;
         }
         return mService.getHiSyncId(device);
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
index 9c2d244..de9ef21 100644
--- a/packages/SystemUI/res/layout/keyguard_media_header.xml
+++ b/packages/SystemUI/res/layout/keyguard_media_header.xml
@@ -52,7 +52,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="horizontal"
-        android:gravity="center_horizontal|fill_vertical"
+        android:gravity="center"
         android:padding="16dp"
     >
         <ImageView
@@ -67,7 +67,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/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 f7ad134..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] -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 07bd3a0..1369350 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -73,14 +73,19 @@
     public static final int SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED = 1 << 9;
     // The search feature is disabled (either by SUW/SysUI/device policy)
     public static final int SYSUI_STATE_SEARCH_DISABLED = 1 << 10;
-    // The notification panel is expanded and interactive (either locked or unlocked), and the
-    // quick settings is not expanded
+    // The notification panel is expanded and interactive (either locked or unlocked), and quick
+    // settings is expanded.
     public static final int SYSUI_STATE_QUICK_SETTINGS_EXPANDED = 1 << 11;
     // Winscope tracing is enabled
     public static final int SYSUI_STATE_TRACING_ENABLED = 1 << 12;
     // The Assistant gesture should be constrained. It is up to the launcher implementation to
     // decide how to constrain it
     public static final int SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED = 1 << 13;
+    // The bubble stack is expanded. This means that the home gesture should be ignored, since a
+    // swipe up is an attempt to close the bubble stack, but that the back gesture should remain
+    // enabled (since it's used to navigate back within the bubbled app, or to collapse the bubble
+    // stack.
+    public static final int SYSUI_STATE_BUBBLES_EXPANDED = 1 << 14;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({SYSUI_STATE_SCREEN_PINNING,
@@ -96,7 +101,8 @@
             SYSUI_STATE_HOME_DISABLED,
             SYSUI_STATE_SEARCH_DISABLED,
             SYSUI_STATE_TRACING_ENABLED,
-            SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED
+            SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED,
+            SYSUI_STATE_BUBBLES_EXPANDED
     })
     public @interface SystemUiStateFlags {}
 
@@ -118,6 +124,7 @@
         str.add((flags & SYSUI_STATE_TRACING_ENABLED) != 0 ? "tracing" : "");
         str.add((flags & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0
                 ? "asst_gesture_constrain" : "");
+        str.add((flags & SYSUI_STATE_BUBBLES_EXPANDED) != 0 ? "bubbles_expanded" : "");
         return str.toString();
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMedia.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardMedia.kt
new file mode 100644
index 0000000..487c295
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMedia.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.Drawable
+
+import java.util.List
+
+/** State for lock screen media controls. */
+data class KeyguardMedia(
+    val foregroundColor: Int,
+    val backgroundColor: Int,
+    val app: String?,
+    val appIcon: Drawable?,
+    val artist: String?,
+    val song: String?,
+    val artwork: Drawable?,
+    val actionIcons: List<Drawable>
+)
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java
index b001726..d154434 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java
@@ -32,6 +32,9 @@
 
 import androidx.core.graphics.drawable.RoundedBitmapDrawable;
 import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.Observer;
 import androidx.palette.graphics.Palette;
 
 import com.android.internal.util.ContrastColorUtil;
@@ -64,39 +67,47 @@
 
     private final Context mContext;
     private final Executor mBackgroundExecutor;
-    private float mAlbumArtRadius;
-    private int mAlbumArtSize;
-    private View mMediaNotifView;
+    private final KeyguardMediaViewModel mViewModel;
+    private KeyguardMediaObserver mObserver;
 
     @Inject
     public KeyguardMediaPlayer(Context context, @Background Executor backgroundExecutor) {
         mContext = context;
         mBackgroundExecutor = backgroundExecutor;
-        loadDimens();
+        mViewModel = new KeyguardMediaViewModel(context);
     }
 
     /** Binds media controls to a view hierarchy. */
     public void bindView(View v) {
-        if (mMediaNotifView != null) {
+        if (mObserver != null) {
             throw new IllegalStateException("cannot bind views, already bound");
         }
-        mMediaNotifView = v;
-        loadDimens();
+        mViewModel.loadDimens();
+        mObserver = new KeyguardMediaObserver(v);
+        // Control buttons
+        for (int i = 0; i < ACTION_IDS.length; i++) {
+            ImageButton button = v.findViewById(ACTION_IDS[i]);
+            if (button == null) {
+                continue;
+            }
+            final int index = i;
+            button.setOnClickListener(unused -> mViewModel.onActionClick(index));
+        }
+        mViewModel.getKeyguardMedia().observeForever(mObserver);
     }
 
     /** Unbinds media controls. */
     public void unbindView() {
-        if (mMediaNotifView == null) {
+        if (mObserver == null) {
             throw new IllegalStateException("cannot unbind views, nothing bound");
         }
-        mMediaNotifView = null;
+        mViewModel.getKeyguardMedia().removeObserver(mObserver);
+        mObserver = null;
     }
 
     /** Clear the media controls because there isn't an active session. */
     public void clearControls() {
-        if (mMediaNotifView != null) {
-            mMediaNotifView.setVisibility(View.GONE);
-        }
+        mBackgroundExecutor.execute(mViewModel::clearControls);
     }
 
     /**
@@ -110,157 +121,244 @@
      */
     public void updateControls(NotificationEntry entry, Icon appIcon,
             MediaMetadata mediaMetadata) {
-        if (mMediaNotifView == null) {
+        if (mObserver == null) {
             throw new IllegalStateException("cannot update controls, views not bound");
         }
         if (mediaMetadata == null) {
-            throw new IllegalArgumentException("media metadata was null");
+            Log.d(TAG, "media metadata was null, closing media controls");
+            // Note that clearControls() executes on the same background executor, so there
+            // shouldn't be an issue with an outdated update running after clear. However, if stale
+            // controls are observed then consider removing any enqueued updates.
+            clearControls();
+            return;
         }
-        mMediaNotifView.setVisibility(View.VISIBLE);
+        mBackgroundExecutor.execute(() -> mViewModel.updateControls(entry, appIcon, mediaMetadata));
+    }
 
-        Notification notif = entry.getSbn().getNotification();
+    /** ViewModel for KeyguardMediaControls. */
+    private static final class KeyguardMediaViewModel {
 
-        // 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);
+        private final Context mContext;
+        private final MutableLiveData<KeyguardMedia> mMedia = new MutableLiveData<>();
+        private final Object mActionsLock = new Object();
+        private List<PendingIntent> mActions;
+        private float mAlbumArtRadius;
+        private int mAlbumArtSize;
 
-        // 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));
+        KeyguardMediaViewModel(Context context) {
+            mContext = context;
+            loadDimens();
         }
 
-        // App icon
-        ImageView appIconView = mMediaNotifView.findViewById(R.id.icon);
-        if (appIconView != null) {
-            Drawable iconDrawable = appIcon.loadDrawable(mContext);
-            iconDrawable.setTint(fgColor);
-            appIconView.setImageDrawable(iconDrawable);
+        /** Close the media player because there isn't an active session. */
+        public void clearControls() {
+            synchronized (mActionsLock) {
+                mActions = null;
+            }
+            mMedia.postValue(null);
         }
 
-        // App name
-        TextView appName = mMediaNotifView.findViewById(R.id.app_name);
-        if (appName != null) {
+        /** Update the media player with information about the active session. */
+        public void updateControls(NotificationEntry entry, Icon appIcon,
+                MediaMetadata mediaMetadata) {
+
+            // Foreground and Background colors computed from album art
+            Notification notif = entry.getSbn().getNotification();
+            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
+            RoundedBitmapDrawable artwork = null;
+            if (artworkBitmap != null) {
+                Bitmap original = artworkBitmap.copy(Bitmap.Config.ARGB_8888, true);
+                Bitmap scaled = Bitmap.createScaledBitmap(original, mAlbumArtSize, mAlbumArtSize,
+                        false);
+                artwork = RoundedBitmapDrawableFactory.create(mContext.getResources(), scaled);
+                artwork.setCornerRadius(mAlbumArtRadius);
+            }
+
+            // App name
             Notification.Builder builder = Notification.Builder.recoverBuilder(mContext, notif);
-            String appNameString = builder.loadHeaderAppName();
-            appName.setText(appNameString);
-            appName.setTextColor(fgColor);
-        }
+            String app = builder.loadHeaderAppName();
 
-        // 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);
-        }
+            // App Icon
+            Drawable appIconDrawable = appIcon.loadDrawable(mContext);
 
-        // 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);
-        }
+            // Song name
+            String song = mediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE);
 
-        // Background color
-        if (mMediaNotifView instanceof MediaHeaderView) {
-            MediaHeaderView head = (MediaHeaderView) mMediaNotifView;
-            head.setBackgroundColor(bgColor);
-        }
+            // Artist name
+            String artist = mediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
 
-        // 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);
+            // Control buttons
+            List<Drawable> actionIcons = 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++) {
+                if (actionsToShow != null && actions != null && i < actionsToShow.length
+                        && actionsToShow[i] < actions.length) {
+                    final int idx = actionsToShow[i];
+                    actionIcons.add(actions[idx].getIcon().loadDrawable(packageContext));
+                    intents.add(actions[idx].actionIntent);
+                } else {
+                    actionIcons.add(null);
+                    intents.add(null);
+                }
             }
+            synchronized (mActionsLock) {
+                mActions = intents;
+            }
+
+            KeyguardMedia data = new KeyguardMedia(fgColor, bgColor, app, appIconDrawable, artist,
+                    song, artwork, actionIcons);
+            mMedia.postValue(data);
         }
 
-        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;
+        /** Gets state for the lock screen media controls. */
+        public LiveData<KeyguardMedia> getKeyguardMedia() {
+            return mMedia;
+        }
+
+        /**
+         * Handle user clicks on media control buttons (actions).
+         *
+         * @param index position of the button that was clicked.
+         */
+        public void onActionClick(int index) {
+            PendingIntent intent = null;
+            // This might block the ui thread to wait for the lock. Currently, however, the
+            // lock is held by the bg thread to assign a member, which should be fast. An
+            // alternative could be to add the intents to the state and let the observer set
+            // the onClick listeners.
+            synchronized (mActionsLock) {
+                if (mActions != null && index < mActions.size()) {
+                    intent = mActions.get(index);
+                }
             }
-            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);
-                        }
-                    });
+            if (intent != null) {
+                try {
+                    intent.send();
+                } catch (PendingIntent.CanceledException e) {
+                    Log.d(TAG, "failed to send action intent", e);
                 }
             }
         }
+
+        void loadDimens() {
+            mAlbumArtRadius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius);
+            mAlbumArtSize = (int) mContext.getResources().getDimension(
+                    R.dimen.qs_media_album_size);
+        }
     }
 
-    /**
-     * 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");
+    /** Observer for state changes of lock screen media controls. */
+    private static final class KeyguardMediaObserver implements Observer<KeyguardMedia> {
+
+        private final View mRootView;
+        private final MediaHeaderView mMediaHeaderView;
+        private final ImageView mAlbumView;
+        private final ImageView mAppIconView;
+        private final TextView mAppNameView;
+        private final TextView mTitleView;
+        private final TextView mArtistView;
+        private final List<ImageButton> mButtonViews = new ArrayList<>();
+
+        KeyguardMediaObserver(View v) {
+            mRootView = v;
+            mMediaHeaderView = v instanceof MediaHeaderView ? (MediaHeaderView) v : null;
+            mAlbumView = v.findViewById(R.id.album_art);
+            mAppIconView = v.findViewById(R.id.icon);
+            mAppNameView = v.findViewById(R.id.app_name);
+            mTitleView = v.findViewById(R.id.header_title);
+            mArtistView = v.findViewById(R.id.header_artist);
+            for (int i = 0; i < ACTION_IDS.length; i++) {
+                mButtonViews.add(v.findViewById(ACTION_IDS[i]));
+            }
         }
 
-        // 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);
-        });
-    }
+        /** Updates lock screen media player views when state changes. */
+        @Override
+        public void onChanged(KeyguardMedia data) {
+            if (data == null) {
+                mRootView.setVisibility(View.GONE);
+                return;
+            }
+            mRootView.setVisibility(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);
+            // Background color
+            if (mMediaHeaderView != null) {
+                mMediaHeaderView.setBackgroundColor(data.getBackgroundColor());
+            }
+
+            // Album art
+            if (mAlbumView != null) {
+                mAlbumView.setImageDrawable(data.getArtwork());
+                mAlbumView.setVisibility(data.getArtwork() == null ? View.GONE : View.VISIBLE);
+            }
+
+            // App icon
+            if (mAppIconView != null) {
+                Drawable iconDrawable = data.getAppIcon();
+                iconDrawable.setTint(data.getForegroundColor());
+                mAppIconView.setImageDrawable(iconDrawable);
+            }
+
+            // App name
+            if (mAppNameView != null) {
+                String appNameString = data.getApp();
+                mAppNameView.setText(appNameString);
+                mAppNameView.setTextColor(data.getForegroundColor());
+            }
+
+            // Song name
+            if (mTitleView != null) {
+                mTitleView.setText(data.getSong());
+                mTitleView.setTextColor(data.getForegroundColor());
+            }
+
+            // Artist name
+            if (mArtistView != null) {
+                mArtistView.setText(data.getArtist());
+                mArtistView.setTextColor(data.getForegroundColor());
+            }
+
+            // Control buttons
+            for (int i = 0; i < ACTION_IDS.length; i++) {
+                ImageButton button = mButtonViews.get(i);
+                if (button == null) {
+                    continue;
+                }
+                Drawable icon = data.getActionIcons().get(i);
+                if (icon == null) {
+                    button.setVisibility(View.GONE);
+                    button.setImageDrawable(null);
+                } else {
+                    button.setVisibility(View.VISIBLE);
+                    button.setImageDrawable(icon);
+                    button.setImageTintList(ColorStateList.valueOf(data.getForegroundColor()));
+                }
+            }
+        }
     }
 }
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/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/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..01c2faa 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -74,6 +74,7 @@
 import com.android.systemui.R;
 import com.android.systemui.bubbles.dagger.BubbleModule;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.PinnedStackListenerForwarder;
@@ -174,6 +175,7 @@
 
     private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
     private IStatusBarService mBarService;
+    private SysUiState mSysUiState;
 
     // Used for determining view rect for touch interaction
     private Rect mTempRect = new Rect();
@@ -290,11 +292,12 @@
             NotifPipeline notifPipeline,
             FeatureFlags featureFlags,
             DumpManager dumpManager,
-            FloatingContentCoordinator floatingContentCoordinator) {
+            FloatingContentCoordinator floatingContentCoordinator,
+            SysUiState sysUiState) {
         this(context, notificationShadeWindowController, statusBarStateController, shadeController,
                 data, null /* synchronizer */, configurationController, interruptionStateProvider,
                 zenModeController, notifUserManager, groupManager, entryManager,
-                notifPipeline, featureFlags, dumpManager, floatingContentCoordinator);
+                notifPipeline, featureFlags, dumpManager, floatingContentCoordinator, sysUiState);
     }
 
     /**
@@ -315,7 +318,8 @@
             NotifPipeline notifPipeline,
             FeatureFlags featureFlags,
             DumpManager dumpManager,
-            FloatingContentCoordinator floatingContentCoordinator) {
+            FloatingContentCoordinator floatingContentCoordinator,
+            SysUiState sysUiState) {
         dumpManager.registerDumpable(TAG, this);
         mContext = context;
         mShadeController = shadeController;
@@ -340,6 +344,7 @@
         });
 
         configurationController.addCallback(this /* configurationListener */);
+        mSysUiState = sysUiState;
 
         mBubbleData = data;
         mBubbleData.setListener(mBubbleDataListener);
@@ -593,7 +598,8 @@
     private void ensureStackViewCreated() {
         if (mStackView == null) {
             mStackView = new BubbleStackView(
-                    mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator);
+                    mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator,
+                    mSysUiState);
             ViewGroup nsv = mNotificationShadeWindowController.getNotificationShadeView();
             int bubbleScrimIndex = nsv.indexOfChild(nsv.findViewById(R.id.scrim_for_bubble));
             int stackIndex = bubbleScrimIndex + 1;  // Show stack above bubble scrim.
@@ -957,9 +963,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 077ffd3..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;
                 }
             }
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 541c8cf..7191a20 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -80,6 +80,8 @@
 import com.android.systemui.bubbles.animation.ExpandedAnimationController;
 import com.android.systemui.bubbles.animation.PhysicsAnimationLayout;
 import com.android.systemui.bubbles.animation.StackAnimationController;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.util.DismissCircleView;
 import com.android.systemui.util.FloatingContentCoordinator;
@@ -241,6 +243,7 @@
 
     private BubbleTouchHandler mTouchHandler;
     private BubbleController.BubbleExpandListener mExpandListener;
+    private SysUiState mSysUiState;
 
     private boolean mViewUpdatedRequested = false;
     private boolean mIsExpansionAnimating = false;
@@ -437,7 +440,8 @@
 
     public BubbleStackView(Context context, BubbleData data,
             @Nullable SurfaceSynchronizer synchronizer,
-            FloatingContentCoordinator floatingContentCoordinator) {
+            FloatingContentCoordinator floatingContentCoordinator,
+            SysUiState sysUiState) {
         super(context);
 
         mBubbleData = data;
@@ -445,6 +449,8 @@
         mTouchHandler = new BubbleTouchHandler(this, data, context);
         setOnTouchListener(mTouchHandler);
 
+        mSysUiState = sysUiState;
+
         Resources res = getResources();
         mMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered);
         mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
@@ -1055,6 +1061,11 @@
         if (shouldExpand == mIsExpanded) {
             return;
         }
+
+        mSysUiState
+                .setFlag(QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED, shouldExpand)
+                .commitUpdate(mContext.getDisplayId());
+
         if (mIsExpanded) {
             animateCollapse();
             logBubbleEvent(mExpandedBubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
index 27c9e98..e84e932 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
@@ -21,6 +21,7 @@
 import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.bubbles.BubbleData;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -62,7 +63,8 @@
             NotifPipeline notifPipeline,
             FeatureFlags featureFlags,
             DumpManager dumpManager,
-            FloatingContentCoordinator floatingContentCoordinator) {
+            FloatingContentCoordinator floatingContentCoordinator,
+            SysUiState sysUiState) {
         return new BubbleController(
                 context,
                 notificationShadeWindowController,
@@ -79,6 +81,7 @@
                 notifPipeline,
                 featureFlags,
                 dumpManager,
-                floatingContentCoordinator);
+                floatingContentCoordinator,
+                sysUiState);
     }
 }
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 902b578..6ce5e7c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -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 3879c16..1aa7831 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -25,6 +25,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.annotation.Nullable;
+import android.app.ActivityTaskManager;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -129,6 +130,7 @@
 
     private int mDividerInsets;
     private final Display mDefaultDisplay;
+    private boolean mSupportSplitScreenMultiWindow;
 
     private int mDividerSize;
     private int mTouchElevation;
@@ -282,6 +284,8 @@
         final DisplayManager displayManager =
                 (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
         mDefaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
+        mSupportSplitScreenMultiWindow =
+                ActivityTaskManager.supportsSplitScreenMultiWindow(mContext);
     }
 
     @Override
@@ -354,6 +358,11 @@
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        if (!mSupportSplitScreenMultiWindow) {
+            super.onLayout(changed, left, top, right, bottom);
+            return;
+        }
+
         if (mFirstLayout) {
             // Wait for first layout so that the ViewRootImpl surface has been created.
             initializeSurfaceState();
@@ -1085,6 +1094,13 @@
         crop.offsetTo(-(otherTaskRect.left - otherRect.left),
                 -(otherTaskRect.top - otherRect.top));
         t.setWindowCrop(mTiles.mSecondarySurface, crop);
+        // Reposition home and recents surfaces or they would be positioned relatively to its
+        // parent (split-screen secondary task) position.
+        for (int i = mTiles.mHomeAndRecentsSurfaces.size() - 1; i >= 0; --i) {
+            t.setPosition(mTiles.mHomeAndRecentsSurfaces.get(i),
+                    mTiles.mHomeBounds.left - otherTaskRect.left,
+                    mTiles.mHomeBounds.top - otherTaskRect.top);
+        }
         final SurfaceControl dividerCtrl = getWindowSurfaceControl();
         if (dividerCtrl != null) {
             if (isHorizontalDivision()) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
index c4089e5..6cb7f4f 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
@@ -26,12 +26,15 @@
 
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.WindowConfiguration;
+import android.graphics.Rect;
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.Display;
-import android.window.ITaskOrganizer;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
+import android.window.ITaskOrganizer;
+
+import java.util.ArrayList;
 
 class SplitScreenTaskOrganizer extends ITaskOrganizer.Stub {
     private static final String TAG = "SplitScreenTaskOrganizer";
@@ -43,6 +46,8 @@
     SurfaceControl mSecondarySurface;
     SurfaceControl mPrimaryDim;
     SurfaceControl mSecondaryDim;
+    ArrayList<SurfaceControl> mHomeAndRecentsSurfaces = new ArrayList<>();
+    Rect mHomeBounds = new Rect();
     final Divider mDivider;
 
     SplitScreenTaskOrganizer(Divider divider) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 8724e49..6ed7afe 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -29,9 +29,9 @@
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.Display;
+import android.view.WindowManagerGlobal;
 import android.window.IWindowContainer;
 import android.window.WindowContainerTransaction;
-import android.view.WindowManagerGlobal;
 import android.window.WindowOrganizer;
 
 import com.android.internal.annotations.GuardedBy;
@@ -157,6 +157,7 @@
         for (int i = homeStacks.size() - 1; i >= 0; --i) {
             wct.setBounds(homeStacks.get(i), homeBounds);
         }
+        layout.mTiles.mHomeBounds.set(homeBounds);
         return isHomeResizable;
     }
 
@@ -180,13 +181,17 @@
             if (rootTasks.isEmpty()) {
                 return false;
             }
+            tiles.mHomeAndRecentsSurfaces.clear();
             for (int i = rootTasks.size() - 1; i >= 0; --i) {
-                if (rootTasks.get(i).configuration.windowConfiguration.getWindowingMode()
+                final ActivityManager.RunningTaskInfo rootTask = rootTasks.get(i);
+                if (isHomeOrRecentTask(rootTask)) {
+                    tiles.mHomeAndRecentsSurfaces.add(rootTask.token.getLeash());
+                }
+                if (rootTask.configuration.windowConfiguration.getWindowingMode()
                         != WINDOWING_MODE_FULLSCREEN) {
                     continue;
                 }
-                wct.reparent(rootTasks.get(i).token, tiles.mSecondary.token,
-                        true /* onTop */);
+                wct.reparent(rootTask.token, tiles.mSecondary.token, true /* onTop */);
             }
             boolean isHomeResizable = applyHomeTasksMinimized(layout, null /* parent */, wct);
             WindowOrganizer.applyTransaction(wct);
@@ -213,6 +218,7 @@
             // Set launch root first so that any task created after getChildContainers and
             // before reparent (pretty unlikely) are put into fullscreen.
             TaskOrganizer.setLaunchRoot(Display.DEFAULT_DISPLAY, null);
+            tiles.mHomeAndRecentsSurfaces.clear();
             // TODO(task-org): Once task-org is more complete, consider using Appeared/Vanished
             //                 plus specific APIs to clean this up.
             List<ActivityManager.RunningTaskInfo> primaryChildren =
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/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/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/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/HideNotifsForOtherUsersCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinator.java
new file mode 100644
index 0000000..261ae07
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinator.java
@@ -0,0 +1,69 @@
+/*
+ * 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.collection.coordinator;
+
+import android.content.pm.UserInfo;
+import android.util.SparseArray;
+
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+
+import javax.inject.Inject;
+
+/**
+ * A coordinator that filters out notifications for other users
+ *
+ * The NotifCollection contains the notifs for ALL users, so we need to remove any notifications
+ * that have been posted specifically to other users. Note that some system notifications are not
+ * posted to any particular user, and so must be shown to everyone.
+ *
+ * TODO: The NotificationLockscreenUserManager currently maintains the list of active user profiles.
+ *  We should spin that off into a standalone section at some point.
+ */
+public class HideNotifsForOtherUsersCoordinator implements Coordinator {
+    private final NotificationLockscreenUserManager mLockscreenUserManager;
+
+    @Inject
+    public HideNotifsForOtherUsersCoordinator(
+            NotificationLockscreenUserManager lockscreenUserManager) {
+        mLockscreenUserManager = lockscreenUserManager;
+    }
+
+    @Override
+    public void attach(NotifPipeline pipeline) {
+        pipeline.addPreGroupFilter(mFilter);
+        mLockscreenUserManager.addUserChangedListener(mUserChangedListener);
+    }
+
+    private final NotifFilter mFilter = new NotifFilter("NotCurrentUserFilter") {
+        @Override
+        public boolean shouldFilterOut(NotificationEntry entry, long now) {
+            return !mLockscreenUserManager
+                    .isCurrentProfile(entry.getSbn().getUser().getIdentifier());
+        }
+    };
+
+    private final UserChangedListener mUserChangedListener = new UserChangedListener() {
+        @Override
+        public void onCurrentProfilesChanged(SparseArray<UserInfo> currentProfiles) {
+            mFilter.invalidateList();
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
index aaf71f5..b773856 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
@@ -95,11 +95,6 @@
         public boolean shouldFilterOut(NotificationEntry entry, long now) {
             final StatusBarNotification sbn = entry.getSbn();
 
-            // FILTER OUT the notification when the notification isn't for the current profile
-            if (!mLockscreenUserManager.isCurrentProfile(sbn.getUserId())) {
-                return true;
-            }
-
             // FILTER OUT the notification when the keyguard is showing and...
             if (mKeyguardStateController.isShowing()) {
                 // ... user settings or the device policy manager doesn't allow lockscreen
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
index 98104f8..03c0ae6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
@@ -49,15 +49,17 @@
     public NotifCoordinators(
             DumpManager dumpManager,
             FeatureFlags featureFlags,
-            HeadsUpCoordinator headsUpCoordinator,
+            HideNotifsForOtherUsersCoordinator hideNotifsForOtherUsersCoordinator,
             KeyguardCoordinator keyguardCoordinator,
             RankingCoordinator rankingCoordinator,
             ForegroundCoordinator foregroundCoordinator,
             DeviceProvisionedCoordinator deviceProvisionedCoordinator,
             BubbleCoordinator bubbleCoordinator,
+            HeadsUpCoordinator headsUpCoordinator,
             PreparationCoordinator preparationCoordinator) {
         dumpManager.registerDumpable(TAG, this);
         mCoordinators.add(new HideLocallyDismissedNotifsCoordinator());
+        mCoordinators.add(hideNotifsForOtherUsersCoordinator);
         mCoordinators.add(keyguardCoordinator);
         mCoordinators.add(rankingCoordinator);
         mCoordinators.add(foregroundCoordinator);
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/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/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index cf9d43e..d70484e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
-
 import android.annotation.IntDef;
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -30,241 +28,98 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.view.ViewTreeObserver;
-import android.view.accessibility.AccessibilityNodeInfo;
+import android.util.SparseArray;
+import android.view.ViewTreeObserver.OnPreDrawListener;
 
 import com.android.internal.graphics.ColorUtils;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.phone.ScrimController.ScrimVisibility;
-import com.android.systemui.statusbar.policy.AccessibilityController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
-import javax.inject.Inject;
-import javax.inject.Named;
-
 /**
  * Manages the different states and animations of the unlock icon.
  */
-public class LockIcon extends KeyguardAffordanceView implements
-        ViewTreeObserver.OnPreDrawListener {
+public class LockIcon extends KeyguardAffordanceView {
 
-    private static final int STATE_LOCKED = 0;
-    private static final int STATE_LOCK_OPEN = 1;
-    private static final int STATE_SCANNING_FACE = 2;
-    private static final int STATE_BIOMETRICS_ERROR = 3;
-    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    private final AccessibilityController mAccessibilityController;
-    private final KeyguardStateController mKeyguardStateController;
-    private final KeyguardBypassController mBypassController;
-    private final NotificationWakeUpCoordinator mWakeUpCoordinator;
-    private final HeadsUpManagerPhone mHeadsUpManager;
-
-    private int mLastState = 0;
-    private boolean mForceUpdate;
-    private boolean mTransientBiometricsError;
-    private boolean mIsFaceUnlockState;
-    private boolean mSimLocked;
-    private int mDensity;
+    static final int STATE_LOCKED = 0;
+    static final int STATE_LOCK_OPEN = 1;
+    static final int STATE_SCANNING_FACE = 2;
+    static final int STATE_BIOMETRICS_ERROR = 3;
+    private float mDozeAmount;
+    private int mIconColor;
+    private StateProvider mStateProvider;
+    private int mOldState;
     private boolean mPulsing;
     private boolean mDozing;
-    private boolean mDocked;
-    private boolean mBlockUpdates;
-    private int mIconColor;
-    private float mDozeAmount;
-    private boolean mBouncerShowingScrimmed;
-    private boolean mWakeAndUnlockRunning;
-    private boolean mKeyguardShowing;
-    private boolean mShowingLaunchAffordance;
     private boolean mKeyguardJustShown;
-    private boolean mUpdatePending;
-    private boolean mBouncerPreHideAnimation;
-    private int mStatusBarState = StatusBarState.SHADE;
+    private final SparseArray<Drawable> mDrawableCache = new SparseArray<>();
 
-    private final KeyguardStateController.Callback mKeyguardMonitorCallback =
-            new KeyguardStateController.Callback() {
-                @Override
-                public void onKeyguardShowingChanged() {
-                    boolean force = false;
-                    boolean wasShowing = mKeyguardShowing;
-                    mKeyguardShowing = mKeyguardStateController.isShowing();
-                    if (!wasShowing && mKeyguardShowing && mBlockUpdates) {
-                        mBlockUpdates = false;
-                        force = true;
-                    }
-                    if (!wasShowing && mKeyguardShowing) {
-                        mKeyguardJustShown = true;
-                    }
-                    update(force);
-                }
+    private final OnPreDrawListener mOnPreDrawListener = new OnPreDrawListener() {
+        @Override
+        public boolean onPreDraw() {
+            getViewTreeObserver().removeOnPreDrawListener(this);
 
-                @Override
-                public void onKeyguardFadingAwayChanged() {
-                    if (!mKeyguardStateController.isKeyguardFadingAway()) {
-                        mBouncerPreHideAnimation = false;
-                        if (mBlockUpdates) {
-                            mBlockUpdates = false;
-                            update(true /* force */);
-                        }
-                    }
-                }
+            int newState = mStateProvider.getState();
+            Drawable icon = getIcon(newState);
+            setImageDrawable(icon, false);
 
-                @Override
-                public void onUnlockedChanged() {
-                    update();
-                }
-            };
+            if (newState == STATE_SCANNING_FACE) {
+                announceForAccessibility(getResources().getString(
+                        R.string.accessibility_scanning_face));
+            }
 
-    @Inject
-    public LockIcon(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
-            AccessibilityController accessibilityController,
-            KeyguardBypassController bypassController,
-            NotificationWakeUpCoordinator wakeUpCoordinator,
-            KeyguardStateController keyguardStateController,
-            HeadsUpManagerPhone headsUpManager) {
+            if (icon instanceof AnimatedVectorDrawable) {
+                final AnimatedVectorDrawable animation = (AnimatedVectorDrawable) icon;
+                animation.forceAnimationOnUI();
+                animation.clearAnimationCallbacks();
+                animation.registerAnimationCallback(
+                        new Animatable2.AnimationCallback() {
+                            @Override
+                            public void onAnimationEnd(Drawable drawable) {
+                                if (getDrawable() == animation
+                                        && newState == mStateProvider.getState()
+                                        && newState == STATE_SCANNING_FACE) {
+                                    animation.start();
+                                } else {
+                                    Trace.endAsyncSection("LockIcon#Animation", newState);
+                                }
+                            }
+                        });
+                Trace.beginAsyncSection("LockIcon#Animation", newState);
+                animation.start();
+            }
+
+            return true;
+        }
+    };
+
+    public LockIcon(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mContext = context;
-        mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
-        mAccessibilityController = accessibilityController;
-        mBypassController = bypassController;
-        mWakeUpCoordinator = wakeUpCoordinator;
-        mKeyguardStateController = keyguardStateController;
-        mHeadsUpManager = headsUpManager;
     }
 
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mKeyguardStateController.addCallback(mKeyguardMonitorCallback);
-        mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure();
-        update();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mKeyguardStateController.removeCallback(mKeyguardMonitorCallback);
-    }
-
-    /**
-     * If we're currently presenting an authentication error message.
-     */
-    public void setTransientBiometricsError(boolean transientBiometricsError) {
-        mTransientBiometricsError = transientBiometricsError;
-        update();
+    void setStateProvider(StateProvider stateProvider) {
+        mStateProvider = stateProvider;
     }
 
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
-        final int density = newConfig.densityDpi;
-        if (density != mDensity) {
-            mDensity = density;
-            update();
-        }
-    }
-
-    public void update() {
-        update(false /* force */);
-    }
-
-    public void update(boolean force) {
-        if (force) {
-            mForceUpdate = true;
-        }
-        if (!mUpdatePending) {
-            mUpdatePending = true;
-            getViewTreeObserver().addOnPreDrawListener(this);
-        }
-    }
-
-    @Override
-    public boolean onPreDraw() {
-        mUpdatePending = false;
-        getViewTreeObserver().removeOnPreDrawListener(this);
-
-        int state = getState();
-        int lastState = mLastState;
-        boolean keyguardJustShown = mKeyguardJustShown;
-        mIsFaceUnlockState = state == STATE_SCANNING_FACE;
-        mLastState = state;
-        mKeyguardJustShown = false;
-
-        boolean shouldUpdate = lastState != state || mForceUpdate;
-        if (mBlockUpdates && canBlockUpdates()) {
-            shouldUpdate = false;
-        }
-        if (shouldUpdate) {
-            mForceUpdate = false;
-            @LockAnimIndex final int lockAnimIndex = getAnimationIndexForTransition(lastState,
-                    state, mPulsing, mDozing, keyguardJustShown);
-            boolean isAnim = lockAnimIndex != -1;
-            int iconRes = isAnim ? getThemedAnimationResId(lockAnimIndex) : getIconForState(state);
-
-            Drawable icon = mContext.getDrawable(iconRes);
-            final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
-                    ? (AnimatedVectorDrawable) icon
-                    : null;
-            setImageDrawable(icon, false);
-            if (mIsFaceUnlockState) {
-                announceForAccessibility(getContext().getString(
-                        R.string.accessibility_scanning_face));
-            }
-
-            if (animation != null && isAnim) {
-                animation.forceAnimationOnUI();
-                animation.clearAnimationCallbacks();
-                animation.registerAnimationCallback(new Animatable2.AnimationCallback() {
-                    @Override
-                    public void onAnimationEnd(Drawable drawable) {
-                        if (getDrawable() == animation && state == getState()
-                                && doesAnimationLoop(lockAnimIndex)) {
-                            animation.start();
-                        } else {
-                            Trace.endAsyncSection("LockIcon#Animation", state);
-                        }
-                    }
-                });
-                Trace.beginAsyncSection("LockIcon#Animation", state);
-                animation.start();
-            }
-        }
-        updateDarkTint();
-
-        updateIconVisibility();
-        updateClickability();
-
-        return true;
+        mDrawableCache.clear();
     }
 
     /**
      * Update the icon visibility
      * @return true if the visibility changed
      */
-    boolean updateIconVisibility() {
-        boolean onAodNotPulsingOrDocked = mDozing && (!mPulsing || mDocked);
-        boolean invisible = onAodNotPulsingOrDocked || mWakeAndUnlockRunning
-                || mShowingLaunchAffordance;
-        if (mBypassController.getBypassEnabled() && !mBouncerShowingScrimmed) {
-            if ((mHeadsUpManager.isHeadsUpGoingAway() || mHeadsUpManager.hasPinnedHeadsUp()
-                    || mStatusBarState == StatusBarState.KEYGUARD)
-                    && !mWakeUpCoordinator.getNotificationsFullyHidden()) {
-                invisible = true;
-            }
-        }
-        boolean wasInvisible = getVisibility() == INVISIBLE;
-        if (invisible != wasInvisible) {
-            setVisibility(invisible ? INVISIBLE : VISIBLE);
+    boolean updateIconVisibility(boolean visible) {
+        boolean wasVisible = getVisibility() == VISIBLE;
+        if (visible != wasVisible) {
+            setVisibility(visible ? VISIBLE : INVISIBLE);
             animate().cancel();
-            if (!invisible) {
+            if (visible) {
                 setScaleX(0);
                 setScaleY(0);
                 animate()
@@ -280,49 +135,47 @@
         return false;
     }
 
-    private boolean canBlockUpdates() {
-        return mKeyguardShowing || mKeyguardStateController.isKeyguardFadingAway();
+    void update(int oldState, boolean pulsing, boolean dozing, boolean keyguardJustShown) {
+        mOldState = oldState;
+        mPulsing = pulsing;
+        mDozing = dozing;
+        mKeyguardJustShown = keyguardJustShown;
+
+        getViewTreeObserver().addOnPreDrawListener(mOnPreDrawListener);
     }
 
-    private void updateClickability() {
-        if (mAccessibilityController == null) {
-            return;
+    void setDozeAmount(float dozeAmount) {
+        mDozeAmount = dozeAmount;
+        updateDarkTint();
+    }
+
+    void onThemeChange(int iconColor) {
+        mDrawableCache.clear();
+        mIconColor = iconColor;
+        updateDarkTint();
+    }
+
+    private void updateDarkTint() {
+        int color = ColorUtils.blendARGB(mIconColor, Color.WHITE, mDozeAmount);
+        setImageTintList(ColorStateList.valueOf(color));
+    }
+
+    private Drawable getIcon(int newState) {
+        @LockAnimIndex final int lockAnimIndex =
+                getAnimationIndexForTransition(mOldState, newState, mPulsing, mDozing,
+                        mKeyguardJustShown);
+
+        boolean isAnim = lockAnimIndex != -1;
+        int iconRes = isAnim ? getThemedAnimationResId(lockAnimIndex) : getIconForState(newState);
+
+        if (!mDrawableCache.contains(iconRes)) {
+            mDrawableCache.put(iconRes, getResources().getDrawable(iconRes));
         }
-        boolean canLock = mKeyguardStateController.isMethodSecure()
-                && mKeyguardStateController.canDismissLockScreen();
-        boolean clickToUnlock = mAccessibilityController.isAccessibilityEnabled();
-        setClickable(clickToUnlock);
-        setLongClickable(canLock && !clickToUnlock);
-        setFocusable(mAccessibilityController.isAccessibilityEnabled());
+
+        return mDrawableCache.get(iconRes);
     }
 
-    @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(info);
-        boolean fingerprintRunning = mKeyguardUpdateMonitor.isFingerprintDetectionRunning();
-        // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong
-        // as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to
-        // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the
-        // check of whether non-strong biometric is allowed
-        boolean unlockingAllowed = mKeyguardUpdateMonitor
-                        .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */);
-        if (fingerprintRunning && unlockingAllowed) {
-            AccessibilityNodeInfo.AccessibilityAction unlock
-                    = new AccessibilityNodeInfo.AccessibilityAction(
-                    AccessibilityNodeInfo.ACTION_CLICK,
-                    getContext().getString(R.string.accessibility_unlock_without_fingerprint));
-            info.addAction(unlock);
-            info.setHintText(getContext().getString(
-                    R.string.accessibility_waiting_for_fingerprint));
-        } else if (mIsFaceUnlockState) {
-            //Avoid 'button' to be spoken for scanning face
-            info.setClassName(LockIcon.class.getName());
-            info.setContentDescription(getContext().getString(
-                R.string.accessibility_scanning_face));
-        }
-    }
-
-    private int getIconForState(int state) {
+    static int getIconForState(int state) {
         int iconRes;
         switch (state) {
             case STATE_LOCKED:
@@ -343,11 +196,7 @@
         return iconRes;
     }
 
-    private boolean doesAnimationLoop(@LockAnimIndex int lockAnimIndex) {
-        return lockAnimIndex == SCANNING;
-    }
-
-    private static int getAnimationIndexForTransition(int oldState, int newState, boolean pulsing,
+    static int getAnimationIndexForTransition(int oldState, int newState, boolean pulsing,
             boolean dozing, boolean keyguardJustShown) {
 
         // Never animate when screen is off
@@ -367,42 +216,10 @@
         return -1;
     }
 
-    public void setBouncerShowingScrimmed(boolean bouncerShowing) {
-        mBouncerShowingScrimmed = bouncerShowing;
-        if (mBypassController.getBypassEnabled()) {
-            update();
-        }
-    }
-
-    /**
-     * Animate padlock opening when bouncer challenge is solved.
-     */
-    public void onBouncerPreHideAnimation() {
-        mBouncerPreHideAnimation = true;
-        update();
-    }
-
-    void setIconColor(int iconColor) {
-        mIconColor = iconColor;
-        updateDarkTint();
-    }
-
-    void setSimLocked(boolean simLocked) {
-        mSimLocked = simLocked;
-    }
-
-    /** Set if the device is docked. */
-    public void setDocked(boolean docked) {
-        if (mDocked != docked) {
-            mDocked = docked;
-            update();
-        }
-    }
-
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({ERROR, UNLOCK, LOCK, SCANNING})
     @interface LockAnimIndex {}
-    private static final int ERROR = 0, UNLOCK = 1, LOCK = 2, SCANNING = 3;
+    static final int ERROR = 0, UNLOCK = 1, LOCK = 2, SCANNING = 3;
     private static final int[][] LOCK_ANIM_RES_IDS = new int[][] {
             {
                     R.anim.lock_to_error,
@@ -433,7 +250,7 @@
     private int getThemedAnimationResId(@LockAnimIndex int lockAnimIndex) {
         final String setting = TextUtils.emptyIfNull(
                 Settings.Secure.getString(getContext().getContentResolver(),
-                Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES));
+                        Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES));
         if (setting.contains("com.android.theme.icon_pack.circular.android")) {
             return LOCK_ANIM_RES_IDS[1][lockAnimIndex];
         } else if (setting.contains("com.android.theme.icon_pack.filled.android")) {
@@ -444,83 +261,8 @@
         return LOCK_ANIM_RES_IDS[0][lockAnimIndex];
     }
 
-    private int getState() {
-        KeyguardUpdateMonitor updateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
-        if ((mKeyguardStateController.canDismissLockScreen() || !mKeyguardShowing
-                || mKeyguardStateController.isKeyguardGoingAway()) && !mSimLocked) {
-            return STATE_LOCK_OPEN;
-        } else if (mTransientBiometricsError) {
-            return STATE_BIOMETRICS_ERROR;
-        } else if (updateMonitor.isFaceDetectionRunning() && !mPulsing) {
-            return STATE_SCANNING_FACE;
-        } else {
-            return STATE_LOCKED;
-        }
+    interface StateProvider {
+        int getState();
     }
 
-    /**
-     * When keyguard is in pulsing (AOD2) state.
-     * @param pulsing {@code true} when pulsing.
-     */
-    public void setPulsing(boolean pulsing) {
-        mPulsing = pulsing;
-        update();
-    }
-
-    private void updateDarkTint() {
-        int color = ColorUtils.blendARGB(mIconColor, Color.WHITE, mDozeAmount);
-        setImageTintList(ColorStateList.valueOf(color));
-    }
-
-    /**
-     * We need to hide the lock whenever there's a fingerprint unlock, otherwise you'll see the
-     * icon on top of the black front scrim.
-     * @param wakeAndUnlock are we wake and unlocking
-     * @param isUnlock are we currently unlocking
-     */
-    public void onBiometricAuthModeChanged(boolean wakeAndUnlock, boolean isUnlock) {
-        if (wakeAndUnlock) {
-            mWakeAndUnlockRunning = true;
-        }
-        if (isUnlock && mBypassController.getBypassEnabled() && canBlockUpdates()) {
-            // We don't want the icon to change while we are unlocking
-            mBlockUpdates = true;
-        }
-        update();
-    }
-
-    /**
-     * When we're launching an affordance, like double pressing power to open camera.
-     */
-    public void onShowingLaunchAffordanceChanged(boolean showing) {
-        mShowingLaunchAffordance = showing;
-        update();
-    }
-
-    /**
-     * Called whenever the scrims become opaque, transparent or semi-transparent.
-     */
-    public void onScrimVisibilityChanged(@ScrimVisibility int scrimsVisible) {
-        if (mWakeAndUnlockRunning
-                && scrimsVisible == ScrimController.TRANSPARENT) {
-            mWakeAndUnlockRunning = false;
-            update();
-        }
-    }
-
-    void setDozing(boolean dozing) {
-        mDozing = dozing;
-        update();
-    }
-
-    void setDozeAmount(float dozeAmount) {
-        mDozeAmount = dozeAmount;
-        updateDarkTint();
-    }
-
-    /** Set the StatusBarState. */
-    public void setStatusBarState(int statusBarState) {
-        mStatusBarState = statusBarState;
-        updateIconVisibility();
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
index 2b1a8a4..f7c861b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
@@ -16,11 +16,19 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.systemui.statusbar.phone.LockIcon.STATE_BIOMETRICS_ERROR;
+import static com.android.systemui.statusbar.phone.LockIcon.STATE_LOCKED;
+import static com.android.systemui.statusbar.phone.LockIcon.STATE_LOCK_OPEN;
+import static com.android.systemui.statusbar.phone.LockIcon.STATE_SCANNING_FACE;
+
+import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.hardware.biometrics.BiometricSourceType;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
 
 import androidx.annotation.Nullable;
 
@@ -29,15 +37,18 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator.WakeUpListener;
 import com.android.systemui.statusbar.policy.AccessibilityController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import java.util.Optional;
 
@@ -59,6 +70,21 @@
     private final NotificationWakeUpCoordinator mNotificationWakeUpCoordinator;
     private final KeyguardBypassController mKeyguardBypassController;
     private final Optional<DockManager> mDockManager;
+    private final KeyguardStateController mKeyguardStateController;
+    private final Resources mResources;
+    private final HeadsUpManagerPhone mHeadsUpManagerPhone;
+    private boolean mKeyguardShowing;
+    private boolean mKeyguardJustShown;
+    private boolean mBlockUpdates;
+    private boolean mPulsing;
+    private boolean mDozing;
+    private boolean mSimLocked;
+    private boolean mTransientBiometricsError;
+    private boolean mDocked;
+    private boolean mWakeAndUnlockRunning;
+    private boolean mShowingLaunchAffordance;
+    private boolean mBouncerShowingScrimmed;
+    private int mStatusBarState = StatusBarState.SHADE;
     private LockIcon mLockIcon;
 
     private View.OnAttachStateChangeListener mOnAttachStateChangeListener =
@@ -69,10 +95,13 @@
             mConfigurationController.addCallback(mConfigurationListener);
             mNotificationWakeUpCoordinator.addListener(mWakeUpListener);
             mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
+            mKeyguardStateController.addCallback(mKeyguardMonitorCallback);
 
             mDockManager.ifPresent(dockManager -> dockManager.addListener(mDockEventListener));
 
+            mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure();
             mConfigurationListener.onThemeChanged();
+            update();
         }
 
         @Override
@@ -81,7 +110,7 @@
             mConfigurationController.removeCallback(mConfigurationListener);
             mNotificationWakeUpCoordinator.removeListener(mWakeUpListener);
             mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
-
+            mKeyguardStateController.removeCallback(mKeyguardMonitorCallback);
 
             mDockManager.ifPresent(dockManager -> dockManager.removeListener(mDockEventListener));
         }
@@ -91,32 +120,44 @@
             new StatusBarStateController.StateListener() {
                 @Override
                 public void onDozingChanged(boolean isDozing) {
-                    mLockIcon.setDozing(isDozing);
+                    setDozing(isDozing);
                 }
 
                 @Override
                 public void onDozeAmountChanged(float linear, float eased) {
-                    mLockIcon.setDozeAmount(eased);
+                    if (mLockIcon != null) {
+                        mLockIcon.setDozeAmount(eased);
+                    }
                 }
 
                 @Override
                 public void onStateChanged(int newState) {
-                    mLockIcon.setStatusBarState(newState);
+                    setStatusBarState(newState);
                 }
             };
 
     private final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
+        private int mDensity;
+
         @Override
         public void onThemeChanged() {
+            if (mLockIcon == null) {
+                return;
+            }
+
             TypedArray typedArray = mLockIcon.getContext().getTheme().obtainStyledAttributes(
                     null, new int[]{ R.attr.wallpaperTextColor }, 0, 0);
             int iconColor = typedArray.getColor(0, Color.WHITE);
             typedArray.recycle();
-            mLockIcon.setIconColor(iconColor);
+            mLockIcon.onThemeChange(iconColor);
         }
 
         @Override
         public void onDensityOrFontScaleChanged() {
+            if (mLockIcon == null) {
+                return;
+            }
+
             ViewGroup.LayoutParams lp = mLockIcon.getLayoutParams();
             if (lp == null) {
                 return;
@@ -125,24 +166,41 @@
             lp.height = mLockIcon.getResources().getDimensionPixelSize(
                     R.dimen.keyguard_lock_height);
             mLockIcon.setLayoutParams(lp);
-            mLockIcon.update(true /* force */);
+            update(true /* force */);
         }
 
         @Override
         public void onLocaleListChanged() {
+            if (mLockIcon == null) {
+                return;
+            }
+
             mLockIcon.setContentDescription(
                     mLockIcon.getResources().getText(R.string.accessibility_unlock_button));
-            mLockIcon.update(true /* force */);
+            update(true /* force */);
+        }
+
+        @Override
+        public void onConfigChanged(Configuration newConfig) {
+            final int density = newConfig.densityDpi;
+            if (density != mDensity) {
+                mDensity = density;
+                update();
+            }
         }
     };
 
     private final WakeUpListener mWakeUpListener = new WakeUpListener() {
         @Override
+        public void onPulseExpansionChanged(boolean expandingChanged) {
+        }
+
+        @Override
         public void onFullyHiddenChanged(boolean isFullyHidden) {
             if (mKeyguardBypassController.getBypassEnabled()) {
-                boolean changed = mLockIcon.updateIconVisibility();
+                boolean changed = updateIconVisibility();
                 if (changed) {
-                    mLockIcon.update();
+                    update();
                 }
             }
         }
@@ -152,30 +210,103 @@
             new KeyguardUpdateMonitorCallback() {
                 @Override
                 public void onSimStateChanged(int subId, int slotId, int simState) {
-                    mLockIcon.setSimLocked(mKeyguardUpdateMonitor.isSimPinSecure());
-                    mLockIcon.update();
+                    mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure();
+                    update();
                 }
 
                 @Override
                 public void onKeyguardVisibilityChanged(boolean showing) {
-                    mLockIcon.update();
+                    update();
                 }
 
                 @Override
                 public void onBiometricRunningStateChanged(boolean running,
                         BiometricSourceType biometricSourceType) {
-                    mLockIcon.update();
+                    update();
                 }
 
                 @Override
                 public void onStrongAuthStateChanged(int userId) {
-                    mLockIcon.update();
+                    update();
                 }
             };
 
     private final DockManager.DockEventListener mDockEventListener =
-            event -> mLockIcon.setDocked(event == DockManager.STATE_DOCKED
-                    || event == DockManager.STATE_DOCKED_HIDE);
+            event -> {
+                boolean docked =
+                        event == DockManager.STATE_DOCKED || event == DockManager.STATE_DOCKED_HIDE;
+                if (docked != mDocked) {
+                    mDocked = docked;
+                    update();
+                }
+            };
+
+    private final KeyguardStateController.Callback mKeyguardMonitorCallback =
+            new KeyguardStateController.Callback() {
+                @Override
+                public void onKeyguardShowingChanged() {
+                    boolean force = false;
+                    boolean wasShowing = mKeyguardShowing;
+                    mKeyguardShowing = mKeyguardStateController.isShowing();
+                    if (!wasShowing && mKeyguardShowing && mBlockUpdates) {
+                        mBlockUpdates = false;
+                        force = true;
+                    }
+                    if (!wasShowing && mKeyguardShowing) {
+                        mKeyguardJustShown = true;
+                    }
+                    update(force);
+                }
+
+                @Override
+                public void onKeyguardFadingAwayChanged() {
+                    if (!mKeyguardStateController.isKeyguardFadingAway()) {
+                        if (mBlockUpdates) {
+                            mBlockUpdates = false;
+                            update(true /* force */);
+                        }
+                    }
+                }
+
+                @Override
+                public void onUnlockedChanged() {
+                    update();
+                }
+            };
+
+    private final View.AccessibilityDelegate mAccessibilityDelegate =
+            new View.AccessibilityDelegate() {
+                @Override
+                public void onInitializeAccessibilityNodeInfo(View host,
+                        AccessibilityNodeInfo info) {
+                    super.onInitializeAccessibilityNodeInfo(host, info);
+                    boolean fingerprintRunning =
+                            mKeyguardUpdateMonitor.isFingerprintDetectionRunning();
+                    // Only checking if unlocking with Biometric is allowed (no matter strong or
+                    // non-strong as long as primary auth, i.e. PIN/pattern/password, is not
+                    // required), so it's ok to pass true for isStrongBiometric to
+                    // isUnlockingWithBiometricAllowed() to bypass the check of whether non-strong
+                    // biometric is allowed
+                    boolean unlockingAllowed = mKeyguardUpdateMonitor
+                            .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */);
+                    if (fingerprintRunning && unlockingAllowed) {
+                        AccessibilityNodeInfo.AccessibilityAction unlock =
+                                new AccessibilityNodeInfo.AccessibilityAction(
+                                AccessibilityNodeInfo.ACTION_CLICK,
+                                mResources.getString(
+                                        R.string.accessibility_unlock_without_fingerprint));
+                        info.addAction(unlock);
+                        info.setHintText(mResources.getString(
+                                R.string.accessibility_waiting_for_fingerprint));
+                    } else if (getState() == STATE_SCANNING_FACE) {
+                        //Avoid 'button' to be spoken for scanning face
+                        info.setClassName(LockIcon.class.getName());
+                        info.setContentDescription(mResources.getString(
+                                R.string.accessibility_scanning_face));
+                    }
+                }
+            };
+    private int mLastState;
 
     @Inject
     public LockscreenLockIconController(LockscreenGestureLogger lockscreenGestureLogger,
@@ -188,7 +319,10 @@
             ConfigurationController configurationController,
             NotificationWakeUpCoordinator notificationWakeUpCoordinator,
             KeyguardBypassController keyguardBypassController,
-            @Nullable DockManager dockManager) {
+            @Nullable DockManager dockManager,
+            KeyguardStateController keyguardStateController,
+            @Main Resources resources,
+            HeadsUpManagerPhone headsUpManagerPhone) {
         mLockscreenGestureLogger = lockscreenGestureLogger;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mLockPatternUtils = lockPatternUtils;
@@ -200,24 +334,31 @@
         mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
         mKeyguardBypassController = keyguardBypassController;
         mDockManager = dockManager == null ? Optional.empty() : Optional.of(dockManager);
+        mKeyguardStateController = keyguardStateController;
+        mResources = resources;
+        mHeadsUpManagerPhone = headsUpManagerPhone;
 
         mKeyguardIndicationController.setLockIconController(this);
     }
 
     /**
      * Associate the controller with a {@link LockIcon}
+     *
+     * TODO: change to an init method and inject the view.
      */
     public void attach(LockIcon lockIcon) {
         mLockIcon = lockIcon;
 
         mLockIcon.setOnClickListener(this::handleClick);
         mLockIcon.setOnLongClickListener(this::handleLongClick);
+        mLockIcon.setAccessibilityDelegate(mAccessibilityDelegate);
+        mLockIcon.setStateProvider(this::getState);
 
         if (mLockIcon.isAttachedToWindow()) {
             mOnAttachStateChangeListener.onViewAttachedToWindow(mLockIcon);
         }
         mLockIcon.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
-        mLockIcon.setStatusBarState(mStatusBarStateController.getState());
+        setStatusBarState(mStatusBarStateController.getState());
     }
 
     public LockIcon getView() {
@@ -228,8 +369,10 @@
      * Called whenever the scrims become opaque, transparent or semi-transparent.
      */
     public void onScrimVisibilityChanged(Integer scrimsVisible) {
-        if (mLockIcon != null) {
-            mLockIcon.onScrimVisibilityChanged(scrimsVisible);
+        if (mWakeAndUnlockRunning
+                && scrimsVisible == ScrimController.TRANSPARENT) {
+            mWakeAndUnlockRunning = false;
+            update();
         }
     }
 
@@ -237,55 +380,56 @@
      * Propagate {@link StatusBar} pulsing state.
      */
     public void setPulsing(boolean pulsing) {
-        if (mLockIcon != null) {
-            mLockIcon.setPulsing(pulsing);
-        }
+        mPulsing = pulsing;
+        update();
     }
 
     /**
-     * Called when the biometric authentication mode changes.
-     *
-     * @param wakeAndUnlock If the type is {@link BiometricUnlockController#isWakeAndUnlock()}
-     * @param isUnlock      If the type is {@link BiometricUnlockController#isBiometricUnlock()} ()
+     * We need to hide the lock whenever there's a fingerprint unlock, otherwise you'll see the
+     * icon on top of the black front scrim.
+     * @param wakeAndUnlock are we wake and unlocking
+     * @param isUnlock are we currently unlocking
      */
     public void onBiometricAuthModeChanged(boolean wakeAndUnlock, boolean isUnlock) {
-        if (mLockIcon != null) {
-            mLockIcon.onBiometricAuthModeChanged(wakeAndUnlock, isUnlock);
+        if (wakeAndUnlock) {
+            mWakeAndUnlockRunning = true;
         }
+        if (isUnlock && mKeyguardBypassController.getBypassEnabled() && canBlockUpdates()) {
+            // We don't want the icon to change while we are unlocking
+            mBlockUpdates = true;
+        }
+        update();
     }
 
     /**
      * When we're launching an affordance, like double pressing power to open camera.
      */
     public void onShowingLaunchAffordanceChanged(Boolean showing) {
-        if (mLockIcon != null) {
-            mLockIcon.onShowingLaunchAffordanceChanged(showing);
-        }
+        mShowingLaunchAffordance = showing;
+        update();
     }
 
     /** Sets whether the bouncer is showing. */
     public void setBouncerShowingScrimmed(boolean bouncerShowing) {
-        if (mLockIcon != null) {
-            mLockIcon.setBouncerShowingScrimmed(bouncerShowing);
+        mBouncerShowingScrimmed = bouncerShowing;
+        if (mKeyguardBypassController.getBypassEnabled()) {
+            update();
         }
     }
 
     /**
-     * When {@link KeyguardBouncer} starts to be dismissed and starts to play its animation.
+     * Animate padlock opening when bouncer challenge is solved.
      */
     public void onBouncerPreHideAnimation() {
-        if (mLockIcon != null) {
-            mLockIcon.onBouncerPreHideAnimation();
-        }
+        update();
     }
 
     /**
      * If we're currently presenting an authentication error message.
      */
     public void setTransientBiometricsError(boolean transientBiometricsError) {
-        if (mLockIcon != null) {
-            mLockIcon.setTransientBiometricsError(transientBiometricsError);
-        }
+        mTransientBiometricsError = transientBiometricsError;
+        update();
     }
 
     private boolean handleLongClick(View view) {
@@ -306,4 +450,90 @@
         }
         mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
     }
+
+    private void update() {
+        update(false /* force */);
+    }
+
+    private void update(boolean force) {
+        int state = getState();
+        boolean shouldUpdate = mLastState != state || force;
+        if (mBlockUpdates && canBlockUpdates()) {
+            shouldUpdate = false;
+        }
+        if (shouldUpdate && mLockIcon != null) {
+            mLockIcon.update(mLastState, mPulsing, mDozing, mKeyguardJustShown);
+        }
+        mLastState = state;
+        mKeyguardJustShown = false;
+        updateIconVisibility();
+        updateClickability();
+    }
+
+    private int getState() {
+        if ((mKeyguardStateController.canDismissLockScreen() || !mKeyguardShowing
+                || mKeyguardStateController.isKeyguardGoingAway()) && !mSimLocked) {
+            return STATE_LOCK_OPEN;
+        } else if (mTransientBiometricsError) {
+            return STATE_BIOMETRICS_ERROR;
+        } else if (mKeyguardUpdateMonitor.isFaceDetectionRunning() && !mPulsing) {
+            return STATE_SCANNING_FACE;
+        } else {
+            return STATE_LOCKED;
+        }
+    }
+
+    private boolean canBlockUpdates() {
+        return mKeyguardShowing || mKeyguardStateController.isKeyguardFadingAway();
+    }
+
+    private void setDozing(boolean isDozing) {
+        mDozing = isDozing;
+        update();
+    }
+
+    /** Set the StatusBarState. */
+    private void setStatusBarState(int statusBarState) {
+        mStatusBarState = statusBarState;
+        updateIconVisibility();
+    }
+
+    /**
+     * Update the icon visibility
+     * @return true if the visibility changed
+     */
+    private boolean updateIconVisibility() {
+        boolean onAodNotPulsingOrDocked = mDozing && (!mPulsing || mDocked);
+        boolean invisible = onAodNotPulsingOrDocked || mWakeAndUnlockRunning
+                || mShowingLaunchAffordance;
+        if (mKeyguardBypassController.getBypassEnabled() && !mBouncerShowingScrimmed) {
+            if ((mHeadsUpManagerPhone.isHeadsUpGoingAway()
+                    || mHeadsUpManagerPhone.hasPinnedHeadsUp()
+                    || mStatusBarState == StatusBarState.KEYGUARD)
+                    && !mNotificationWakeUpCoordinator.getNotificationsFullyHidden()) {
+                invisible = true;
+            }
+        }
+
+        if (mLockIcon == null) {
+            return false;
+        }
+
+        return mLockIcon.updateIconVisibility(!invisible);
+    }
+
+    private void updateClickability() {
+        if (mAccessibilityController == null) {
+            return;
+        }
+        boolean canLock = mKeyguardStateController.isMethodSecure()
+                && mKeyguardStateController.canDismissLockScreen();
+        boolean clickToUnlock = mAccessibilityController.isAccessibilityEnabled();
+        if (mLockIcon != null) {
+            mLockIcon.setClickable(clickToUnlock);
+            mLockIcon.setLongClickable(canLock && !clickToUnlock);
+            mLockIcon.setFocusable(mAccessibilityController.isAccessibilityEnabled());
+        }
+    }
+
 }
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/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
index 56aae17..c637123 100644
--- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -34,7 +34,6 @@
 import com.android.systemui.qs.customize.QSCustomizer;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.phone.LockIcon;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -148,11 +147,6 @@
         KeyguardMessageArea createKeyguardMessageArea();
 
         /**
-         * Creates the keyguard LockIcon.
-         */
-        LockIcon createLockIcon();
-
-        /**
          * Creates the QSPanel.
          */
         QSPanel createQSPanel();
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
index 5411839..bae5bb4 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
@@ -20,6 +20,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.content.res.Configuration;
+import android.graphics.Point;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -97,7 +98,7 @@
         }
         if (mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation()
                 != pd.mRotation && isImeShowing(displayId)) {
-            pd.startAnimation(true);
+            pd.startAnimation(true, false /* forceRestart */);
         }
     }
 
@@ -200,7 +201,15 @@
                         continue;
                     }
                     if (activeControl.getType() == InsetsState.ITYPE_IME) {
-                        mImeSourceControl = activeControl;
+                        mHandler.post(() -> {
+                            final Point lastSurfacePosition = mImeSourceControl != null
+                                    ? mImeSourceControl.getSurfacePosition() : null;
+                            mImeSourceControl = activeControl;
+                            if (!activeControl.getSurfacePosition().equals(lastSurfacePosition)
+                                    && mAnimation != null) {
+                                startAnimation(mImeShowing, true /* forceRestart */);
+                            }
+                        });
                     }
                 }
             }
@@ -212,7 +221,7 @@
                 return;
             }
             if (DEBUG) Slog.d(TAG, "Got showInsets for ime");
-            startAnimation(true /* show */);
+            startAnimation(true /* show */, false /* forceRestart */);
         }
 
         @Override
@@ -221,7 +230,7 @@
                 return;
             }
             if (DEBUG) Slog.d(TAG, "Got hideInsets for ime");
-            startAnimation(false /* show */);
+            startAnimation(false /* show */, false /* forceRestart */);
         }
 
         /**
@@ -239,7 +248,7 @@
             return imeSource.getFrame().top + (int) surfaceOffset;
         }
 
-        private void startAnimation(final boolean show) {
+        private void startAnimation(final boolean show, final boolean forceRestart) {
             final InsetsSource imeSource = mInsetsState.getSource(InsetsState.ITYPE_IME);
             if (imeSource == null || mImeSourceControl == null) {
                 return;
@@ -250,7 +259,7 @@
                             + (mAnimationDirection == DIRECTION_SHOW ? "SHOW"
                             : (mAnimationDirection == DIRECTION_HIDE ? "HIDE" : "NONE")));
                 }
-                if ((mAnimationDirection == DIRECTION_SHOW && show)
+                if (!forceRestart && (mAnimationDirection == DIRECTION_SHOW && show)
                         || (mAnimationDirection == DIRECTION_HIDE && !show)) {
                     return;
                 }
@@ -270,11 +279,6 @@
                 final float shownY = defaultY;
                 final float startY = show ? hiddenY : shownY;
                 final float endY = show ? shownY : hiddenY;
-                if (mImeShowing && show) {
-                    // IME is already showing, so set seek to end
-                    seekValue = shownY;
-                    seek = true;
-                }
                 mImeShowing = show;
                 mAnimation = ValueAnimator.ofFloat(startY, endY);
                 mAnimation.setDuration(
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt
index 464a740..072bc44 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt
@@ -22,6 +22,8 @@
 import android.testing.TestableLooper
 import android.view.View
 import android.widget.TextView
+import androidx.arch.core.executor.ArchTaskExecutor
+import androidx.arch.core.executor.TaskExecutor
 import androidx.test.filters.SmallTest
 
 import com.android.systemui.R
@@ -50,25 +52,46 @@
     private lateinit var mediaMetadata: MediaMetadata.Builder
     private lateinit var entry: NotificationEntryBuilder
     @Mock private lateinit var mockView: View
-    private lateinit var textView: TextView
+    private lateinit var songView: TextView
+    private lateinit var artistView: TextView
     @Mock private lateinit var mockIcon: Icon
 
+    private val taskExecutor: TaskExecutor = object : TaskExecutor() {
+        public override fun executeOnDiskIO(runnable: Runnable) {
+            runnable.run()
+        }
+        public override fun postToMainThread(runnable: Runnable) {
+            runnable.run()
+        }
+        public override fun isMainThread(): Boolean {
+            return true
+        }
+    }
+
     @Before
     public fun setup() {
         fakeExecutor = FakeExecutor(FakeSystemClock())
         keyguardMediaPlayer = KeyguardMediaPlayer(context, fakeExecutor)
-        mockView = mock(View::class.java)
-        textView = TextView(context)
         mockIcon = mock(Icon::class.java)
+
+        mockView = mock(View::class.java)
+        songView = TextView(context)
+        artistView = TextView(context)
+        whenever<TextView>(mockView.findViewById(R.id.header_title)).thenReturn(songView)
+        whenever<TextView>(mockView.findViewById(R.id.header_artist)).thenReturn(artistView)
+
         mediaMetadata = MediaMetadata.Builder()
         entry = NotificationEntryBuilder()
 
+        ArchTaskExecutor.getInstance().setDelegate(taskExecutor)
+
         keyguardMediaPlayer.bindView(mockView)
     }
 
     @After
     public fun tearDown() {
         keyguardMediaPlayer.unbindView()
+        ArchTaskExecutor.getInstance().setDelegate(null)
     }
 
     @Test
@@ -87,34 +110,36 @@
     @Test
     public fun testUpdateControls() {
         keyguardMediaPlayer.updateControls(entry.build(), mockIcon, mediaMetadata.build())
+        FakeExecutor.exhaustExecutors(fakeExecutor)
         verify(mockView).setVisibility(View.VISIBLE)
     }
 
     @Test
     public fun testClearControls() {
         keyguardMediaPlayer.clearControls()
+        FakeExecutor.exhaustExecutors(fakeExecutor)
         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)
+        assertThat(fakeExecutor.runAllReady()).isEqualTo(1)
+        assertThat(songView.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)
+        assertThat(fakeExecutor.runAllReady()).isEqualTo(1)
+        assertThat(artistView.getText()).isEqualTo(artist)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 6e612d7..4f16031 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -62,7 +62,9 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationRemoveInterceptor;
@@ -136,6 +138,9 @@
     @Mock
     private FloatingContentCoordinator mFloatingContentCoordinator;
 
+    private SysUiState mSysUiState;
+    private boolean mSysUiStateBubblesExpanded;
+
     @Captor
     private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor;
     @Captor
@@ -229,6 +234,11 @@
         mZenModeConfig.suppressedVisualEffects = 0;
         when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
 
+        mSysUiState = new SysUiState();
+        mSysUiState.addCallback(sysUiFlags ->
+                mSysUiStateBubblesExpanded =
+                        (sysUiFlags & QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED) != 0);
+
         TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
                 new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
                         mock(PowerManager.class),
@@ -257,7 +267,8 @@
                 mNotifPipeline,
                 mFeatureFlagsOldPipeline,
                 mDumpManager,
-                mFloatingContentCoordinator);
+                mFloatingContentCoordinator,
+                mSysUiState);
         mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
         mBubbleController.setExpandListener(mBubbleExpandListener);
 
@@ -277,6 +288,7 @@
         assertTrue(mBubbleController.hasBubbles());
 
         verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
+        assertFalse(mSysUiStateBubblesExpanded);
     }
 
     @Test
@@ -284,6 +296,7 @@
         assertFalse(mBubbleController.hasBubbles());
         mBubbleController.updateBubble(mRow.getEntry());
         assertTrue(mBubbleController.hasBubbles());
+        assertFalse(mSysUiStateBubblesExpanded);
     }
 
     @Test
@@ -300,6 +313,8 @@
         assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
         verify(mNotificationEntryManager, times(2)).updateNotifications(anyString());
         verify(mBubbleStateChangeListener).onHasBubblesChanged(false);
+
+        assertFalse(mSysUiStateBubblesExpanded);
     }
 
     @Test
@@ -323,6 +338,8 @@
         verify(mNotificationEntryManager, times(1)).performRemoveNotification(
                 eq(mRow.getEntry().getSbn()), anyInt());
         assertFalse(mBubbleController.hasBubbles());
+
+        assertFalse(mSysUiStateBubblesExpanded);
     }
 
     @Test
@@ -340,6 +357,8 @@
         verify(mNotificationEntryManager, times(3)).updateNotifications(any());
         assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
         assertNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey()));
+
+        assertFalse(mSysUiStateBubblesExpanded);
     }
 
     @Test
@@ -363,6 +382,8 @@
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
         assertTrue(mNotificationShadeWindowController.getBubbleExpanded());
 
+        assertTrue(mSysUiStateBubblesExpanded);
+
         // Make sure the notif is suppressed
         assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
                 mRow.getEntry()));
@@ -372,6 +393,8 @@
         verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey());
         assertFalse(mBubbleController.isStackExpanded());
         assertFalse(mNotificationShadeWindowController.getBubbleExpanded());
+
+        assertFalse(mSysUiStateBubblesExpanded);
     }
 
     @Test
@@ -395,6 +418,8 @@
         assertTrue(mBubbleController.isStackExpanded());
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
 
+        assertTrue(mSysUiStateBubblesExpanded);
+
         // Last added is the one that is expanded
         assertEquals(mRow2.getEntry(), mBubbleData.getSelectedBubble().getEntry());
         assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
@@ -416,6 +441,8 @@
         // Collapse
         mBubbleController.collapseStack();
         assertFalse(mBubbleController.isStackExpanded());
+
+        assertFalse(mSysUiStateBubblesExpanded);
     }
 
     @Test
@@ -437,6 +464,8 @@
         assertTrue(mBubbleController.isStackExpanded());
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
 
+        assertTrue(mSysUiStateBubblesExpanded);
+
         // Notif is suppressed after expansion
         assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
                 mRow.getEntry()));
@@ -463,6 +492,8 @@
         assertTrue(mBubbleController.isStackExpanded());
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
 
+        assertTrue(mSysUiStateBubblesExpanded);
+
         // Notif is suppressed after expansion
         assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
                 mRow.getEntry()));
@@ -493,6 +524,8 @@
         BubbleStackView stackView = mBubbleController.getStackView();
         mBubbleController.expandStack();
 
+        assertTrue(mSysUiStateBubblesExpanded);
+
         assertTrue(mBubbleController.isStackExpanded());
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
 
@@ -522,6 +555,8 @@
         verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey());
         verify(mBubbleStateChangeListener).onHasBubblesChanged(false);
         assertFalse(mBubbleController.hasBubbles());
+
+        assertFalse(mSysUiStateBubblesExpanded);
     }
 
     @Test
@@ -541,6 +576,8 @@
 
         // # of bubbles should change
         verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+
+        assertFalse(mSysUiStateBubblesExpanded);
     }
 
     @Test
@@ -559,6 +596,8 @@
 
         // # of bubbles should change
         verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+
+        assertTrue(mSysUiStateBubblesExpanded);
     }
 
     @Test
@@ -579,6 +618,8 @@
 
         // # of bubbles should change
         verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+
+        assertFalse(mSysUiStateBubblesExpanded);
     }
 
     @Test
@@ -605,6 +646,8 @@
 
         // # of bubbles should change
         verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+
+        assertFalse(mSysUiStateBubblesExpanded);
     }
 
     @Test
@@ -619,6 +662,8 @@
                 mRow.getEntry().getKey(), mRow.getEntry(), REASON_APP_CANCEL);
 
         mBubbleController.expandStackAndSelectBubble(key);
+
+        assertTrue(mSysUiStateBubblesExpanded);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 6244644..a31e3f8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -58,6 +58,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -132,6 +133,9 @@
     private KeyguardBypassController mKeyguardBypassController;
     @Mock
     private FloatingContentCoordinator mFloatingContentCoordinator;
+
+    private SysUiState mSysUiState = new SysUiState();
+
     @Captor
     private ArgumentCaptor<NotifCollectionListener> mNotifListenerCaptor;
     private TestableBubbleController mBubbleController;
@@ -242,7 +246,8 @@
                 mNotifPipeline,
                 mFeatureFlagsNewPipeline,
                 mDumpManager,
-                mFloatingContentCoordinator);
+                mFloatingContentCoordinator,
+                mSysUiState);
         mBubbleController.addNotifCallback(mNotifCallback);
         mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
         mBubbleController.setExpandListener(mBubbleExpandListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
index d3d90c4..f486102 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -52,12 +53,13 @@
             NotifPipeline notifPipeline,
             FeatureFlags featureFlags,
             DumpManager dumpManager,
-            FloatingContentCoordinator floatingContentCoordinator) {
+            FloatingContentCoordinator floatingContentCoordinator,
+            SysUiState sysUiState) {
         super(context,
                 notificationShadeWindowController, statusBarStateController, shadeController,
                 data, Runnable::run, configurationController, interruptionStateProvider,
                 zenModeController, lockscreenUserManager, groupManager, entryManager,
-                notifPipeline, featureFlags, dumpManager, floatingContentCoordinator);
+                notifPipeline, featureFlags, dumpManager, floatingContentCoordinator, sysUiState);
         setInflateSynchronously(true);
     }
 }
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/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/HideNotifsForOtherUsersCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java
new file mode 100644
index 0000000..87fc020
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.collection.coordinator;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.util.SparseArray;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable.PluggableListener;
+
+import org.junit.Before;
+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;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class HideNotifsForOtherUsersCoordinatorTest extends SysuiTestCase {
+
+    @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
+    @Mock private NotifPipeline mNotifPipeline;
+    @Mock private PluggableListener<NotifFilter> mInvalidationListener;
+
+    @Captor private ArgumentCaptor<UserChangedListener> mUserChangedListenerCaptor;
+    @Captor private ArgumentCaptor<NotifFilter> mNotifFilterCaptor;
+
+    private UserChangedListener mCapturedUserChangeListener;
+    private NotifFilter mCapturedNotifFilter;
+
+    private NotificationEntry mEntry = new NotificationEntryBuilder().build();
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        HideNotifsForOtherUsersCoordinator coordinator =
+                new HideNotifsForOtherUsersCoordinator(mLockscreenUserManager);
+        coordinator.attach(mNotifPipeline);
+
+        verify(mLockscreenUserManager).addUserChangedListener(mUserChangedListenerCaptor.capture());
+        verify(mNotifPipeline).addPreGroupFilter(mNotifFilterCaptor.capture());
+
+        mCapturedUserChangeListener = mUserChangedListenerCaptor.getValue();
+        mCapturedNotifFilter = mNotifFilterCaptor.getValue();
+
+        mCapturedNotifFilter.setInvalidationListener(mInvalidationListener);
+    }
+
+    @Test
+    public void testFilterOutNotifsFromOtherProfiles() {
+        // GIVEN that all notifs are NOT for the current user
+        when(mLockscreenUserManager.isCurrentProfile(anyInt())).thenReturn(false);
+
+        // THEN they should all be filtered out
+        assertTrue(mCapturedNotifFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    @Test
+    public void testPreserveNotifsFromThisProfile() {
+        // GIVEN that all notifs ARE for the current user
+        when(mLockscreenUserManager.isCurrentProfile(anyInt())).thenReturn(true);
+
+        // THEN none should be filtered out
+        assertFalse(mCapturedNotifFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    @Test
+    public void testFilterIsInvalidatedWhenProfilesChange() {
+        // WHEN the current user profiles change
+        mCapturedUserChangeListener.onCurrentProfilesChanged(new SparseArray<>());
+
+        // THEN the filter is invalidated
+        verify(mInvalidationListener).onPluggableInvalidated(mCapturedNotifFilter);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
index c4f3a16..4f48108 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
@@ -102,16 +102,6 @@
     }
 
     @Test
-    public void notificationNotForCurrentProfile() {
-        // GIVEN the notification isn't for the given user
-        setupUnfilteredState(mEntry);
-        when(mLockscreenUserManager.isCurrentProfile(NOTIF_USER_ID)).thenReturn(false);
-
-        // THEN filter out the entry
-        assertTrue(mKeyguardFilter.shouldFilterOut(mEntry, 0));
-    }
-
-    @Test
     public void keyguardNotShowing() {
         // GIVEN the lockscreen isn't showing
         setupUnfilteredState(mEntry);
@@ -229,9 +219,6 @@
      * KeyguardNotificationCoordinator when the keyguard is showing.
      */
     private void setupUnfilteredState(NotificationEntry entry) {
-        // notification is for current profile
-        when(mLockscreenUserManager.isCurrentProfile(NOTIF_USER_ID)).thenReturn(true);
-
         // keyguard is showing
         when(mKeyguardStateController.isShowing()).thenReturn(true);
 
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/phone/LockscreenIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
index 487885a..85b5d70 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.res.Resources;
 import android.view.View;
 
 import androidx.test.filters.SmallTest;
@@ -35,6 +36,7 @@
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.policy.AccessibilityController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -71,6 +73,12 @@
     private KeyguardBypassController mKeyguardBypassController;
     @Mock
     private DockManager mDockManager;
+    @Mock
+    private KeyguardStateController mKeyguardStateController;
+    @Mock
+    private Resources mResources;
+    @Mock
+    private HeadsUpManagerPhone mHeadsUpManagerPhone;
 
 
     @Before
@@ -81,7 +89,8 @@
                 mLockscreenGestureLogger, mKeyguardUpdateMonitor, mLockPatternUtils,
                 mShadeController, mAccessibilityController, mKeyguardIndicationController,
                 mStatusBarStateController, mConfigurationController, mNotificationWakeUpCoordinator,
-                mKeyguardBypassController, mDockManager);
+                mKeyguardBypassController, mDockManager, mKeyguardStateController, mResources,
+                mHeadsUpManagerPhone);
 
         mLockIconController.attach(mLockIcon);
     }
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index 2fbba68..00d0d9c 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -60,6 +60,7 @@
     hostdex: true, // for hiddenapi check
     visibility: ["//frameworks/base/packages/Tethering:__subpackages__"],
     apex_available: ["com.android.tethering"],
+    permitted_packages: ["android.net"],
 }
 
 stubs_defaults {
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 ec84ae7..76a8e14 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -40,6 +40,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_TEST;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkPolicyManager.RULE_NONE;
 import static android.net.NetworkPolicyManager.uidRulesToString;
@@ -50,6 +51,7 @@
 
 import static java.util.Map.Entry;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
@@ -2702,10 +2704,18 @@
 
             switch (msg.what) {
                 case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: {
-                    final NetworkCapabilities networkCapabilities = (NetworkCapabilities) msg.obj;
+                    NetworkCapabilities networkCapabilities = (NetworkCapabilities) msg.obj;
                     if (networkCapabilities.hasConnectivityManagedCapability()) {
                         Slog.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
                     }
+                    if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
+                        // Make sure the original object is not mutated. NetworkAgent normally
+                        // makes a copy of the capabilities when sending the message through
+                        // the Messenger, but if this ever changes, not making a defensive copy
+                        // here will give attack vectors to clients using this code path.
+                        networkCapabilities = new NetworkCapabilities(networkCapabilities);
+                        networkCapabilities.restrictCapabilitesForTestNetwork();
+                    }
                     updateCapabilities(nai.getCurrentScore(), nai, networkCapabilities);
                     break;
                 }
@@ -5778,7 +5788,16 @@
     public Network registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
             LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
             int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) {
-        enforceNetworkFactoryPermission();
+        if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
+            enforceAnyPermissionOf(Manifest.permission.MANAGE_TEST_NETWORKS);
+            // Strictly, sanitizing here is unnecessary as the capabilities will be sanitized in
+            // the call to mixInCapabilities below anyway, but sanitizing here means the NAI never
+            // sees capabilities that may be malicious, which might prevent mistakes in the future.
+            networkCapabilities = new NetworkCapabilities(networkCapabilities);
+            networkCapabilities.restrictCapabilitesForTestNetwork();
+        } else {
+            enforceNetworkFactoryPermission();
+        }
 
         LinkProperties lp = new LinkProperties(linkProperties);
         lp.ensureDirectlyConnectedRoutes();
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/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 0bf81e0..bb94460 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -155,6 +155,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.pm.Installer;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.storage.AppFuseBridge;
 import com.android.server.storage.StorageSessionController;
 import com.android.server.storage.StorageSessionController.ExternalStorageServiceException;
@@ -3268,6 +3269,25 @@
         }
     }
 
+    @Override
+    public void fixupAppDir(String path) {
+        final Matcher matcher = KNOWN_APP_DIR_PATHS.matcher(path);
+        if (matcher.matches()) {
+            AndroidPackage pkg = mPmInternal.getPackage(matcher.group(3));
+            if (pkg != null) {
+                try {
+                    mVold.fixupAppDir(path + "/", pkg.getUid());
+                } catch (RemoteException | ServiceSpecificException e) {
+                    Log.e(TAG, "Failed to fixup app dir for " + pkg.getPackageName(), e);
+                }
+            } else {
+                Log.e(TAG, "Can't find package belonging to " + path);
+            }
+        } else {
+            Log.e(TAG, "Path " + path + " is not a valid application-specific directory");
+        }
+    }
+
     /** Not thread safe */
     class AppFuseMountScope extends AppFuseBridge.MountScope {
         private boolean mMounted = false;
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/incident/IncidentCompanionService.java b/services/core/java/com/android/server/incident/IncidentCompanionService.java
index 87fe785..ad08663 100644
--- a/services/core/java/com/android/server/incident/IncidentCompanionService.java
+++ b/services/core/java/com/android/server/incident/IncidentCompanionService.java
@@ -50,6 +50,9 @@
  */
 public class IncidentCompanionService extends SystemService {
     static final String TAG = "IncidentCompanionService";
+    // TODO(b/152289743): Expose below intent.
+    private static final String INTENT_CHECK_USER_CONSENT =
+            "com.android.internal.intent.action.CHECK_USER_CONSENT";
 
     /**
      * Dump argument for proxying restricted image dumps to the services
@@ -89,6 +92,12 @@
 
             final long ident = Binder.clearCallingIdentity();
             try {
+                Intent intent = new Intent(INTENT_CHECK_USER_CONSENT);
+                intent.setPackage(callingPackage);
+                intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+                getContext().sendBroadcast(intent, android.Manifest.permission.DUMP);
+
                 mPendingReports.authorizeReport(callingUid, callingPackage,
                         receiverClass, reportId, flags, listener);
             } finally {
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 58e332a..c1fbcfb 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -653,9 +653,6 @@
         mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0);
         mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_TIMEOUT), 0);
 
-        mNetworkConnectivityHandler = new GnssNetworkConnectivityHandler(context,
-                GnssLocationProvider.this::onNetworkAvailable, mLooper);
-
         // App ops service to keep track of who is accessing the GPS
         mAppOps = mContext.getSystemService(AppOpsManager.class);
 
@@ -677,6 +674,9 @@
         mNIHandler = new GpsNetInitiatedHandler(context,
                 mNetInitiatedListener,
                 mSuplEsEnabled);
+        mNetworkConnectivityHandler = new GnssNetworkConnectivityHandler(context,
+                GnssLocationProvider.this::onNetworkAvailable, mLooper, mNIHandler);
+
         sendMessage(INITIALIZE_HANDLER, 0, null);
 
         mGnssStatusListenerHelper = new GnssStatusListenerHelper(mContext, mHandler) {
diff --git a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
index 93227bd..5d6474b 100644
--- a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
@@ -29,12 +29,22 @@
 import android.provider.Telephony.Carriers;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.PreciseCallState;
+import android.telephony.PhoneStateListener;
 import android.util.Log;
 
+import com.android.internal.location.GpsNetInitiatedHandler;
+
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.Arrays;
+import java.util.Map;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Iterator;
 
 /**
  * Handles network connection requests and network state change updates for AGPS data download.
@@ -86,6 +96,9 @@
     private HashMap<Network, NetworkAttributes> mAvailableNetworkAttributes =
             new HashMap<>(HASH_MAP_INITIAL_CAPACITY_TO_TRACK_CONNECTED_NETWORKS);
 
+    // Phone State Listeners to track all the active sub IDs
+    private HashMap<Integer, SubIdPhoneStateListener> mPhoneStateListeners;
+
     private final ConnectivityManager mConnMgr;
 
     private final Handler mHandler;
@@ -94,6 +107,9 @@
     private int mAGpsDataConnectionState;
     private InetAddress mAGpsDataConnectionIpAddr;
     private int mAGpsType;
+    private int mActiveSubId = -1;
+    private final GpsNetInitiatedHandler mNiHandler;
+
 
     private final Context mContext;
 
@@ -166,18 +182,109 @@
 
     GnssNetworkConnectivityHandler(Context context,
             GnssNetworkListener gnssNetworkListener,
-            Looper looper) {
+            Looper looper,
+            GpsNetInitiatedHandler niHandler) {
         mContext = context;
         mGnssNetworkListener = gnssNetworkListener;
 
+    SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class);
+        if (subManager != null) {
+            subManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener);
+        }
+
         PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
 
         mHandler = new Handler(looper);
+        mNiHandler = niHandler;
         mConnMgr = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
         mSuplConnectivityCallback = createSuplConnectivityCallback();
     }
 
+    /**
+     * SubId Phone State Listener is used cache the last active Sub ID when a call is made,
+     * which will be used during an emergency call to set the Network Specifier to the particular
+     * sub when an emergency supl connection is requested
+     */
+    private final class SubIdPhoneStateListener extends PhoneStateListener {
+        private Integer mSubId;
+        SubIdPhoneStateListener(Integer subId) {
+            mSubId = subId;
+        }
+        @Override
+        public void onPreciseCallStateChanged(PreciseCallState state) {
+            if (state.PRECISE_CALL_STATE_ACTIVE == state.getForegroundCallState()) {
+                mActiveSubId = mSubId;
+                if (DEBUG) Log.d(TAG, "mActiveSubId: " + mActiveSubId);
+            }
+        }
+    };
+
+    /**
+     * Subscription Changed Listener is used to get all active subscriptions and create a
+     * Phone State Listener for each Sub ID that we find in the active subscription list
+     */
+    private final SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangeListener
+            = new SubscriptionManager.OnSubscriptionsChangedListener() {
+        @Override
+        public void onSubscriptionsChanged() {
+            if (mPhoneStateListeners == null) {
+                // Capacity=2 Load-Factor=1.0, as typically no more than 2 SIMs
+                mPhoneStateListeners = new HashMap<Integer, SubIdPhoneStateListener>(2,1);
+            }
+            SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class);
+            TelephonyManager telManager = mContext.getSystemService(TelephonyManager.class);
+            if (subManager != null && telManager != null) {
+                List<SubscriptionInfo> subscriptionInfoList =
+                        subManager.getActiveSubscriptionInfoList();
+                HashSet<Integer> activeSubIds = new HashSet<Integer>();
+                if (subscriptionInfoList != null) {
+                    if (DEBUG) Log.d(TAG, "Active Sub List size: " + subscriptionInfoList.size());
+                    // populate phone state listeners with all new active subs
+                    for (SubscriptionInfo subInfo : subscriptionInfoList) {
+                        activeSubIds.add(subInfo.getSubscriptionId());
+                        if (!mPhoneStateListeners.containsKey(subInfo.getSubscriptionId())) {
+                            TelephonyManager subIdTelManager =
+                                    telManager.createForSubscriptionId(subInfo.getSubscriptionId());
+                            if (subIdTelManager != null) {
+                                if (DEBUG) Log.d(TAG, "Listener sub" + subInfo.getSubscriptionId());
+                                SubIdPhoneStateListener subIdPhoneStateListener =
+                                        new SubIdPhoneStateListener(subInfo.getSubscriptionId());
+                                mPhoneStateListeners.put(subInfo.getSubscriptionId(),
+                                        subIdPhoneStateListener);
+                                subIdTelManager.listen(subIdPhoneStateListener,
+                                        PhoneStateListener.LISTEN_PRECISE_CALL_STATE);
+                            }
+                        }
+                    }
+                }
+                // clean up phone state listeners than no longer have active subs
+                Iterator<Map.Entry<Integer, SubIdPhoneStateListener> > iterator =
+                        mPhoneStateListeners.entrySet().iterator();
+                while (iterator.hasNext()) {
+                    Map.Entry<Integer, SubIdPhoneStateListener> element = iterator.next();
+                    if (!activeSubIds.contains(element.getKey())) {
+                        TelephonyManager subIdTelManager =
+                                telManager.createForSubscriptionId(element.getKey());
+                        if (subIdTelManager != null) {
+                            if (DEBUG) Log.d(TAG, "unregister listener sub " + element.getKey());
+                            subIdTelManager.listen(element.getValue(),
+                                                   PhoneStateListener.LISTEN_NONE);
+                            // removes the element from mPhoneStateListeners
+                            iterator.remove();
+                        } else {
+                            Log.e(TAG, "Telephony Manager for Sub " + element.getKey() + " null");
+                        }
+                    }
+                }
+                // clean up cached active phone call sub if it is no longer an active sub
+                if (!activeSubIds.contains(mActiveSubId)) {
+                    mActiveSubId = -1;
+                }
+            }
+        }
+    };
+
     void registerNetworkCallbacks() {
         // register for connectivity change events.
         NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
@@ -467,6 +574,12 @@
         NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
         networkRequestBuilder.addCapability(getNetworkCapability(mAGpsType));
         networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+        // During an emergency call, and when we have cached the Active Sub Id, we set the
+        // Network Specifier so that the network request goes to the correct Sub Id
+        if (mNiHandler.getInEmergency() && mActiveSubId >= 0) {
+            if (DEBUG) Log.d(TAG, "Adding Network Specifier: " + Integer.toString(mActiveSubId));
+            networkRequestBuilder.setNetworkSpecifier(Integer.toString(mActiveSubId));
+        }
         NetworkRequest networkRequest = networkRequestBuilder.build();
         mConnMgr.requestNetwork(
                 networkRequest,
@@ -598,6 +711,15 @@
         }
         TelephonyManager phone = (TelephonyManager)
                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
+        // During an emergency call with an active sub id, get the Telephony Manager specific
+        // to the active sub to get the correct value from getServiceState and getNetworkType
+        if (mNiHandler.getInEmergency() && mActiveSubId >= 0) {
+            TelephonyManager subIdTelManager =
+                    phone.createForSubscriptionId(mActiveSubId);
+            if (subIdTelManager != null) {
+                phone = subIdTelManager;
+            }
+        }
         ServiceState serviceState = phone.getServiceState();
         String projection = null;
         String selection = null;
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/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b79f75a..58a9d9c 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]);
         }
@@ -24381,7 +24487,8 @@
         } else {
             synchronized (mLock) {
                 boolean manifestWhitelisted =
-                        mPackages.get(packageName).isAllowDontAutoRevokePermmissions();
+                        mPackages.get(packageName).getAutoRevokePermissions()
+                                == ApplicationInfo.AUTO_REVOKE_DISALLOWED;
                 return manifestWhitelisted;
             }
         }
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 3cc10d1..5a1e8e2 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -230,6 +230,10 @@
             info.sharedLibraryInfos = usesLibraryInfos.isEmpty() ? null : usesLibraryInfos;
         }
 
+        info.seInfo = AndroidPackageUtils.getSeInfo(pkg, pkgSetting);
+        info.primaryCpuAbi = AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting);
+        info.secondaryCpuAbi = AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting);
+
         info.flags |= appInfoFlags(pkg, pkgSetting);
         info.privateFlags |= appInfoPrivateFlags(pkg, pkgSetting);
         return info;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 79d0c2d..04c965e 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -21,6 +21,8 @@
 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISALLOWED;
+import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISCOURAGED;
 import static android.content.pm.PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
@@ -3238,31 +3240,25 @@
 
     @Override
     public List<String> getAutoRevokeExemptionRequestedPackages(int userId) {
-        mContext.enforceCallingPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
-                "Must hold " + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY);
-
-        List<String> result = new ArrayList<>();
-        mPackageManagerInt.forEachInstalledPackage(pkg -> {
-            if (pkg.isDontAutoRevokePermmissions()) {
-                result.add(pkg.getPackageName());
-            }
-        }, userId);
-
-        return result;
+        return getPackagesWithAutoRevokePolicy(AUTO_REVOKE_DISCOURAGED, userId);
     }
 
     @Override
     public List<String> getAutoRevokeExemptionGrantedPackages(int userId) {
+        return getPackagesWithAutoRevokePolicy(AUTO_REVOKE_DISALLOWED, userId);
+    }
+
+    @NonNull
+    private List<String> getPackagesWithAutoRevokePolicy(int autoRevokePolicy, int userId) {
         mContext.enforceCallingPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
                 "Must hold " + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY);
 
         List<String> result = new ArrayList<>();
         mPackageManagerInt.forEachInstalledPackage(pkg -> {
-            if (pkg.isAllowDontAutoRevokePermmissions()) {
+            if (pkg.getAutoRevokePermissions() == autoRevokePolicy) {
                 result.add(pkg.getPackageName());
             }
         }, userId);
-
         return result;
     }
 
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/TEST_MAPPING b/services/core/java/com/android/server/power/TEST_MAPPING
index acf3f79..74958b6 100644
--- a/services/core/java/com/android/server/power/TEST_MAPPING
+++ b/services/core/java/com/android/server/power/TEST_MAPPING
@@ -3,6 +3,7 @@
     {
       "name": "CtsBatterySavingTestCases",
       "options": [
+        {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
         {"exclude-annotation": "androidx.test.filters.LargeTest"},
         {"exclude-annotation": "androidx.test.filters.FlakyTest"}
       ]
@@ -11,6 +12,7 @@
       "name": "FrameworksMockingServicesTests",
       "options": [
         {"include-filter": "com.android.server.power"},
+        {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
         {"exclude-annotation": "androidx.test.filters.FlakyTest"}
       ]
     },
@@ -18,6 +20,7 @@
       "name": "FrameworksServicesTests",
       "options": [
         {"include-filter": "com.android.server.power"},
+        {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
         {"exclude-annotation": "androidx.test.filters.FlakyTest"},
         {
           "exclude-filter": "com.android.server.power.PowerManagerServiceTest#testWakefulnessAwake_ShouldWakeUpWhenPluggedIn"
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/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 6d53786..3477d82 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -4618,6 +4618,9 @@
             } catch (Exception e) {
                 Slog.w(TAG, "Exception thrown sending start: " + intent.getComponent(), e);
             }
+            // The activity may be waiting for stop, but that is no longer appropriate if we are
+            // starting the activity again
+            mStackSupervisor.mStoppingActivities.remove(this);
         }
         return false;
     }
@@ -4667,7 +4670,7 @@
      * and {@link #shouldPauseActivity(ActivityRecord)}.
      */
     private boolean shouldStartActivity() {
-        return mVisibleRequested && isState(STOPPED);
+        return mVisibleRequested && (isState(STOPPED) || isState(STOPPING));
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index e8bfe8e..fa0ad50 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;
@@ -303,9 +302,6 @@
 
     private static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 1;
 
-    // TODO(task-hierarchy): remove when tiles can be actual parents
-    TaskTile mTile = null;
-
     private final Handler mHandler;
 
     private class ActivityStackHandler extends Handler {
@@ -551,10 +547,10 @@
     }
 
     ActivityStack(ActivityTaskManagerService atmService, int id, int activityType,
-            ActivityInfo info, Intent intent) {
+            ActivityInfo info, Intent intent, boolean createdByOrganizer) {
         this(atmService, id, info, intent, null /*voiceSession*/, null /*voiceInteractor*/,
                 null /*taskDescription*/, null /*stack*/);
-
+        mCreatedByOrganizer = createdByOrganizer;
         setActivityType(activityType);
     }
 
@@ -601,20 +597,11 @@
     }
 
     @Override
-    public void resolveTileOverrideConfiguration(Configuration newParentConfig) {
-        super.resolveTileOverrideConfiguration(newParentConfig);
-        if (mTile != null) {
-            // If this is a virtual child of a tile, simulate the parent-child relationship
-            mTile.updateResolvedConfig(getResolvedOverrideConfiguration());
-        }
-    }
-
-    @Override
     public void onConfigurationChanged(Configuration newParentConfig) {
         // Calling Task#onConfigurationChanged() for leaf task since the ops in this method are
         // particularly for ActivityStack, like preventing bounds changes when inheriting certain
         // windowing mode.
-        if (!isRootTask() || this instanceof TaskTile) {
+        if (!isRootTask()) {
             super.onConfigurationChanged(newParentConfig);
             return;
         }
@@ -689,6 +676,9 @@
 
     @Override
     public void setWindowingMode(int windowingMode) {
+        // Reset the cached result of toString()
+        stringName = null;
+
         // Calling Task#setWindowingMode() for leaf task since this is the a specialization of
         // {@link #setWindowingMode(int)} for ActivityStack.
         if (!isRootTask()) {
@@ -742,7 +732,6 @@
         final int currentOverrideMode = getRequestedOverrideWindowingMode();
         final DisplayContent display = getDisplay();
         final Task topTask = getTopMostTask();
-        final ActivityStack splitScreenStack = display.getRootSplitScreenPrimaryTask();
         int windowingMode = preferredWindowingMode;
         if (preferredWindowingMode == WINDOWING_MODE_UNDEFINED
                 && isTransientWindowingMode(currentMode)) {
@@ -756,14 +745,14 @@
             windowingMode = display.validateWindowingMode(windowingMode,
                     null /* ActivityRecord */, topTask, getActivityType());
         }
-        if (splitScreenStack == this
+        if (display.getRootSplitScreenPrimaryTask() == this
                 && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
             // Resolution to split-screen secondary for the primary split-screen stack means
             // we want to leave split-screen mode.
             windowingMode = mRestoreOverrideWindowingMode;
         }
 
-        final boolean alreadyInSplitScreenMode = display.hasSplitScreenPrimaryTask();
+        final boolean alreadyInSplitScreenMode = display.isSplitScreenModeActivated();
 
         // Don't send non-resizeable notifications if the windowing mode changed was a side effect
         // of us entering split-screen mode.
@@ -831,7 +820,7 @@
                 return;
             }
 
-            if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && splitScreenStack != null) {
+            if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && alreadyInSplitScreenMode) {
                 // We already have a split-screen stack in this display, so just move the tasks over.
                 // TODO: Figure-out how to do all the stuff in
                 // AMS.setTaskWindowingModeSplitScreenPrimary
@@ -1063,7 +1052,7 @@
         final DisplayContent display = getDisplay();
 
         if (inSplitScreenSecondaryWindowingMode()) {
-            // If the stack is in split-screen seconardy mode, we need to make sure we move the
+            // If the stack is in split-screen secondary mode, we need to make sure we move the
             // primary split-screen stack forward in the case it is currently behind a fullscreen
             // stack so both halves of the split-screen appear on-top and the fullscreen stack isn't
             // cutting between them.
@@ -1085,12 +1074,13 @@
             display.moveHomeStackToFront(reason + " returnToHome");
         }
 
-        final boolean movingTask = task != null;
-        display.positionStackAtTop(this, !movingTask /* includingParents */, reason);
-        if (movingTask) {
-            // This also moves the entire hierarchy branch to top, including parents
-            positionChildAtTop(task);
+        if (isRootTask()) {
+            display.positionStackAtTop(this, false /* includingParents */, reason);
         }
+        if (task == null) {
+            task = this;
+        }
+        task.getParent().positionChildAt(POSITION_TOP, task, true /* includingParents */);
     }
 
     /**
@@ -1116,12 +1106,6 @@
         }
     }
 
-    @Override
-    boolean isFocusable() {
-        // Special check for tile which isn't really in the hierarchy
-        return mTile != null ? mTile.isFocusable() : super.isFocusable();
-    }
-
     boolean isTopActivityFocusable() {
         final ActivityRecord r = topRunningActivity();
         return r != null ? r.isFocusable()
@@ -2588,6 +2572,13 @@
         if (r == null || r.app != app) {
             return null;
         }
+        if (r.isActivityTypeHome() && mAtmService.mHomeProcess == app) {
+            // Home activities should not be force-finished as we have nothing else to go
+            // back to. AppErrors will get to it after two crashes in MIN_CRASH_INTERVAL.
+            Slog.w(TAG, "  Not force finishing home activity "
+                    + r.intent.getComponent().flattenToShortString());
+            return null;
+        }
         Slog.w(TAG, "  Force finishing activity "
                 + r.intent.getComponent().flattenToShortString());
         Task finishedTask = r.getTask();
@@ -3542,6 +3533,10 @@
 
     @Override
     void onChildPositionChanged(WindowContainer child) {
+        if (isOrganized()) {
+            mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, false /* force */);
+        }
+
         if (!mChildren.contains(child)) {
             return;
         }
@@ -3572,11 +3567,6 @@
         if (oldDisplay != null && oldDisplay.isRemoving()) {
             postReparent();
         }
-        if (mTile != null && getSurfaceControl() != null) {
-            // by now, the TaskStack should already have been reparented, so we can reparent its
-            // surface here
-            reparentSurfaceControl(getPendingTransaction(), mTile.getSurfaceControl());
-        }
     }
 
     void reparent(DisplayContent newParent, boolean onTop) {
@@ -3614,16 +3604,7 @@
 
     @Override
     void getRelativeDisplayedPosition(Point outPos) {
-        // check for tile which is "virtually" a parent.
-        if (mTile != null) {
-            final Rect dispBounds = getDisplayedBounds();
-            outPos.set(dispBounds.left, dispBounds.top);
-            final Rect parentBounds = mTile.getBounds();
-            outPos.offset(-parentBounds.left, -parentBounds.top);
-        } else {
-            super.getRelativeDisplayedPosition(outPos);
-        }
-
+        super.getRelativeDisplayedPosition(outPos);
         final int outset = getStackOutset();
         outPos.x -= outset;
         outPos.y -= outset;
@@ -3633,16 +3614,6 @@
         if (mSurfaceControl == null) {
             return;
         }
-        if (mTile != null) {
-            // Tile controls crop, so the app needs to be able to draw its background outside of
-            // the stack bounds for when the tile crop gets bigger than the stack.
-            if (mLastSurfaceSize.equals(0, 0)) {
-                return;
-            }
-            transaction.setWindowCrop(mSurfaceControl, null);
-            mLastSurfaceSize.set(0, 0);
-            return;
-        }
 
         final Rect stackBounds = getDisplayedBounds();
         int width = stackBounds.width();
@@ -3666,9 +3637,6 @@
 
     @Override
     void onDisplayChanged(DisplayContent dc) {
-        if (mTile != null && dc != mTile.getDisplay()) {
-            mTile.removeChild(this);
-        }
         super.onDisplayChanged(dc);
         if (isRootTask()) {
             updateSurfaceBounds();
@@ -3781,20 +3749,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();
     }
@@ -3825,49 +3779,6 @@
         return shouldSleepActivities() || mAtmService.mShuttingDown;
     }
 
-    TaskTile getTile() {
-        return mTile;
-    }
-
-    /**
-     * Don't call this directly. instead use {@link TaskTile#addChild} or
-     * {@link TaskTile#removeChild}.
-     */
-    void setTile(TaskTile tile) {
-        TaskTile origTile = mTile;
-        mTile = tile;
-        final ConfigurationContainer parent = getParent();
-        if (parent != null) {
-            onConfigurationChanged(parent.getConfiguration());
-        }
-
-        // Reparent to tile surface or back to original parent
-        if (getSurfaceControl() == null) {
-            return;
-        }
-        if (mTile != null) {
-            // don't use reparentSurfaceControl because we need to bypass taskorg check
-            mSurfaceAnimator.reparent(getPendingTransaction(), mTile.getSurfaceControl());
-        } else if (mTile == null && origTile != null) {
-            mSurfaceAnimator.reparent(getPendingTransaction(), getParentSurfaceControl());
-        }
-    }
-
-    @Override
-    public SurfaceControl getParentSurfaceControl() {
-        // Tile is a "virtual" parent, so we need to intercept the parent surface here
-        return mTile != null ? mTile.getSurfaceControl() : super.getParentSurfaceControl();
-    }
-
-    @Override
-    void removeImmediately() {
-        // TODO(task-hierarchy): remove this override when tiles are in hierarchy
-        if (mTile != null) {
-            mTile.removeChild(this);
-        }
-        super.removeImmediately();
-    }
-
     @Override
     public void dumpDebug(ProtoOutputStream proto, long fieldId,
             @WindowTraceLogLevel int logLevel) {
@@ -3912,7 +3823,7 @@
             proto.write(SURFACE_HEIGHT, mSurfaceControl.getHeight());
         }
 
-        proto.write(CREATED_BY_ORGANIZER, this instanceof TaskTile);
+        proto.write(CREATED_BY_ORGANIZER, mCreatedByOrganizer);
 
         proto.end(token);
     }
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 57f357d..4652f49 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -423,6 +423,10 @@
 
             final ActivityStack toStack = mToDisplay.getOrCreateStack(
                     null, mTmpOptions, task, task.getActivityType(), mOnTop);
+            if (task == toStack) {
+                // The task was reused as the root task.
+                return;
+            }
 
             if (mOnTop) {
                 final boolean isTopTask = task == mTopTask;
@@ -1704,7 +1708,7 @@
                 mRootWindowContainer.getLaunchStack(null, aOptions, task, onTop);
         final WindowContainer parent = task.getParent();
 
-        if (parent == stack) {
+        if (parent == stack || task == stack) {
             // Nothing else to do since it is already restored in the right stack.
             return true;
         }
@@ -2237,7 +2241,7 @@
         final boolean isSecondaryDisplayPreferred =
                 (preferredDisplayId != DEFAULT_DISPLAY && preferredDisplayId != INVALID_DISPLAY);
         final boolean inSplitScreenMode = actualStack != null
-                && actualStack.getDisplay().hasSplitScreenPrimaryTask();
+                && actualStack.getDisplay().isSplitScreenModeActivated();
         if (((!inSplitScreenMode && preferredWindowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
                 && !isSecondaryDisplayPreferred) || !task.isActivityTypeStandardOrUndefined()) {
             return;
@@ -2284,16 +2288,14 @@
         if (!task.supportsSplitScreenWindowingMode() || forceNonResizable) {
             // Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
             // we need to move it to top of fullscreen stack, otherwise it will be covered.
-
-            final ActivityStack dockedStack =
-                    task.getStack().getDisplay().getRootSplitScreenPrimaryTask();
-            if (dockedStack != null) {
+            final DisplayContent display = task.getStack().getDisplay();
+            if (display.isSplitScreenModeActivated()) {
                 // Display a warning toast that we tried to put an app that doesn't support
                 // split-screen in split-screen.
                 mService.getTaskChangeNotificationController()
                         .notifyActivityDismissingDockedStack();
-                dockedStack.getDisplay().onSplitScreenModeDismissed();
-                dockedStack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
+                display.onSplitScreenModeDismissed();
+                display.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
                         true /* notifyClients */);
             }
             return;
@@ -2602,7 +2604,7 @@
                         "startActivityFromRecents: Task " + taskId + " not found.");
             } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
                     && task.getWindowingMode() != windowingMode) {
-                mService.moveTaskToSplitScreenPrimaryTile(task, true /* toTop */);
+                mService.moveTaskToSplitScreenPrimaryTask(task, true /* toTop */);
             }
 
             if (windowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 14ca7cb..da1c045 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -30,7 +30,6 @@
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.WaitResult.LAUNCH_STATE_COLD;
 import static android.app.WaitResult.LAUNCH_STATE_HOT;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -78,6 +77,7 @@
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY;
 import static com.android.server.wm.Task.REPARENT_MOVE_STACK_TO_FRONT;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -1566,7 +1566,7 @@
         }
 
         if (!mAvoidMoveToFront && mDoResume) {
-            mTargetStack.moveToFront("reuseOrNewTask");
+            mTargetStack.getStack().moveToFront("reuseOrNewTask", targetTask);
             if (mOptions != null) {
                 if (mPreferredWindowingMode != WINDOWING_MODE_UNDEFINED) {
                     mTargetStack.setWindowingMode(mPreferredWindowingMode);
@@ -2364,6 +2364,7 @@
     private void setTargetStackIfNeeded(ActivityRecord intentActivity) {
         mTargetStack = intentActivity.getRootTask();
         mTargetStack.mLastPausedActivity = null;
+        Task intentTask = intentActivity.getTask();
         // If the target task is not in the front, then we need to bring it to the front...
         // except...  well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have
         // the same behavior as if a new instance was being started, which means not bringing it
@@ -2374,7 +2375,7 @@
             final ActivityRecord curTop = (focusStack == null)
                     ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
             final Task topTask = curTop != null ? curTop.getTask() : null;
-            differentTopTask = topTask != intentActivity.getTask()
+            differentTopTask = topTask != intentTask
                     || (focusStack != null && topTask != focusStack.getTopMostTask());
         } else {
             // The existing task should always be different from those in other displays.
@@ -2391,7 +2392,6 @@
                     intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask());
                 }
 
-                final Task intentTask = intentActivity.getTask();
                 final ActivityStack launchStack =
                         getLaunchStack(mStartActivity, mLaunchFlags, intentTask, mOptions);
                 if (launchStack == null || launchStack == mTargetStack) {
@@ -2400,6 +2400,14 @@
                     // new intent has delivered.
                     final boolean isSplitScreenTopStack = mTargetStack.isTopSplitScreenStack();
 
+                    // TODO(b/151572268): Figure out a better way to move tasks in above 2-levels
+                    //  tasks hierarchies.
+                    if (mTargetStack != intentTask
+                            && mTargetStack != intentTask.getParent().asTask()) {
+                        intentTask.getParent().positionChildAt(POSITION_TOP, intentTask,
+                                false /* includingParents */);
+                        intentTask = intentTask.getParent().asTask();
+                    }
                     // We only want to move to the front, if we aren't going to launch on a
                     // different stack. If we launch on a different stack, we will put the
                     // task on top there.
@@ -2420,8 +2428,8 @@
         // Need to update mTargetStack because if task was moved out of it, the original stack may
         // be destroyed.
         mTargetStack = intentActivity.getRootTask();
-        mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTask(),
-                WINDOWING_MODE_UNDEFINED, DEFAULT_DISPLAY, mTargetStack);
+        mSupervisor.handleNonResizableTaskIfNeeded(intentTask, WINDOWING_MODE_UNDEFINED,
+                DEFAULT_DISPLAY, mTargetStack);
     }
 
     private void resumeTargetStackIfNeeded() {
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..a5b0026 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -31,12 +31,12 @@
 import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -122,6 +122,7 @@
 import static com.android.server.wm.Task.LOCK_TASK_AUTH_DONT_LOCK;
 import static com.android.server.wm.Task.REPARENT_KEEP_STACK_AT_FRONT;
 import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import android.Manifest;
 import android.annotation.IntDef;
@@ -144,7 +145,6 @@
 import android.app.IAssistDataReceiver;
 import android.app.INotificationManager;
 import android.app.IRequestFinishCallback;
-import android.window.ITaskOrganizerController;
 import android.app.ITaskStackListener;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -230,9 +230,9 @@
 import android.view.IRecentsAnimationRunner;
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationDefinition;
+import android.view.WindowManager;
 import android.window.IWindowOrganizerController;
 import android.window.WindowContainerTransaction;
-import android.view.WindowManager;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
@@ -1274,9 +1274,13 @@
         a.colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
         a.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
 
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchActivityType(ACTIVITY_TYPE_DREAM);
+
         try {
             getActivityStartController().obtainStarter(intent, "dream")
                     .setActivityInfo(a)
+                    .setActivityOptions(options.toBundle())
                     .setIsDream(true)
                     .execute();
             return true;
@@ -2349,16 +2353,18 @@
                 }
 
                 final ActivityStack stack = task.getStack();
-                // Convert some windowing-mode changes into root-task reparents for split-screen.
-                if (stack.getTile() != null) {
-                    stack.getDisplay().onSplitScreenModeDismissed();
-                }
                 if (toTop) {
                     stack.moveToFront("setTaskWindowingMode", task);
                 }
-                stack.setWindowingMode(windowingMode);
-                stack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
-                        true /* notifyClients */);
+                // Convert some windowing-mode changes into root-task reparents for split-screen.
+                if (stack.inSplitScreenWindowingMode()) {
+                    stack.getDisplay().onSplitScreenModeDismissed();
+
+                } else {
+                    stack.setWindowingMode(windowingMode);
+                    stack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
+                            true /* notifyClients */);
+                }
                 return true;
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -2755,24 +2761,22 @@
         }
 
         final int prevMode = task.getWindowingMode();
-        moveTaskToSplitScreenPrimaryTile(task, toTop);
+        moveTaskToSplitScreenPrimaryTask(task, toTop);
         return prevMode != task.getWindowingMode();
     }
 
-    void moveTaskToSplitScreenPrimaryTile(Task task, boolean toTop) {
-        ActivityStack stack = task.getStack();
-        TaskTile tile = null;
-        for (int i = stack.getDisplay().getStackCount() - 1; i >= 0; --i) {
-            tile = stack.getDisplay().getStackAt(i).asTile();
-            if (tile != null && tile.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-                break;
-            }
+    void moveTaskToSplitScreenPrimaryTask(Task task, boolean toTop) {
+        final DisplayContent display = task.getDisplayContent();
+        final ActivityStack primarySplitTask = display.getRootSplitScreenPrimaryTask();
+        if (primarySplitTask == null) {
+            throw new IllegalStateException("Can't enter split without associated organized task");
         }
-        if (tile == null) {
-            throw new IllegalStateException("Can't enter split without associated tile");
+
+        if (toTop) {
+            display.positionStackAt(POSITION_TOP, primarySplitTask, false /* includingParents */);
         }
         WindowContainerTransaction wct = new WindowContainerTransaction();
-        wct.reparent(stack.mRemoteToken, tile.mRemoteToken, toTop);
+        wct.reparent(task.getStack().mRemoteToken, primarySplitTask.mRemoteToken, toTop);
         mWindowOrganizerController.applyTransaction(wct);
     }
 
@@ -3239,7 +3243,8 @@
 
                 final ActivityStack stack = r.getRootTask();
                 final Task task = stack.getDisplay().createStack(stack.getWindowingMode(),
-                        stack.getActivityType(), !ON_TOP, ainfo, intent);
+                        stack.getActivityType(), !ON_TOP, ainfo, intent,
+                        false /* createdByOrganizer */);
 
                 if (!mRecentTasks.addToBottom(task)) {
                     // The app has too many tasks already and we can't add any more
@@ -4278,19 +4283,9 @@
         try {
             synchronized (mGlobalLock) {
                 final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
-                TaskTile primary = null;
-                TaskTile secondary = null;
-                for (int i = dc.getStackCount() - 1; i >= 0; --i) {
-                    final TaskTile t = dc.getStackAt(i).asTile();
-                    if (t == null) {
-                        continue;
-                    }
-                    if (t.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-                        primary = t;
-                    } else if (t.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
-                        secondary = t;
-                    }
-                }
+                final Task primary = dc.getRootSplitScreenPrimaryTask();
+                final Task secondary = dc.getTask(t -> t.mCreatedByOrganizer && t.isRootTask()
+                        && t.inSplitScreenSecondaryWindowingMode());
                 if (primary == null || secondary == null) {
                     return;
                 }
@@ -7477,10 +7472,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/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 33dd9cf..1036af6 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -467,6 +468,10 @@
         return getActivityType() == ACTIVITY_TYPE_ASSISTANT;
     }
 
+    public boolean isActivityTypeDream() {
+        return getActivityType() == ACTIVITY_TYPE_DREAM;
+    }
+
     public boolean isActivityTypeStandard() {
         return getActivityType() == ACTIVITY_TYPE_STANDARD;
     }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 7df731b..6c5428c 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -200,7 +200,6 @@
 import android.view.Gravity;
 import android.view.IDisplayWindowInsetsController;
 import android.view.ISystemGestureExclusionListener;
-import android.window.ITaskOrganizer;
 import android.view.IWindow;
 import android.view.InputChannel;
 import android.view.InputDevice;
@@ -217,6 +216,7 @@
 import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.WindowManagerPolicyConstants.PointerEventListener;
+import android.window.WindowContainerTransaction;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
@@ -456,6 +456,8 @@
 
     private final Configuration mTmpConfiguration = new Configuration();
 
+    private ArrayList<Task> mTmpTasks = new ArrayList<>();
+
     /** Remove this display when animation on it has completed. */
     private boolean mDeferredRemoval;
 
@@ -654,8 +656,11 @@
     private final RootWindowContainer.FindTaskResult
             mTmpFindTaskResult = new RootWindowContainer.FindTaskResult();
 
-    // When non-null, new stacks get put into this tile.
-    TaskTile mLaunchTile = null;
+    // When non-null, new tasks get put into this root task.
+    Task mLaunchRootTask = null;
+
+    // Used in performing layout
+    private boolean mTmpWindowsBehindIme;
 
     private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
         WindowStateAnimator winAnimator = w.mWinAnimator;
@@ -748,6 +753,12 @@
                     + " parentHidden=" + w.isParentWindowHidden());
         }
 
+        // Sets mBehindIme for each window. Windows behind IME can get IME insets.
+        w.mBehindIme = mTmpWindowsBehindIme;
+        if (w == mInputMethodWindow) {
+            mTmpWindowsBehindIme = true;
+        }
+
         // If this view is GONE, then skip it -- keep the current frame, and let the caller know
         // so they can ignore it if they want.  (We do the normal layout for INVISIBLE windows,
         // since that means "perform layout as normal, just don't display").
@@ -2128,12 +2139,13 @@
     }
 
     /** @return The primary split-screen task, and {@code null} otherwise. */
-    ActivityStack getRootSplitScreenPrimaryTask() {
+    @Nullable ActivityStack getRootSplitScreenPrimaryTask() {
         return mTaskContainers.getRootSplitScreenPrimaryTask();
     }
 
-    boolean hasSplitScreenPrimaryTask() {
-        return getRootSplitScreenPrimaryTask() != null;
+    boolean isSplitScreenModeActivated() {
+        Task task = getRootSplitScreenPrimaryTask();
+        return task != null && task.hasChild();
     }
 
     ActivityStack getRootPinnedTask() {
@@ -2600,7 +2612,7 @@
         }
         amendWindowTapExcludeRegion(mTouchExcludeRegion);
         // TODO(multi-display): Support docked stacks on secondary displays.
-        if (mDisplayId == DEFAULT_DISPLAY && getRootSplitScreenPrimaryTask() != null) {
+        if (mDisplayId == DEFAULT_DISPLAY && isSplitScreenModeActivated()) {
             mDividerControllerLocked.getTouchRegion(mTmpRect);
             mTmpRegion.set(mTmpRect);
             mTouchExcludeRegion.op(mTmpRegion, Op.UNION);
@@ -2623,8 +2635,7 @@
         // If the task is home stack and it is resizable and visible (top of its root task), we want
         // to exclude the docked stack from touch so we need the entire screen area and not just a
         // small portion which the home stack currently is resized to.
-        if (task.isActivityTypeHome() && task.isVisible() && task.getStack().getTile() != null
-                && task.isResizeable()) {
+        if (task.isActivityTypeHome() && task.isVisible() && task.isResizeable()) {
             mDisplayContent.getBounds(mTmpRect);
         } else {
             task.getDimBounds(mTmpRect);
@@ -4014,6 +4025,9 @@
         mTmpWindow = null;
         mTmpInitial = initial;
 
+        // Used to indicate that we have processed the IME window.
+        mTmpWindowsBehindIme = false;
+
         // First perform layout of any root windows (not attached to another window).
         forAllWindows(mPerformLayout, true /* traverseTopToBottom */);
 
@@ -4328,15 +4342,8 @@
 
         @VisibleForTesting
         ActivityStack getTopStack() {
-            // TODO(task-hierarchy): Just grab index -1 once tiles are in hierarchy.
-            for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
-                final ActivityStack child = mTaskContainers.getChildAt(i);
-                if (child instanceof TaskTile) {
-                    continue;
-                }
-                return child;
-            }
-            return null;
+            final int count = mTaskContainers.getChildCount();
+            return count > 0 ? mTaskContainers.getChildAt(count - 1) : null;
         }
 
         int getIndexOf(ActivityStack stack) {
@@ -4375,10 +4382,6 @@
         }
 
         private void addStackReferenceIfNeeded(ActivityStack stack) {
-            // TODO(task-hierarchy): Remove when tiles are in hierarchy.
-            if (stack instanceof TaskTile) {
-                return;
-            }
             if (stack.isActivityTypeHome()) {
                 if (mRootHomeTask != null) {
                     if (!stack.isDescendantOf(mRootHomeTask)) {
@@ -4390,27 +4393,26 @@
                     mRootHomeTask = stack;
                 }
             }
+
+            if (!stack.isRootTask()) {
+                return;
+            }
             final int windowingMode = stack.getWindowingMode();
             if (windowingMode == WINDOWING_MODE_PINNED) {
                 if (mRootPinnedTask != null) {
-                    if (!stack.isDescendantOf(mRootPinnedTask)) {
-                        throw new IllegalArgumentException(
-                                "addStackReferenceIfNeeded: pinned stack=" + mRootPinnedTask
-                                        + " already exist on display=" + this + " stack=" + stack);
-                    }
-                } else {
-                    mRootPinnedTask = stack;
+                    throw new IllegalArgumentException(
+                            "addStackReferenceIfNeeded: pinned stack=" + mRootPinnedTask
+                                    + " already exist on display=" + this + " stack=" + stack);
                 }
+                mRootPinnedTask = stack;
             } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
                 if (mRootSplitScreenPrimaryTask != null) {
-                    if (!stack.isDescendantOf(mRootSplitScreenPrimaryTask)) {
-                        throw new IllegalArgumentException("addStackReferenceIfNeeded:"
-                                + " split-screen-primary" + " stack=" + mRootSplitScreenPrimaryTask
-                                + " already exist on display=" + this + " stack=" + stack);
-                    }
-                } else {
-                    mRootSplitScreenPrimaryTask = stack;
+                    throw new IllegalArgumentException(
+                            "addStackReferenceIfNeeded: split screen primary stack="
+                                    + mRootSplitScreenPrimaryTask
+                                    + " already exist on display=" + this + " stack=" + stack);
                 }
+                mRootSplitScreenPrimaryTask = stack;
             }
         }
 
@@ -4495,6 +4497,10 @@
          */
         private int findPositionForStack(int requestedPosition, ActivityStack stack,
                 boolean adding) {
+            if (stack.isActivityTypeDream()) {
+                return POSITION_TOP;
+            }
+
             if (stack.inPinnedWindowingMode()) {
                 return POSITION_TOP;
             }
@@ -4645,10 +4651,9 @@
                 // Apps and their containers are not allowed to specify an orientation while using
                 // root tasks...except for the home stack if it is not resizable and currently
                 // visible (top of) its root task.
-                if (mRootHomeTask != null && mRootHomeTask.isVisible()
-                        && mRootHomeTask.getTile() != null) {
+                if (mRootHomeTask != null && mRootHomeTask.isVisible()) {
                     final Task topMost = mRootHomeTask.getTopMostTask();
-                    final boolean resizable = topMost == null && topMost.isResizeable();
+                    final boolean resizable = topMost != null && topMost.isResizeable();
                     if (!(resizable && mRootHomeTask.matchParentBounds())) {
                         final int orientation = mRootHomeTask.getOrientation();
                         if (orientation != SCREEN_ORIENTATION_UNSET) {
@@ -4792,17 +4797,6 @@
                 mSplitScreenDividerAnchor = null;
             }
         }
-
-        @Override
-        void onChildPositionChanged(WindowContainer child) {
-            // TODO(task-hierarchy): Move functionality to TaskTile when it's a proper parent.
-            TaskTile tile = ((ActivityStack) child).getTile();
-            if (tile == null) {
-                return;
-            }
-            mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
-                    tile, false /* force */);
-        }
     }
 
     private class WindowContainers extends DisplayChildWindowContainer<WindowContainer> {
@@ -4983,7 +4977,7 @@
         private boolean skipImeWindowsDuringTraversal(DisplayContent dc) {
             // We skip IME windows so they're processed just above their target, except
             // in split-screen mode where we process the IME containers above the docked divider.
-            return dc.mInputMethodTarget != null && !dc.hasSplitScreenPrimaryTask();
+            return dc.mInputMethodTarget != null && !dc.isSplitScreenModeActivated();
         }
 
         /** Like {@link #forAllWindows}, but ignores {@link #skipImeWindowsDuringTraversal} */
@@ -5657,6 +5651,14 @@
         mAtmService.updateSleepIfNeededLocked();
     }
 
+    void addStackReferenceIfNeeded(ActivityStack stack) {
+        mTaskContainers.addStackReferenceIfNeeded(stack);
+    }
+
+    void removeStackReferenceIfNeeded(ActivityStack stack) {
+        mTaskContainers.removeStackReferenceIfNeeded(stack);
+    }
+
     void onStackRemoved(ActivityStack stack) {
         if (ActivityTaskManagerDebugConfig.DEBUG_STACK) {
             Slog.v(TAG_STACK, "removeStack: detaching " + stack + " from displayId=" + mDisplayId);
@@ -5703,6 +5705,14 @@
                     "positionStackAt: Can only have one task on display=" + this);
         }
 
+        final boolean movingToTop = wasContained && position >= getStackCount() - 1;
+        // Reset mPreferredTopFocusableStack before positioning to top or {@link
+        // ActivityStackSupervisor#updateTopResumedActivityIfNeeded()} won't update the top
+        // resumed activity.
+        if (movingToTop && stack.isFocusable()) {
+            mPreferredTopFocusableStack = null;
+        }
+
         // Since positionChildAt() is called during the creation process of pinned stacks,
         // ActivityStack#getStack() can be null.
         positionStackAt(position, stack, includingParents);
@@ -5712,7 +5722,7 @@
         // we are looking for top focusable stack. The condition {@code wasContained} restricts the
         // preferred stack is set only when moving an existing stack to top instead of adding a new
         // stack that may be too early (e.g. in the middle of launching or reparenting).
-        if (wasContained && position >= getStackCount() - 1 && stack.isFocusableAndVisible()) {
+        if (movingToTop && stack.isFocusableAndVisible()) {
             mPreferredTopFocusableStack = stack;
         } else if (mPreferredTopFocusableStack == stack) {
             mPreferredTopFocusableStack = null;
@@ -5755,18 +5765,54 @@
     /**
      * Returns an existing stack compatible with the windowing mode and activity type or creates one
      * if a compatible stack doesn't exist.
+     * @see #getOrCreateStack(int, int, boolean, Intent, Task, boolean)
+     */
+    ActivityStack getOrCreateStack(int windowingMode, int activityType, boolean onTop) {
+        return getOrCreateStack(windowingMode, activityType, onTop, null /* intent */,
+                null /* candidateTask */, false /* createdByOrganizer */);
+    }
+
+    /**
+     * When two level tasks are required for given windowing mode and activity type, returns an
+     * existing compatible root task or creates a new one.
+     * For one level task, the candidate task would be reused to also be the root task or create
+     * a new root task if no candidate task.
      * @see #getStack(int, int)
      * @see #createStack(int, int, boolean)
      */
-    ActivityStack getOrCreateStack(int windowingMode, int activityType,
-            boolean onTop) {
+    ActivityStack getOrCreateStack(int windowingMode, int activityType, boolean onTop,
+            Intent intent, Task candidateTask, boolean createdByOrganizer) {
         if (!alwaysCreateStack(windowingMode, activityType)) {
             ActivityStack stack = getStack(windowingMode, activityType);
             if (stack != null) {
                 return stack;
             }
+        } else if (candidateTask != null) {
+            final ActivityStack stack = (ActivityStack) candidateTask;
+            final int position = onTop ? POSITION_TOP : POSITION_BOTTOM;
+            if (isSplitScreenModeActivated()) {
+                final Task splitRootSecondary = getTask(t -> t.mCreatedByOrganizer && t.isRootTask()
+                        && t.inSplitScreenSecondaryWindowingMode());
+                if (stack.getParent() == null) {
+                    splitRootSecondary.addChild(stack, position);
+                } else if (stack.getParent() != splitRootSecondary) {
+                    stack.reparent(splitRootSecondary, position);
+                }
+            } else if (stack.getDisplay() != this || !stack.isRootTask()) {
+                if (stack.getParent() == null) {
+                    addStack(stack, position);
+                } else {
+                    stack.reparent(this, onTop);
+                }
+            }
+            // Update windowing mode if necessary, e.g. moving a pinned task to fullscreen.
+            if (candidateTask.getWindowingMode() != windowingMode) {
+                candidateTask.setWindowingMode(windowingMode);
+            }
+            return stack;
         }
-        return createStack(windowingMode, activityType, onTop);
+        return createStack(windowingMode, activityType, onTop, null /*info*/, intent,
+                createdByOrganizer);
     }
 
     /**
@@ -5784,7 +5830,8 @@
         // UNDEFINED windowing mode is a valid result and means that the new stack will inherit
         // it's display's windowing mode.
         windowingMode = validateWindowingMode(windowingMode, r, candidateTask, activityType);
-        return getOrCreateStack(windowingMode, activityType, onTop);
+        return getOrCreateStack(windowingMode, activityType, onTop, null /* intent */,
+                candidateTask, false /* createdByOrganizer */);
     }
 
     @VisibleForTesting
@@ -5793,7 +5840,8 @@
     }
 
     ActivityStack createStack(int windowingMode, int activityType, boolean onTop) {
-        return createStack(windowingMode, activityType, onTop, null /*info*/, null /*intent*/);
+        return createStack(windowingMode, activityType, onTop, null /* info */, null /* intent */,
+                false /* createdByOrganizer */);
     }
 
     /**
@@ -5805,25 +5853,29 @@
      *                     {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} then the stack will
      *                     be created in {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}.
      * @param onTop If true the stack will be created at the top of the display, else at the bottom.
+     * @param info The started activity info.
+     * @param intent The intent that started this task.
+     * @param createdByOrganizer @{code true} if this is created by task organizer, @{code false}
+     *                          otherwise.
      * @return The newly created stack.
      */
     ActivityStack createStack(int windowingMode, int activityType, boolean onTop, ActivityInfo info,
-            Intent intent) {
+            Intent intent, boolean createdByOrganizer) {
         if (mSingleTaskInstance && getStackCount() > 0) {
             // Create stack on default display instead since this display can only contain 1 stack.
             // TODO: Kinda a hack, but better that having the decision at each call point. Hoping
             // this goes away once ActivityView is no longer using virtual displays.
             return mRootWindowContainer.getDefaultDisplay().createStack(
-                    windowingMode, activityType, onTop, info, intent);
+                    windowingMode, activityType, onTop, info, intent, createdByOrganizer);
         }
 
-        if (activityType == ACTIVITY_TYPE_UNDEFINED) {
+        if (activityType == ACTIVITY_TYPE_UNDEFINED && !createdByOrganizer) {
             // Can't have an undefined stack type yet...so re-map to standard. Anyone that wants
-            // anything else should be passing it in anyways...
+            // anything else should be passing it in anyways...except for the task organizer.
             activityType = ACTIVITY_TYPE_STANDARD;
         }
 
-        if (activityType != ACTIVITY_TYPE_STANDARD) {
+        if (activityType != ACTIVITY_TYPE_STANDARD && activityType != ACTIVITY_TYPE_UNDEFINED) {
             // For now there can be only one stack of a particular non-standard activity type on a
             // display. So, get that ignoring whatever windowing mode it is currently in.
             ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
@@ -5842,39 +5894,39 @@
         }
 
         final int stackId = getNextStackId();
-        return createStackUnchecked(windowingMode, activityType, stackId, onTop, info, intent);
+        return createStackUnchecked(windowingMode, activityType, stackId, onTop, info, intent,
+                createdByOrganizer);
     }
 
-    /** @return the tile to create the next stack in. */
-    private TaskTile updateLaunchTile(int windowingMode) {
+    /** @return the root task to create the next task in. */
+    private Task updateLaunchRootTask(int windowingMode) {
         if (!isSplitScreenWindowingMode(windowingMode)) {
-            // Only split-screen windowing modes interact with tiles.
+            // Only split-screen windowing modes can do this currently...
             return null;
         }
         for (int i = getStackCount() - 1; i >= 0; --i) {
-            final TaskTile t = getStackAt(i).asTile();
-            if (t == null || t.getRequestedOverrideWindowingMode() != windowingMode) {
+            final Task t = getStackAt(i);
+            if (!t.mCreatedByOrganizer || t.getRequestedOverrideWindowingMode() != windowingMode) {
                 continue;
             }
-            // If not already set, pick a launch tile which is not the one we are launching
-            // into.
-            if (mLaunchTile == null) {
+            // If not already set, pick a launch root which is not the one we are launching into.
+            if (mLaunchRootTask == null) {
                 for (int j = 0, n = getStackCount(); j < n; ++j) {
-                    TaskTile tt = getStackAt(j).asTile();
-                    if (tt != t) {
-                        mLaunchTile = tt;
+                    final Task tt = getStackAt(j);
+                    if (tt.mCreatedByOrganizer && tt != t) {
+                        mLaunchRootTask = tt;
                         break;
                     }
                 }
             }
             return t;
         }
-        return mLaunchTile;
+        return mLaunchRootTask;
     }
 
     @VisibleForTesting
-    ActivityStack createStackUnchecked(int windowingMode, int activityType,
-            int stackId, boolean onTop, ActivityInfo info, Intent intent) {
+    ActivityStack createStackUnchecked(int windowingMode, int activityType, int stackId,
+            boolean onTop, ActivityInfo info, Intent intent, boolean createdByOrganizer) {
         if (windowingMode == WINDOWING_MODE_PINNED && activityType != ACTIVITY_TYPE_STANDARD) {
             throw new IllegalArgumentException("Stack with windowing mode cannot with non standard "
                     + "activity type.");
@@ -5884,19 +5936,25 @@
             info.applicationInfo = new ApplicationInfo();
         }
 
-        TaskTile tile = updateLaunchTile(windowingMode);
-        if (tile != null) {
-            // Since this stack will be put into a tile, its windowingMode will be inherited.
+        // Task created by organizer are added as root.
+        Task launchRootTask = createdByOrganizer ? null : updateLaunchRootTask(windowingMode);
+        if (launchRootTask != null) {
+            // Since this stack will be put into a root task, its windowingMode will be inherited.
             windowingMode = WINDOWING_MODE_UNDEFINED;
         }
+
         final ActivityStack stack = (ActivityStack) Task.create(mAtmService, stackId, activityType,
-                info, intent);
-        addStack(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
-        stack.setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
-                false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
-                true /* creating */);
-        if (tile != null) {
-            tile.addChild(stack, 0 /* index */);
+                info, intent, createdByOrganizer);
+        if (launchRootTask != null) {
+            launchRootTask.addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
+            if (onTop) {
+                positionStackAtTop((ActivityStack) launchRootTask, false /* includingParents */);
+            }
+        } else {
+            addStack(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
+            stack.setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
+                    false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
+                    true /* creating */);
         }
         return stack;
     }
@@ -6031,7 +6089,7 @@
         mTmpFindTaskResult.clear();
         for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
             final ActivityStack stack = getStackAt(stackNdx);
-            if (!r.hasCompatibleActivityType(stack)) {
+            if (!r.hasCompatibleActivityType(stack) && stack.isLeafTask()) {
                 if (DEBUG_TASKS) {
                     Slog.d(TAG_TASKS, "Skipping stack: (mismatch activity/stack) " + stack);
                 }
@@ -6103,7 +6161,15 @@
             final int activityType = activityTypes[j];
             for (int i = getStackCount() - 1; i >= 0; --i) {
                 final ActivityStack stack = getStackAt(i);
-                if (stack.getActivityType() == activityType) {
+                // Collect the root tasks that are currently being organized.
+                if (stack.isOrganized()) {
+                    for (int k = stack.getChildCount() - 1; k >= 0; --k) {
+                        final ActivityStack childStack = (ActivityStack) stack.getChildAt(k);
+                        if (childStack.getActivityType() == activityType) {
+                            stacks.add(childStack);
+                        }
+                    }
+                } else if (stack.getActivityType() == activityType) {
                     stacks.add(stack);
                 }
             }
@@ -6117,13 +6183,8 @@
     void onSplitScreenModeDismissed() {
         mAtmService.deferWindowLayout();
         try {
-            mLaunchTile = null;
-            for (int i = getStackCount() - 1; i >= 0; --i) {
-                final TaskTile t = getStackAt(i).asTile();
-                if (t != null) {
-                    t.removeAllChildren();
-                }
-            }
+            mLaunchRootTask = null;
+            moveSplitScreenTasksToFullScreen();
         } finally {
             final ActivityStack topFullscreenStack =
                     getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -6141,6 +6202,24 @@
         }
     }
 
+    private void moveSplitScreenTasksToFullScreen() {
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        mTmpTasks.clear();
+        forAllTasks(task -> {
+            if (task.mCreatedByOrganizer && task.inSplitScreenWindowingMode() && task.hasChild()) {
+                mTmpTasks.add(task);
+            }
+        });
+
+        for (int i = mTmpTasks.size() - 1; i >= 0; i--) {
+            final Task root = mTmpTasks.get(i);
+            for (int j = 0; j < root.getChildCount(); j++) {
+                wct.reparent(root.getChildAt(j).mRemoteToken, null, true /* toTop */);
+            }
+        }
+        mAtmService.mWindowOrganizerController.applyTransaction(wct);
+    }
+
     /**
      * Returns true if the {@param windowingMode} is supported based on other parameters passed in.
      * @param windowingMode The windowing mode we are checking support for.
@@ -6253,7 +6332,7 @@
             }
         }
 
-        final boolean inSplitScreenMode = hasSplitScreenPrimaryTask();
+        final boolean inSplitScreenMode = isSplitScreenModeActivated();
         if (!inSplitScreenMode
                 && windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY) {
             // Switch to the display's windowing mode if we are not in split-screen mode and we are
@@ -6278,10 +6357,6 @@
     }
 
     boolean isTopNotPinnedStack(ActivityStack stack) {
-        // TODO(task-hierarchy): Remove when tiles are in hierarchy.
-        if (stack instanceof TaskTile) {
-            return false;
-        }
         for (int i = getStackCount() - 1; i >= 0; --i) {
             final ActivityStack current = getStackAt(i);
             if (!current.inPinnedWindowingMode()) {
@@ -6503,7 +6578,7 @@
                     // If default display is in split-window mode, set windowing mode of the stack
                     // to split-screen secondary. Otherwise, set the windowing mode to undefined by
                     // default to let stack inherited the windowing mode from the new display.
-                    final int windowingMode = toDisplay.hasSplitScreenPrimaryTask()
+                    final int windowingMode = toDisplay.isSplitScreenModeActivated()
                             ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
                             : WINDOWING_MODE_UNDEFINED;
                     stack.reparent(toDisplay, true /* onTop */);
@@ -6607,27 +6682,35 @@
      *         already top-most.
      */
     ActivityStack getStackAbove(ActivityStack stack) {
-        final int stackIndex = getIndexOf(stack) + 1;
-        return (stackIndex < getStackCount()) ? getStackAt(stackIndex) : null;
+        final WindowContainer wc = stack.getParent();
+        final int index = wc.mChildren.indexOf(stack) + 1;
+        return (index < wc.mChildren.size()) ? (ActivityStack) wc.mChildren.get(index) : null;
     }
 
     /**
      * Adjusts the {@param stack} behind the last visible stack in the display if necessary.
      * Generally used in conjunction with {@link #moveStackBehindStack}.
      */
+    // TODO(b/151575894): Remove special stack movement methods.
     void moveStackBehindBottomMostVisibleStack(ActivityStack stack) {
         if (stack.shouldBeVisible(null)) {
             // Skip if the stack is already visible
             return;
         }
 
-        // Move the stack to the bottom to not affect the following visibility checks
-        positionStackAtBottom(stack);
+        final boolean isRootTask = stack.isRootTask();
+        if (isRootTask) {
+            // Move the stack to the bottom to not affect the following visibility checks
+            positionStackAtBottom(stack);
+        } else {
+            stack.getParent().positionChildAt(POSITION_BOTTOM, stack, false /* includingParents */);
+        }
 
         // Find the next position where the stack should be placed
-        final int numStacks = getStackCount();
+        final int numStacks = isRootTask ? getStackCount() : stack.getParent().getChildCount();
         for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
-            final ActivityStack s = getStackAt(stackNdx);
+            final ActivityStack s = isRootTask ? getStackAt(stackNdx)
+                    : (ActivityStack) stack.getParent().getChildAt(stackNdx);
             if (s == stack) {
                 continue;
             }
@@ -6636,7 +6719,12 @@
                     || winMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
             if (s.shouldBeVisible(null) && isValidWindowingMode) {
                 // Move the provided stack to behind this stack
-                positionStackAt(stack, Math.max(0, stackNdx - 1));
+                final int position = Math.max(0, stackNdx - 1);
+                if (isRootTask) {
+                    positionStackAt(stack, position);
+                } else {
+                    stack.getParent().positionChildAt(position, stack, false /*includingParents */);
+                }
                 break;
             }
         }
@@ -6652,15 +6740,25 @@
             return;
         }
 
+        final WindowContainer parent = stack.getParent();
+        if (parent == null || parent != behindStack.getParent()) {
+            return;
+        }
+
         // Note that positionChildAt will first remove the given stack before inserting into the
         // list, so we need to adjust the insertion index to account for the removed index
         // TODO: Remove this logic when WindowContainer.positionChildAt() is updated to adjust the
         //       position internally
-        final int stackIndex = getIndexOf(stack);
-        final int behindStackIndex = getIndexOf(behindStack);
+        final int stackIndex = parent.mChildren.indexOf(stack);
+        final int behindStackIndex = parent.mChildren.indexOf(behindStack);
         final int insertIndex = stackIndex <= behindStackIndex
                 ? behindStackIndex - 1 : behindStackIndex;
-        positionStackAt(stack, Math.max(0, insertIndex));
+        final int position = Math.max(0, insertIndex);
+        if (stack.isRootTask()) {
+            positionStackAt(stack, position);
+        } else {
+            parent.positionChildAt(position, stack, false /* includingParents */);
+        }
     }
 
     void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
@@ -6697,19 +6795,6 @@
         return getHomeActivityForUser(mRootWindowContainer.mCurrentUser);
     }
 
-    // TODO(task-hierarchy): Remove when tiles are in hierarchy.
-    void addTile(TaskTile tile) {
-        mTaskContainers.addChild(tile, POSITION_BOTTOM);
-        ITaskOrganizer organizer = mAtmService.mTaskOrganizerController.getTaskOrganizer(
-                tile.getWindowingMode());
-        tile.setTaskOrganizer(organizer);
-    }
-
-    // TODO(task-hierarchy): Remove when tiles are in hierarchy.
-    void removeTile(TaskTile tile) {
-        mTaskContainers.removeChild(tile);
-    }
-
     @Nullable
     ActivityRecord getHomeActivityForUser(int userId) {
         final ActivityStack homeStack = getRootHomeTask();
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 34acabe..f593393 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -29,7 +29,6 @@
 import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
 import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
 import static android.view.InsetsState.ITYPE_CAPTION_BAR;
-import static android.view.InsetsState.ITYPE_IME;
 import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT;
 import static android.view.InsetsState.ITYPE_LEFT_GESTURES;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
@@ -115,7 +114,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 +176,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 +330,6 @@
                 }
             };
 
-    @GuardedBy("mHandler")
-    private SleepToken mDreamingSleepToken;
-
     // The windows we were told about in focusChanged.
     private WindowState mFocusedWindow;
     private WindowState mLastFocusedWindow;
@@ -394,7 +388,6 @@
     private boolean mShowingDream;
     private boolean mLastShowingDream;
     private boolean mDreamingLockscreen;
-    private boolean mDreamingSleepTokenNeeded;
     private boolean mAllowLockscreenWhenOn;
 
     private InputConsumer mInputConsumer = null;
@@ -414,7 +407,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 +426,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)
@@ -1503,14 +1492,8 @@
      */
     public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {
         displayFrames.onBeginLayout();
-        final InsetsState insetsState =
-                mDisplayContent.getInsetsStateController().getRawInsetsState();
-
-        // Reset the frame of IME so that the layout of windows above IME won't get influenced.
-        // Once we layout the IME, frames will be set again on the source.
-        insetsState.getSource(ITYPE_IME).setFrame(0, 0, 0, 0);
-
-        updateInsetsStateForDisplayCutout(displayFrames, insetsState);
+        updateInsetsStateForDisplayCutout(displayFrames,
+                mDisplayContent.getInsetsStateController().getRawInsetsState());
         mSystemGestures.screenWidth = displayFrames.mUnrestricted.width();
         mSystemGestures.screenHeight = displayFrames.mUnrestricted.height();
 
@@ -1537,25 +1520,7 @@
                 && (mNotificationShade.getAttrs().privateFlags
                 & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;
 
-        // When the navigation bar isn't visible, we put up a fake input window to catch all
-        // touch events. This way we can detect when the user presses anywhere to bring back the
-        // nav bar and ensure the application doesn't see the event.
-        if (navVisible || navAllowedHidden) {
-            if (mInputConsumer != null) {
-                mInputConsumer.dismiss();
-                mHandler.sendMessage(
-                        mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer));
-                mInputConsumer = null;
-            }
-        } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) {
-            mInputConsumer = mDisplayContent.getInputMonitor().createInputConsumer(
-                    mHandler.getLooper(),
-                    INPUT_CONSUMER_NAVIGATION,
-                    HideNavInputEventReceiver::new);
-            // As long as mInputConsumer is active, hover events are not dispatched to the app
-            // and the pointer icon is likely to become stale. Hide it to avoid confusion.
-            InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL);
-        }
+        updateHideNavInputEventReceiver(navVisible, navAllowedHidden);
 
         // For purposes of positioning and showing the nav bar, if we have decided that it can't
         // be hidden (because of the screen aspect ratio), then take that into account.
@@ -1577,6 +1542,28 @@
         mLastNotificationShadeForcesShowingNavigation = notificationShadeForcesShowingNavigation;
     }
 
+    void updateHideNavInputEventReceiver(boolean navVisible, boolean navAllowedHidden) {
+        // When the navigation bar isn't visible, we put up a fake input window to catch all
+        // touch events. This way we can detect when the user presses anywhere to bring back the
+        // nav bar and ensure the application doesn't see the event.
+        if (navVisible || navAllowedHidden) {
+            if (mInputConsumer != null) {
+                mInputConsumer.dismiss();
+                mHandler.sendMessage(
+                        mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer));
+                mInputConsumer = null;
+            }
+        } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) {
+            mInputConsumer = mDisplayContent.getInputMonitor().createInputConsumer(
+                    mHandler.getLooper(),
+                    INPUT_CONSUMER_NAVIGATION,
+                    HideNavInputEventReceiver::new);
+            // As long as mInputConsumer is active, hover events are not dispatched to the app
+            // and the pointer icon is likely to become stale. Hide it to avoid confusion.
+            InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL);
+        }
+    }
+
     private static void updateInsetsStateForDisplayCutout(DisplayFrames displayFrames,
             InsetsState state) {
         if (displayFrames.mDisplayCutout.getDisplayCutout().isEmpty()) {
@@ -2638,15 +2625,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 +3138,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 +3817,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/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index bb02789..fda70d1 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -25,6 +25,7 @@
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
 import static android.view.SyncRtSurfaceTransactionApplier.applyParams;
+import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
 
@@ -91,6 +92,13 @@
                 || focusedWin != getNavControlTarget(focusedWin)
                 || focusedWin.getRequestedInsetsState().getSource(ITYPE_NAVIGATION_BAR)
                         .isVisible());
+        updateHideNavInputEventReceiver();
+    }
+
+    private void updateHideNavInputEventReceiver() {
+        mPolicy.updateHideNavInputEventReceiver(!isHidden(ITYPE_NAVIGATION_BAR),
+                mFocusedWin != null
+                        && mFocusedWin.mAttrs.insetsFlags.behavior != BEHAVIOR_SHOW_BARS_BY_TOUCH);
     }
 
     boolean isHidden(@InternalInsetsType int type) {
@@ -169,6 +177,7 @@
         if (windowState == getNavControlTarget(mFocusedWin)) {
             mNavBar.setVisible(state.getSource(ITYPE_NAVIGATION_BAR).isVisible());
         }
+        updateHideNavInputEventReceiver();
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index a4bdfb3..04454a5 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -88,8 +88,8 @@
         final InsetsSourceProvider provider = target.getControllableInsetProvider();
         final @InternalInsetsType int type = provider != null
                 ? provider.getSource().getType() : ITYPE_INVALID;
-        return getInsetsForTypeAndWindowingMode(type, target.getWindowingMode(),
-                target.isAlwaysOnTop());
+        return getInsetsForDispatchInner(type, target.getWindowingMode(), target.isAlwaysOnTop(),
+                isAboveIme(target));
     }
 
     InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
@@ -97,13 +97,24 @@
         final WindowToken token = mDisplayContent.getWindowToken(attrs.token);
         final @WindowingMode int windowingMode = token != null
                 ? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
-        final boolean alwaysOnTop = token != null
-                ? token.isAlwaysOnTop() : false;
-        return getInsetsForTypeAndWindowingMode(type, windowingMode, alwaysOnTop);
+        final boolean alwaysOnTop = token != null && token.isAlwaysOnTop();
+        return getInsetsForDispatchInner(type, windowingMode, alwaysOnTop, isAboveIme(token));
+    }
+
+    private boolean isAboveIme(WindowContainer target) {
+        final WindowState imeWindow = mDisplayContent.mInputMethodWindow;
+        if (target == null || imeWindow == null) {
+            return false;
+        }
+        if (target instanceof WindowState) {
+            final WindowState win = (WindowState) target;
+            return win.needsRelativeLayeringToIme() || !win.mBehindIme;
+        }
+        return false;
     }
 
     private static @InternalInsetsType int getInsetsTypeForWindowType(int type) {
-        switch(type) {
+        switch (type) {
             case TYPE_STATUS_BAR:
                 return ITYPE_STATUS_BAR;
             case TYPE_NAVIGATION_BAR:
@@ -116,8 +127,8 @@
     }
 
     /** @see #getInsetsForDispatch */
-    private InsetsState getInsetsForTypeAndWindowingMode(@InternalInsetsType int type,
-            @WindowingMode int windowingMode, boolean isAlwaysOnTop) {
+    private InsetsState getInsetsForDispatchInner(@InternalInsetsType int type,
+            @WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme) {
         InsetsState state = mState;
 
         if (type != ITYPE_INVALID) {
@@ -158,6 +169,11 @@
             state.removeSource(ITYPE_NAVIGATION_BAR);
         }
 
+        if (aboveIme) {
+            state = new InsetsState(state);
+            state.removeSource(ITYPE_IME);
+        }
+
         return state;
     }
 
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 44034ed..6b39fd2 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -406,12 +406,11 @@
             // show on top of the lock screen. In this can we want to dismiss the docked
             // stack since it will be complicated/risky to try to put the activity on top
             // of the lock screen in the right fullscreen configuration.
-            final ActivityStack stack =
-                    mRootWindowContainer.getDefaultDisplay().getRootSplitScreenPrimaryTask();
-            if (stack == null) {
+            final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
+            if (!display.isSplitScreenModeActivated()) {
                 return;
             }
-            mRootWindowContainer.getDefaultDisplay().onSplitScreenModeDismissed();
+            display.onSplitScreenModeDismissed();
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index bd5666d..244ba82 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -21,6 +21,7 @@
 import static android.app.ActivityManager.RECENT_WITH_EXCLUDED;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
@@ -1298,6 +1299,7 @@
         switch (task.getActivityType()) {
             case ACTIVITY_TYPE_HOME:
             case ACTIVITY_TYPE_RECENTS:
+            case ACTIVITY_TYPE_DREAM:
                 // Ignore certain activity types completely
                 return false;
             case ACTIVITY_TYPE_ASSISTANT:
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index adafdec..9089240 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -388,11 +388,12 @@
                     // surfaces needs to be done immediately.
                     mWindowManager.executeAppTransition();
 
-                    if (targetStack.getTile() != null) {
+                    final Task rootTask = targetStack.getRootTask();
+                    if (rootTask.isOrganized()) {
                         // Client state may have changed during the recents animation, so force
                         // send task info so the client can synchronize its state.
                         mService.mTaskOrganizerController.dispatchTaskInfoChanged(
-                                targetStack.mTile, true /* force */);
+                                rootTask, true /* force */);
                     }
                 } catch (Exception e) {
                     Slog.e(TAG, "Failed to clean up recents activity", e);
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/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ebf1bc9..b2920ee 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -1970,8 +1971,7 @@
         final int focusStackId = topFocusedStack != null
                 ? topFocusedStack.getRootTaskId() : INVALID_TASK_ID;
         // We dismiss the docked stack whenever we switch users.
-        final ActivityStack dockedStack = getDefaultDisplay().getRootSplitScreenPrimaryTask();
-        if (dockedStack != null) {
+        if (getDefaultDisplay().isSplitScreenModeActivated()) {
             getDefaultDisplay().onSplitScreenModeDismissed();
         }
         // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
@@ -2110,20 +2110,18 @@
             final ActivityStack stack;
             if (singleActivity) {
                 stack = r.getRootTask();
+                stack.setWindowingMode(WINDOWING_MODE_PINNED);
             } else {
-                // In the case of multiple activities, we will create a new stack for it and then
-                // move the PIP activity into the stack.
-                // We will then perform a windowing mode change for both scenarios.
-                stack = display.createStack(
-                        r.getRootTask().getRequestedOverrideWindowingMode(),
-                        r.getActivityType(), ON_TOP, r.info, r.intent);
+                // In the case of multiple activities, we will create a new task for it and then
+                // move the PIP activity into the task.
+                stack = display.createStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP,
+                        r.info, r.intent, false /* createdByOrganizer */);
+
                 // There are multiple activities in the task and moving the top activity should
                 // reveal/leave the other activities in their original task.
                 r.reparent(stack, MAX_VALUE, "moveActivityToStack");
             }
 
-            stack.setWindowingMode(WINDOWING_MODE_PINNED);
-
             // Reset the state that indicates it can enter PiP while pausing after we've moved it
             // to the pinned stack
             r.supportsEnterPipOnTaskSwitch = false;
@@ -2799,16 +2797,19 @@
         if (stack == null && r != null) {
             stack = r.getRootTask();
         }
+        int windowingMode = launchParams != null ? launchParams.mWindowingMode
+                : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
         if (stack != null) {
             display = stack.getDisplay();
             if (display != null && canLaunchOnDisplay(r, display.mDisplayId)) {
-                int windowingMode = launchParams != null ? launchParams.mWindowingMode
-                        : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
                 if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
                     windowingMode = display.resolveWindowingMode(r, options, candidateTask,
                             activityType);
                 }
-                if (stack.isCompatible(windowingMode, activityType)) {
+                // Always allow organized tasks that created by organizer since the activity type
+                // of an organized task is decided by the activity type of its top child, which
+                // could be incompatible with the given windowing mode and activity type.
+                if (stack.isCompatible(windowingMode, activityType) || stack.mCreatedByOrganizer) {
                     return stack;
                 }
                 if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY
@@ -2826,6 +2827,10 @@
 
         if (display == null || !canLaunchOnDisplay(r, display.mDisplayId)) {
             display = getDefaultDisplay();
+            if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
+                windowingMode = display.resolveWindowingMode(r, options, candidateTask,
+                        activityType);
+            }
         }
 
         return display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
@@ -2915,11 +2920,10 @@
             case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome();
             case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents();
             case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant();
+            case ACTIVITY_TYPE_DREAM: return r.isActivityTypeDream();
         }
-        // TODO(task-hierarchy): Find another way to differentiate tile from normal stack once it is
-        //                       part of the hierarchy
-        if (stack instanceof TaskTile) {
-            // Don't launch directly into tiles.
+        if (stack.mCreatedByOrganizer) {
+            // Don't launch directly into task created by organizer...but why can't we?
             return false;
         }
         // There is a 1-to-1 relationship between stack and task when not in
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 55dc694..f19c106 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;
@@ -114,7 +117,6 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Debug;
 import android.os.IBinder;
@@ -129,11 +131,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;
@@ -489,6 +491,17 @@
     PictureInPictureParams mPictureInPictureParams = new PictureInPictureParams.Builder().build();
 
     /**
+     * This task was created by the task organizer which has the following implementations.
+     * <ul>
+     *     <lis>The task won't be removed when it is empty. Removal has to be an explicit request
+     *     from the task organizer.</li>
+     *     <li>Unlike other non-root tasks, it's direct children are visible to the task
+     *     organizer for ordering purposes.</li>
+     * </ul>
+     */
+    boolean mCreatedByOrganizer;
+
+    /**
      * Don't use constructor directly. Use {@link #create(ActivityTaskManagerService, int,
      * ActivityInfo, Intent, TaskDescription)} instead.
      */
@@ -578,6 +591,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;
     }
 
@@ -586,11 +607,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());
         }
@@ -1349,7 +1366,7 @@
         if (applicationType != ACTIVITY_TYPE_UNDEFINED || !hasChild()) {
             return applicationType;
         }
-        return getChildAt(0).getActivityType();
+        return getTopChild().getActivityType();
     }
 
     @Override
@@ -1362,6 +1379,12 @@
 
         ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addChild: %s at top.", this);
 
+        // A rootable task that is now being added to be the child of an organized task. Making
+        // sure the stack references is keep updated.
+        if (mTaskOrganizer != null && mCreatedByOrganizer && child.asTask() != null) {
+            mDisplayContent.addStackReferenceIfNeeded((ActivityStack) child);
+        }
+
         // Make sure the list of display UID whitelists is updated
         // now that this record is in a new task.
         mRootWindowContainer.updateUIDsPresentOnDisplay();
@@ -1402,6 +1425,11 @@
 
     @Override
     void removeChild(WindowContainer child) {
+        // A rootable child task that is now being removed from an organized task. Making sure
+        // the stack references is keep updated.
+        if (mTaskOrganizer != null && mCreatedByOrganizer && child.asTask() != null) {
+            mDisplayContent.removeStackReferenceIfNeeded((ActivityStack) child);
+        }
         removeChild(child, "removeChild");
     }
 
@@ -1449,8 +1477,9 @@
                 mStackSupervisor.removeTask(this, false /* killProcess */,
                         !REMOVE_FROM_RECENTS, reason);
             }
-        } else if (!mReuseTask) {
+        } else if (!mReuseTask && !mCreatedByOrganizer) {
             // Remove entire task if it doesn't have any activity left and it isn't marked for reuse
+            // or created by task organizer.
             if (!isRootTask) {
                 getStack().removeChild(this, reason);
             }
@@ -1862,7 +1891,12 @@
         final Task parentTask = getParent().asTask();
         if (parentTask != null) {
             parentTask.onActivityStateChanged(record, state, reason);
-            return;
+            // We still want to update the resumed activity if the parent task is created by
+            // organizer in order to keep the information synced once got reparented out from the
+            // organized task.
+            if (!parentTask.mCreatedByOrganizer) {
+                return;
+            }
         }
 
         if (record == mResumedActivity && state != RESUMED) {
@@ -2296,18 +2330,30 @@
         return Configuration.reduceScreenLayout(sourceScreenLayout, longSize, shortSize);
     }
 
-    void resolveTileOverrideConfiguration(Configuration newParentConfig) {
+    private void resolveOrganizedOverrideConfiguration(Configuration newParentConfig) {
         super.resolveOverrideConfiguration(newParentConfig);
+        if (!isOrganized()) {
+            return;
+        }
+
+        final Task root = getRootTask();
+        if (root == this) {
+            return;
+        }
+
+        // Ensure to have the same windowing mode for the child tasks that controlled by task org.
+        getResolvedOverrideConfiguration().windowConfiguration
+                .setWindowingMode(root.getWindowingMode());
     }
 
     @Override
     void resolveOverrideConfiguration(Configuration newParentConfig) {
-        if (!isLeafTask()) {
-            resolveTileOverrideConfiguration(newParentConfig);
+        if (!isLeafTask() || mCreatedByOrganizer) {
+            resolveOrganizedOverrideConfiguration(newParentConfig);
             return;
         }
         mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
-        resolveTileOverrideConfiguration(newParentConfig);
+        resolveOrganizedOverrideConfiguration(newParentConfig);
         int windowingMode =
                 getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
         if (windowingMode == WINDOWING_MODE_UNDEFINED) {
@@ -2396,7 +2442,9 @@
     }
 
     Rect updateOverrideConfigurationFromLaunchBounds() {
-        final Rect bounds = getLaunchBounds();
+        // If the task is controlled by another organized task, do not set override
+        // configurations and let its parent (organized task) to control it;
+        final Rect bounds = isOrganized() && !isRootTask() ? null : getLaunchBounds();
         setBounds(bounds);
         if (bounds != null && !bounds.isEmpty()) {
             // TODO: Review if we actually want to do this - we are setting the launch bounds
@@ -2580,7 +2628,7 @@
         // preserve POSITION_BOTTOM/POSITION_TOP positions if they are still valid.
         if (suggestedPosition == POSITION_BOTTOM && minPosition == 0) {
             return POSITION_BOTTOM;
-        } else if (suggestedPosition == POSITION_TOP && maxPosition == (size - 1)) {
+        } else if (suggestedPosition == POSITION_TOP && maxPosition >= (size - 1)) {
             return POSITION_TOP;
         }
         // Reset position based on minimum/maximum possible positions.
@@ -3199,6 +3247,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();
     }
@@ -3364,14 +3426,12 @@
         info.supportsSplitScreenMultiWindow = supportsSplitScreenWindowingMode();
         info.configuration.setTo(getConfiguration());
         info.token = mRemoteToken;
-        // Get's the first non-undefined activity type among this and children. Can't use
-        // configuration.windowConfiguration because that would only be this level.
-        info.topActivityType = getActivityType();
 
         //TODO (AM refactor): Just use local once updateEffectiveIntent is run during all child
         //                    order changes.
         final Task top = getTopMostTask();
         info.resizeMode = top != null ? top.mResizeMode : mResizeMode;
+        info.topActivityType = top.getActivityType();
 
         if (mPictureInPictureParams.empty()) {
             info.pictureInPictureParams = null;
@@ -3402,10 +3462,6 @@
         return this;
     }
 
-    TaskTile asTile() {
-        return null;
-    }
-
     // TODO(task-merge): Figure-out how this should work with hierarchy tasks.
     boolean shouldBeVisible(ActivityRecord starting) {
         return true;
@@ -3701,8 +3757,9 @@
     }
 
     static Task create(ActivityTaskManagerService service, int taskId, int activityType,
-            ActivityInfo info, Intent intent) {
-        return getTaskFactory().create(service, taskId, activityType, info, intent);
+            ActivityInfo info, Intent intent, boolean createdByOrganizer) {
+        return getTaskFactory().create(service, taskId, activityType, info, intent,
+                createdByOrganizer);
     }
 
     static Task create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
@@ -3724,8 +3781,9 @@
      */
     static class TaskFactory {
         Task create(ActivityTaskManagerService service, int taskId, int activityType,
-                ActivityInfo info, Intent intent) {
-            return new ActivityStack(service, taskId, activityType, info, intent);
+                ActivityInfo info, Intent intent, boolean createdByOrganizer) {
+            return new ActivityStack(service, taskId, activityType, info, intent,
+                    createdByOrganizer);
         }
 
         Task create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
@@ -4001,21 +4059,29 @@
     @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.
-        if (((ActivityStack) rootTask).getTile() != null) {
+        if (rootTask.mTaskOrganizer == null) {
+            // You are obviously not organized...
             return false;
         }
-        return rootTask == this && rootTask.mTaskOrganizer != null;
+        if (rootTask == this) {
+            // Root tasks can be organized.
+            return true;
+        }
+        if (rootTask.mCreatedByOrganizer && getParent() == rootTask) {
+            // Direct children of tasks added by the organizer can the organized.
+            return true;
+        }
+
+        return false;
     }
 
     @Override
     protected void reparentSurfaceControl(SurfaceControl.Transaction t, SurfaceControl newParent) {
         /**
-         * Avoid yanking back control from the TaskOrganizer, which has presumably reparented the
-         * Surface in to its own hierarchy.
+         * Avoid reparenting SurfaceControl of the organized tasks that are always on top, since
+         * the surfaces should be controlled by the organizer itself, like bubbles.
          */
-        if (isOrganized()) {
+        if (isOrganized() && isAlwaysOnTop()) {
             return;
         }
         super.reparentSurfaceControl(t, newParent);
@@ -4050,6 +4116,9 @@
         mTaskOrganizer = null;
         mLastTaskOrganizerWindowingMode = -1;
         onTaskOrganizerChanged();
+        if (mCreatedByOrganizer) {
+            removeImmediately();
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 5523678..05b721b 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
@@ -27,13 +28,14 @@
 
 import android.annotation.Nullable;
 import android.app.ActivityManager.RunningTaskInfo;
+import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
-import android.window.ITaskOrganizerController;
 import android.window.ITaskOrganizer;
+import android.window.ITaskOrganizerController;
 import android.window.IWindowContainer;
 
 import com.android.internal.util.ArrayUtils;
@@ -255,11 +257,12 @@
                 if (display == null) {
                     return null;
                 }
-                final int nextId = display.getNextStackId();
-                TaskTile tile = new TaskTile(mService, nextId, windowingMode);
-                display.addTile(tile);
-                RunningTaskInfo out = tile.getTaskInfo();
-                mLastSentTaskInfos.put(tile, out);
+
+                final Task task = display.getOrCreateStack(windowingMode, ACTIVITY_TYPE_UNDEFINED,
+                        false /* onTop */, new Intent(), null /* candidateTask */,
+                        true /* createdByOrganizer */);
+                RunningTaskInfo out = task.getTaskInfo();
+                mLastSentTaskInfos.put(task, out);
                 return out;
             }
         } finally {
@@ -273,11 +276,13 @@
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                TaskTile tile = TaskTile.forToken(token.asBinder());
-                if (tile == null) {
-                    return false;
+                final Task task = WindowContainer.fromBinder(token.asBinder()).asTask();
+                if (task == null) return false;
+                if (!task.mCreatedByOrganizer) {
+                    throw new IllegalArgumentException(
+                            "Attempt to delete task not created by organizer task=" + task);
                 }
-                tile.removeImmediately();
+                task.removeImmediately();
                 return true;
             }
         } finally {
@@ -358,12 +363,7 @@
                 if (task == null) {
                     return null;
                 }
-                ActivityStack rootTask = (ActivityStack) task.getRootTask();
-                final TaskTile tile = rootTask.getTile();
-                if (tile != null) {
-                    rootTask = tile;
-                }
-                return rootTask.mRemoteToken;
+                return task.getRootTask().mRemoteToken;
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -371,7 +371,7 @@
     }
 
     @Override
-    public void setLaunchRoot(int displayId, @Nullable IWindowContainer tile) {
+    public void setLaunchRoot(int displayId, @Nullable IWindowContainer token) {
         enforceStackPermission("setLaunchRoot()");
         final long origId = Binder.clearCallingIdentity();
         try {
@@ -380,16 +380,21 @@
                 if (display == null) {
                     return;
                 }
-                TaskTile taskTile = tile == null ? null : TaskTile.forToken(tile.asBinder());
-                if (taskTile == null) {
-                    display.mLaunchTile = null;
+                Task task = token == null
+                        ? null : WindowContainer.fromBinder(token.asBinder()).asTask();
+                if (task == null) {
+                    display.mLaunchRootTask = null;
                     return;
                 }
-                if (taskTile.getDisplay() != display) {
-                    throw new RuntimeException("Can't set launch root for display " + displayId
-                            + " to task on display " + taskTile.getDisplay().getDisplayId());
+                if (!task.mCreatedByOrganizer) {
+                    throw new IllegalArgumentException("Attempt to set task not created by "
+                            + "organizer as launch root task=" + task);
                 }
-                display.mLaunchTile = taskTile;
+                if (task.getDisplayContent() != display) {
+                    throw new RuntimeException("Can't set launch root for display " + displayId
+                            + " to task on display " + task.getDisplayContent().getDisplayId());
+                }
+                display.mLaunchRootTask = task;
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -411,25 +416,25 @@
                     Slog.e(TAG, "Can't get children of " + parent + " because it is not valid.");
                     return null;
                 }
-                // For now, only support returning children of persistent root tasks (of which the
-                // only current implementation is TaskTile).
-                if (!(container instanceof TaskTile)) {
+                final Task task = container.asTask();
+                if (task == null) {
+                    Slog.e(TAG, container + " is not a task...");
+                    return null;
+                }
+                // For now, only support returning children of tasks created by the organizer.
+                if (!task.mCreatedByOrganizer) {
                     Slog.w(TAG, "Can only get children of root tasks created via createRootTask");
                     return null;
                 }
                 ArrayList<RunningTaskInfo> out = new ArrayList<>();
-                // Tiles aren't real parents, so we need to go through stacks on the display to
-                // ensure correct ordering.
-                final DisplayContent dc = container.getDisplayContent();
-                for (int i = dc.getStackCount() - 1; i >= 0; --i) {
-                    final ActivityStack as = dc.getStackAt(i);
-                    if (as.getTile() == container) {
-                        if (activityTypes != null
-                                && !ArrayUtils.contains(activityTypes, as.getActivityType())) {
-                            continue;
-                        }
-                        out.add(as.getTaskInfo());
+                for (int i = task.getChildCount() - 1; i >= 0; --i) {
+                    final Task child = task.getChildAt(i).asTask();
+                    if (child == null) continue;
+                    if (activityTypes != null
+                            && !ArrayUtils.contains(activityTypes, child.getActivityType())) {
+                        continue;
                     }
+                    out.add(child.getTaskInfo());
                 }
                 return out;
             }
@@ -451,12 +456,7 @@
                 }
                 ArrayList<RunningTaskInfo> out = new ArrayList<>();
                 for (int i = dc.getStackCount() - 1; i >= 0; --i) {
-                    final ActivityStack task = dc.getStackAt(i);
-                    if (task.getTile() != null) {
-                        // a tile is supposed to look like a parent, so don't include their
-                        // "children" here. They can be accessed via getChildTasks()
-                        continue;
-                    }
+                    final Task task = dc.getStackAt(i);
                     if (activityTypes != null
                             && !ArrayUtils.contains(activityTypes, task.getActivityType())) {
                         continue;
diff --git a/services/core/java/com/android/server/wm/TaskTile.java b/services/core/java/com/android/server/wm/TaskTile.java
deleted file mode 100644
index 51142b1..0000000
--- a/services/core/java/com/android/server/wm/TaskTile.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * 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.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-
-import android.app.ActivityManager;
-import android.app.TaskInfo;
-import android.app.WindowConfiguration;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.os.IBinder;
-import android.util.Slog;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.function.Consumer;
-
-/**
- * A Tile. Right now this acts as a proxy for manipulating non-child stacks. Eventually, this
- * can become an actual parent.
- */
-// TODO(task-hierarchy): Remove when tasks can nest >2 or when single tasks can handle their
-//                       own lifecycles.
-public class TaskTile extends ActivityStack {
-    private static final String TAG = "TaskTile";
-    final ArrayList<WindowContainer> mChildren = new ArrayList<>();
-
-    private static ActivityInfo createEmptyActivityInfo() {
-        ActivityInfo info = new ActivityInfo();
-        info.applicationInfo = new ApplicationInfo();
-        return info;
-    }
-
-    TaskTile(ActivityTaskManagerService atmService, int id, int windowingMode) {
-        super(atmService, id, new Intent() /*intent*/,  null /*affinityIntent*/, null /*affinity*/,
-                null /*rootAffinity*/, null /*realActivity*/, null /*origActivity*/,
-                false /*rootWasReset*/, false /*autoRemoveRecents*/, false /*askedCompatMode*/,
-                0 /*userId*/, 0 /*effectiveUid*/, null /*lastDescription*/,
-                System.currentTimeMillis(), true /*neverRelinquishIdentity*/,
-                new ActivityManager.TaskDescription(), id, INVALID_TASK_ID, INVALID_TASK_ID,
-                0 /*taskAffiliationColor*/, 0 /*callingUid*/, "" /*callingPackage*/,
-                null /*callingFeatureId*/, RESIZE_MODE_RESIZEABLE,
-                false /*supportsPictureInPicture*/, false /*_realActivitySuspended*/,
-                false /*userSetupComplete*/, INVALID_MIN_SIZE, INVALID_MIN_SIZE,
-                createEmptyActivityInfo(), null /*voiceSession*/, null /*voiceInteractor*/,
-                null /*stack*/);
-        getRequestedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
-    }
-
-    @Override
-    void onDisplayChanged(DisplayContent dc) {
-        mDisplayContent = null;
-        if (dc != null) {
-            dc.getPendingTransaction().merge(getPendingTransaction());
-        }
-        mDisplayContent = dc;
-        // Virtual parent, so don't notify children.
-    }
-
-    @Override
-    TaskTile asTile() {
-        return this;
-    }
-
-    @Override
-    protected void addChild(WindowContainer child, Comparator<WindowContainer> comparator) {
-        throw new RuntimeException("Improper use of addChild() on Tile");
-    }
-
-    @Override
-    void addChild(WindowContainer child, int index) {
-        mChildren.add(child);
-        if (child instanceof ActivityStack) {
-            ((ActivityStack) child).setTile(this);
-        }
-        mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
-                this, false /* force */);
-    }
-
-    @Override
-    void removeChild(WindowContainer child) {
-        if (child instanceof ActivityStack) {
-            ((ActivityStack) child).setTile(null);
-        }
-        mChildren.remove(child);
-        mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
-                this, false /* force */);
-    }
-
-    void removeAllChildren() {
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowContainer child = mChildren.get(i);
-            if (child instanceof ActivityStack) {
-                ((ActivityStack) child).setTile(null);
-            }
-        }
-        mChildren.clear();
-        mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
-                this, false /* force */);
-    }
-
-    @Override
-    protected int getChildCount() {
-        // Currently 0 as this isn't a proper hierarchy member yet.
-        return 0;
-    }
-
-    @Override
-    public void setWindowingMode(/*@WindowConfiguration.WindowingMode*/ int windowingMode) {
-        Configuration c = new Configuration(getRequestedOverrideConfiguration());
-        c.windowConfiguration.setWindowingMode(windowingMode);
-        onRequestedOverrideConfigurationChanged(c);
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newParentConfig) {
-        super.onConfigurationChanged(newParentConfig);
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowContainer child = mChildren.get(i);
-            child.onConfigurationChanged(child.getParent().getConfiguration());
-        }
-    }
-
-    void forAllTileActivities(Consumer<ActivityRecord> callback) {
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            mChildren.get(i).forAllActivities(callback, true /* traverseTopToBottom */);
-        }
-    }
-
-    /**
-     * Until this can be part of the hierarchy, the Stack level can use this utility during
-     * resolveOverrideConfig to simulate inheritance.
-     */
-    void updateResolvedConfig(Configuration inOutResolvedConfig) {
-        Rect resolveBounds = inOutResolvedConfig.windowConfiguration.getBounds();
-        if (resolveBounds.isEmpty()) {
-            resolveBounds.set(getRequestedOverrideBounds());
-        }
-        int stackMode = inOutResolvedConfig.windowConfiguration.getWindowingMode();
-        if (stackMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED
-                || stackMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN) {
-            // Also replace FULLSCREEN because we interpret FULLSCREEN as "fill parent"
-            inOutResolvedConfig.windowConfiguration.setWindowingMode(
-                    getRequestedOverrideWindowingMode());
-        }
-        if (inOutResolvedConfig.smallestScreenWidthDp
-                == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
-            inOutResolvedConfig.smallestScreenWidthDp =
-                    getRequestedOverrideConfiguration().smallestScreenWidthDp;
-        }
-        if (inOutResolvedConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
-            inOutResolvedConfig.screenWidthDp = getRequestedOverrideConfiguration().screenWidthDp;
-        }
-        if (inOutResolvedConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
-            inOutResolvedConfig.screenHeightDp = getRequestedOverrideConfiguration().screenHeightDp;
-        }
-        Rect resolveAppBounds = inOutResolvedConfig.windowConfiguration.getAppBounds();
-        if (resolveAppBounds == null || resolveAppBounds.isEmpty()) {
-            inOutResolvedConfig.windowConfiguration.setAppBounds(
-                    getRequestedOverrideConfiguration().windowConfiguration.getAppBounds());
-        }
-    }
-
-    @Override
-    void fillTaskInfo(TaskInfo info) {
-        super.fillTaskInfo(info);
-        WindowContainer top = null;
-        // Check mChildren.isEmpty directly because hasChild() -> getChildCount() always returns 0
-        if (!mChildren.isEmpty()) {
-            // Find the top-most root task which is a virtual child of this Tile. Because this is a
-            // virtual parent, the mChildren order here isn't changed during hierarchy operations.
-            WindowContainer parent = mChildren.get(0).getParent();
-            for (int i = parent.getChildCount() - 1; i >= 0; --i) {
-                if (mChildren.contains(parent.getChildAt(i))) {
-                    top = parent.getChildAt(i);
-                    break;
-                }
-            }
-        }
-        final Task topTask = top == null ? null : top.getTopMostTask();
-        boolean isResizable = topTask == null || topTask.isResizeable();
-        info.resizeMode = isResizable ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE;
-        info.topActivityType = top == null ? ACTIVITY_TYPE_UNDEFINED : top.getActivityType();
-    }
-
-    @Override
-    void removeImmediately() {
-        removeAllChildren();
-        super.removeImmediately();
-    }
-
-    @Override
-    void taskOrganizerDied() {
-        super.taskOrganizerDied();
-        removeImmediately();
-    }
-
-    static TaskTile forToken(IBinder token) {
-        try {
-            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/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index e416e80..5f21e17 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -159,17 +159,7 @@
                                 false /* preserveWindow */);
                         try {
                             for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
-                                final WindowContainer wc = haveConfigChanges.valueAt(i);
-                                final Task task = wc.asTask();
-                                final TaskTile tile = task != null ? task.asTile() : null;
-                                if (tile != null) {
-                                    // Special case for tile. Can't override normal forAllActivities
-                                    // because it generates duplicate calls and messes up existing
-                                    // code-paths.
-                                    tile.forAllTileActivities(f);
-                                } else {
-                                    wc.forAllActivities(f);
-                                }
+                                haveConfigChanges.valueAt(i).forAllActivities(f);
                             }
                         } finally {
                             f.recycle();
@@ -223,51 +213,65 @@
 
     private int sanitizeAndApplyHierarchyOp(WindowContainer container,
             WindowContainerTransaction.HierarchyOp hop) {
-        if (!(container instanceof Task)) {
+        final Task task = container.asTask();
+        if (task == null) {
             throw new IllegalArgumentException("Invalid container in hierarchy op");
         }
-        if (container.getDisplayContent() == null) {
-            Slog.w(TAG, "Container is no longer attached: " + container);
+        final DisplayContent dc = task.getDisplayContent();
+        if (dc == null) {
+            Slog.w(TAG, "Container is no longer attached: " + task);
             return 0;
         }
+        final ActivityStack as = (ActivityStack) task;
+
         if (hop.isReparent()) {
-            // special case for tiles since they are "virtual" parents
-            if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
-                ActivityStack as = (ActivityStack) container;
-                TaskTile newParent = hop.getNewParent() == null ? null
-                        : (TaskTile) WindowContainer.fromBinder(hop.getNewParent());
-                if (as.getTile() != newParent) {
-                    if (as.getTile() != null) {
-                        as.getTile().removeChild(as);
+            final boolean isNonOrganizedRootableTask =
+                    (task.isRootTask() && !task.mCreatedByOrganizer)
+                            || task.getParent().asTask().mCreatedByOrganizer;
+            if (isNonOrganizedRootableTask) {
+                Task newParent = hop.getNewParent() == null ? null
+                        : WindowContainer.fromBinder(hop.getNewParent()).asTask();
+                if (task.getParent() != newParent) {
+                    if (newParent == null) {
+                        // Re-parent task to display as a root task.
+                        dc.moveStackToDisplay(as, hop.getToTop());
+                    } else if (newParent.inMultiWindowMode() && !task.isResizeable()
+                            && task.isLeafTask()) {
+                        Slog.w(TAG, "Can't support task that doesn't support multi-window mode in"
+                                + " multi-window mode... newParent=" + newParent + " task=" + task);
+                        return 0;
+                    } else {
+                        // Clear the window crop on root task since it may not be updated after
+                        // reparent (no longer be a root task)
+                        task.getSurfaceControl().setWindowCrop(null);
+                        task.reparent((ActivityStack) newParent,
+                                hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
+                                false /*moveParents*/, "sanitizeAndApplyHierarchyOp");
                     }
-                    if (newParent != null) {
-                        if (!as.affectedBySplitScreenResize()) {
-                            return 0;
-                        }
-                        newParent.addChild(as, POSITION_TOP);
-                    }
-                }
-                if (hop.getToTop()) {
-                    as.getDisplay().positionStackAtTop(as, false /* includingParents */);
                 } else {
-                    as.getDisplay().positionStackAtBottom(as);
+                    final ActivityStack rootTask =
+                            (ActivityStack) (newParent != null ? newParent : task.getRootTask());
+                    if (hop.getToTop()) {
+                        as.getDisplay().positionStackAtTop(rootTask, false /* includingParents */);
+                    } else {
+                        as.getDisplay().positionStackAtBottom(rootTask);
+                    }
                 }
-            } else if (container instanceof Task) {
+            } else {
                 throw new RuntimeException("Reparenting leaf Tasks is not supported now.");
             }
         } else {
             // Ugh, of course ActivityStack has its own special reorder logic...
-            if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
-                ActivityStack as = (ActivityStack) container;
+            if (task.isRootTask()) {
                 if (hop.getToTop()) {
-                    as.getDisplay().positionStackAtTop(as, false /* includingParents */);
+                    dc.positionStackAtTop(as, false /* includingParents */);
                 } else {
-                    as.getDisplay().positionStackAtBottom(as);
+                    dc.positionStackAtBottom(as);
                 }
             } else {
-                container.getParent().positionChildAt(
+                task.getParent().positionChildAt(
                         hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
-                        container, false /* includingParents */);
+                        task, false /* includingParents */);
             }
         }
         return TRANSACT_EFFECTS_LIFECYCLE;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 161152b..c4d700c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -131,6 +131,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.H.WINDOW_STATE_BLAST_SYNC_TIMEOUT;
 import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
@@ -138,7 +139,6 @@
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
 import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
-import static com.android.server.wm.WindowManagerService.H.WINDOW_STATE_BLAST_SYNC_TIMEOUT;
 import static com.android.server.wm.WindowStateAnimator.COMMIT_DRAW_PENDING;
 import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
 import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
@@ -608,6 +608,11 @@
     boolean mSeamlesslyRotated = false;
 
     /**
+     * Indicates if this window is behind IME. Only windows behind IME can get insets from IME.
+     */
+    boolean mBehindIme = false;
+
+    /**
      * Surface insets from the previous call to relayout(), used to track
      * if we are changing the Surface insets.
      */
@@ -2270,9 +2275,9 @@
             return false;
         }
 
-        if (PixelFormat.formatHasAlpha(mAttrs.format) && mAttrs.alpha == 0) {
-            // Support legacy use cases where completely transparent windows can still be ime target
-            // with FLAG_NOT_FOCUSABLE and ALT_FOCUSABLE_IM set.
+        if (PixelFormat.formatHasAlpha(mAttrs.format)) {
+            // Support legacy use cases where transparent windows can still be ime target with
+            // FLAG_NOT_FOCUSABLE and ALT_FOCUSABLE_IM set.
             // Certain apps listen for IME insets using transparent windows and ADJUST_NOTHING to
             // manually synchronize app content to IME animation b/144619551.
             // TODO(b/145812508): remove this once new focus management is complete b/141738570
@@ -3338,7 +3343,7 @@
         }
 
         final ActivityStack stack = task.getStack();
-        if (stack == null) {
+        if (stack == null || stack.mCreatedByOrganizer) {
             return;
         }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 740c5cb3..09fab3e 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);
         }
@@ -4405,6 +4434,11 @@
                     clearDeviceOwnerLocked(getDeviceOwnerAdminLocked(), userHandle);
                 }
                 if (isProfileOwner(adminReceiver, userHandle)) {
+                    if (isProfileOwnerOfOrganizationOwnedDevice(userHandle)) {
+                        mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
+                                false,
+                                UserHandle.of(getProfileParentId(userHandle)));
+                    }
                     final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver,
                             userHandle, /* parent */ false);
                     clearProfileOwnerLocked(admin, userHandle);
@@ -13041,26 +13075,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 +13102,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 +15685,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 +15704,7 @@
         }
     }
 
-    private @PersonalAppSuspensionReason int makeSuspensionReasons(
+    private @PersonalAppsSuspensionReason int makeSuspensionReasons(
             boolean explicit, boolean timeout) {
         int result = PERSONAL_APPS_NOT_SUSPENDED;
         if (explicit) {
@@ -15793,7 +15828,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 +15848,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/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/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 6ec0f91..ed400ea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -82,9 +82,8 @@
 import android.platform.test.annotations.Presubmit;
 import android.service.voice.IVoiceInteractionSession;
 import android.view.Gravity;
-import android.window.IWindowContainer;
-import android.view.SurfaceControl;
 import android.window.ITaskOrganizer;
+import android.window.IWindowContainer;
 
 import androidx.test.filters.SmallTest;
 
@@ -975,7 +974,7 @@
 
         // Move activity to split-screen-primary stack and make sure it has the focus.
         TestSplitOrganizer splitOrg = new TestSplitOrganizer(mService, top.getDisplayId());
-        splitOrg.mPrimary.addChild(top.getRootTask(), 0 /* index */);
+        top.getRootTask().reparent(splitOrg.mPrimary, POSITION_BOTTOM);
         top.getRootTask().moveToFront("testWindowingModeOptionsLaunchAdjacent");
 
         // Activity must landed on split-screen-secondary when launch adjacent.
@@ -1001,8 +1000,8 @@
 
     static class TestSplitOrganizer extends ITaskOrganizer.Stub {
         final ActivityTaskManagerService mService;
-        TaskTile mPrimary;
-        TaskTile mSecondary;
+        Task mPrimary;
+        Task mSecondary;
         boolean mInSplit = false;
         int mDisplayId;
         TestSplitOrganizer(ActivityTaskManagerService service, int displayId) {
@@ -1014,10 +1013,10 @@
                     WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
             IWindowContainer primary = mService.mTaskOrganizerController.createRootTask(
                     displayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).token;
-            mPrimary = TaskTile.forToken(primary.asBinder());
+            mPrimary = WindowContainer.fromBinder(primary.asBinder()).asTask();
             IWindowContainer secondary = mService.mTaskOrganizerController.createRootTask(
                     displayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).token;
-            mSecondary = TaskTile.forToken(secondary.asBinder());
+            mSecondary = WindowContainer.fromBinder(secondary.asBinder()).asTask();
         }
         @Override
         public void onTaskAppeared(ActivityManager.RunningTaskInfo info) {
@@ -1042,7 +1041,7 @@
                     for (int i = dc.getStackCount() - 1; i >= 0; --i) {
                         if (!WindowConfiguration.isSplitScreenWindowingMode(
                                 dc.getStackAt(i).getWindowingMode())) {
-                            mSecondary.addChild(dc.getStackAt(i), 0);
+                            dc.getStackAt(i).reparent(mSecondary, POSITION_BOTTOM);
                         }
                     }
                 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 4634e2d..716369d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -464,7 +464,7 @@
         ActivityStack build() {
             final int stackId = mStackId >= 0 ? mStackId : mDisplay.getNextStackId();
             final ActivityStack stack = mDisplay.createStackUnchecked(mWindowingMode,
-                    mActivityType, stackId, mOnTop, mInfo, mIntent);
+                    mActivityType, stackId, mOnTop, mInfo, mIntent, false /* createdByOrganizer */);
             final ActivityStackSupervisor supervisor = mRootWindowContainer.mStackSupervisor;
 
             if (mCreateActivity) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index bfb126f..db7bce4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -19,10 +19,12 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.view.InsetsState.ITYPE_IME;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
 import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
@@ -147,6 +149,61 @@
     }
 
     @Test
+    public void testStripForDispatch_belowIme() {
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+
+        getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
+
+        assertNotNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME));
+    }
+
+    @Test
+    public void testStripForDispatch_aboveIme() {
+        final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+
+        getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
+
+        assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME));
+    }
+
+    @Test
+    public void testStripForDispatch_childWindow_altFocusable() {
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+
+        final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
+        child.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM;
+
+        final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+
+        // IME cannot be the IME target.
+        ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
+
+        getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
+
+        assertNull(getController().getInsetsForDispatch(child).peekSource(ITYPE_IME));
+    }
+
+    @Test
+    public void testStripForDispatch_childWindow_splitScreen() {
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+
+        final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
+        child.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
+        child.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+
+        final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+
+        // IME cannot be the IME target.
+        ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
+
+        getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
+
+        assertNull(getController().getInsetsForDispatch(child).peekSource(ITYPE_IME));
+    }
+
+    @Test
     public void testImeForDispatch() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
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 a96f401..ed635ce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -37,6 +37,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -347,45 +348,62 @@
         assertEquals(ACTIVITY_TYPE_UNDEFINED, info2.topActivityType);
 
         DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
-        List<TaskTile> infos = getTaskTiles(dc);
+        List<Task> infos = getTasksCreatedByOrganizer(dc);
         assertEquals(2, infos.size());
 
         assertTrue(mWm.mAtmService.mTaskOrganizerController.deleteRootTask(info1.token));
-        infos = getTaskTiles(dc);
+        infos = getTasksCreatedByOrganizer(dc);
         assertEquals(1, infos.size());
         assertEquals(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, infos.get(0).getWindowingMode());
     }
 
     @Test
     public void testTileAddRemoveChild() {
+        ITaskOrganizer listener = new ITaskOrganizer.Stub() {
+            @Override
+            public void onTaskAppeared(RunningTaskInfo taskInfo) { }
+
+            @Override
+            public void onTaskVanished(RunningTaskInfo container) { }
+
+            @Override
+            public void onTaskInfoChanged(RunningTaskInfo info) throws RemoteException {
+            }
+        };
+        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener,
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
         RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
                 mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
 
         final ActivityStack stack = createTaskStackOnDisplay(
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
         assertEquals(mDisplayContent.getWindowingMode(), stack.getWindowingMode());
-        TaskTile tile1 = TaskTile.forToken(info1.token.asBinder());
-        tile1.addChild(stack, 0 /* index */);
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        wct.reparent(stack.mRemoteToken, info1.token, true /* onTop */);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
         assertEquals(info1.configuration.windowConfiguration.getWindowingMode(),
                 stack.getWindowingMode());
 
         // Info should reflect new membership
-        List<TaskTile> tiles = getTaskTiles(mDisplayContent);
-        info1 = tiles.get(0).getTaskInfo();
+        List<Task> infos = getTasksCreatedByOrganizer(mDisplayContent);
+        info1 = infos.get(0).getTaskInfo();
         assertEquals(ACTIVITY_TYPE_STANDARD, info1.topActivityType);
 
         // Children inherit configuration
         Rect newSize = new Rect(10, 10, 300, 300);
-        Configuration c = new Configuration(tile1.getRequestedOverrideConfiguration());
+        Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask();
+        Configuration c = new Configuration(task1.getRequestedOverrideConfiguration());
         c.windowConfiguration.setBounds(newSize);
         doNothing().when(stack).adjustForMinimalTaskDimensions(any(), any());
-        tile1.onRequestedOverrideConfigurationChanged(c);
+        task1.onRequestedOverrideConfigurationChanged(c);
         assertEquals(newSize, stack.getBounds());
 
-        tile1.removeChild(stack);
+        wct = new WindowContainerTransaction();
+        wct.reparent(stack.mRemoteToken, null, true /* onTop */);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
         assertEquals(mDisplayContent.getWindowingMode(), stack.getWindowingMode());
-        tiles = getTaskTiles(mDisplayContent);
-        info1 = tiles.get(0).getTaskInfo();
+        infos = getTasksCreatedByOrganizer(mDisplayContent);
+        info1 = infos.get(0).getTaskInfo();
         assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType);
     }
 
@@ -415,8 +433,10 @@
 
         final ActivityStack stack = createTaskStackOnDisplay(
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
-        TaskTile tile1 = TaskTile.forToken(info1.token.asBinder());
-        tile1.addChild(stack, 0 /* index */);
+        Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask();
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        wct.reparent(stack.mRemoteToken, info1.token, true /* onTop */);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
         assertTrue(called[0]);
         assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType);
 
@@ -424,19 +444,24 @@
         called[0] = false;
         final ActivityStack stack2 = createTaskStackOnDisplay(
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mDisplayContent);
-        tile1.addChild(stack2, 0 /* index */);
+        wct = new WindowContainerTransaction();
+        wct.reparent(stack2.mRemoteToken, info1.token, true /* onTop */);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
         assertTrue(called[0]);
         assertEquals(ACTIVITY_TYPE_HOME, lastReportedTiles.get(0).topActivityType);
 
         lastReportedTiles.clear();
         called[0] = false;
-        mDisplayContent.positionStackAtTop(stack, false /* includingParents */);
+        task1.positionChildAt(POSITION_TOP, stack, false /* includingParents */);
         assertTrue(called[0]);
         assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType);
 
         lastReportedTiles.clear();
         called[0] = false;
-        tile1.removeAllChildren();
+        wct = new WindowContainerTransaction();
+        wct.reparent(stack.mRemoteToken, null, true /* onTop */);
+        wct.reparent(stack2.mRemoteToken, null, true /* onTop */);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
         assertTrue(called[0]);
         assertEquals(ACTIVITY_TYPE_UNDEFINED, lastReportedTiles.get(0).topActivityType);
     }
@@ -457,9 +482,11 @@
             }
         };
         mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(
+                listener, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(
                 listener, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
         RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
-                mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+                mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
         RunningTaskInfo info2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
                 mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
 
@@ -522,13 +549,11 @@
                 lastReportedTiles.get(info1.token.asBinder()).topActivityType);
     }
 
-    private List<TaskTile> getTaskTiles(DisplayContent dc) {
-        ArrayList<TaskTile> out = new ArrayList<>();
+    private List<Task> getTasksCreatedByOrganizer(DisplayContent dc) {
+        ArrayList<Task> out = new ArrayList<>();
         for (int i = dc.getStackCount() - 1; i >= 0; --i) {
-            final TaskTile t = dc.getStackAt(i).asTile();
-            if (t != null) {
-                out.add(t);
-            }
+            final Task t = dc.getStackAt(i);
+            if (t.mCreatedByOrganizer) out.add(t);
         }
         return out;
     }
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..56c19a4 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;
@@ -151,7 +152,7 @@
             assertFalse(factory.mCreated);
 
             Task.create(mService, 0 /*taskId*/, 0 /*activityType*/,
-                    new ActivityInfo(), new Intent());
+                    new ActivityInfo(), new Intent(), false /* createdByOrganizer */);
 
             assertTrue(factory.mCreated);
         } finally {
@@ -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();
@@ -1000,7 +1015,7 @@
 
         @Override
         Task create(ActivityTaskManagerService service, int taskId, int activityType,
-                ActivityInfo info, Intent intent) {
+                ActivityInfo info, Intent intent, boolean createdByOrganizer) {
             mCreated = true;
             return null;
         }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 85e4a16..e95ccab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -251,11 +251,9 @@
 
         // b/145812508: special legacy use-case for transparent/translucent windows.
         appWindow.mAttrs.format = PixelFormat.TRANSPARENT;
-        appWindow.mAttrs.alpha = 0;
         assertTrue(appWindow.canBeImeTarget());
 
         appWindow.mAttrs.format = PixelFormat.OPAQUE;
-        appWindow.mAttrs.alpha = 1;
         appWindow.mAttrs.flags &= ~FLAG_ALT_FOCUSABLE_IM;
         assertFalse(appWindow.canBeImeTarget());
         appWindow.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE;
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/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 2f9a1c8..9be97b50 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/RollbackTest/RollbackTest.xml b/tests/RollbackTest/RollbackTest.xml
index 269cec1..7b85cc8 100644
--- a/tests/RollbackTest/RollbackTest.xml
+++ b/tests/RollbackTest/RollbackTest.xml
@@ -23,6 +23,10 @@
         <option name="run-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es package &quot;com.google.android.gms.platformconfigurator&quot; --es user '\\*' --esa flags &quot;ModuleConfig__versioned_immediate_commit_packages&quot; --esa types &quot;bytes&quot; --esa values &quot;Cm5vdGFwYWNrYWdlOgA=&quot; com.google.android.gms" />
         <option name="teardown-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es action delete --es package &quot;com.google.android.gms.platformconfigurator&quot; --es user '\*' --esa flag &quot;ModuleConfig__immediate_commit_packages&quot; com.google.android.gms" />
         <option name="teardown-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es action delete --es package &quot;com.google.android.gms.platformconfigurator&quot; --es user '\*' --esa flag &quot;ModuleConfig__versioned_immediate_commit_packages&quot; com.google.android.gms" />
+        <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
+        <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
+        <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
+        <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.tests.rollback" />
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index 5a92d68..cab8b42 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -75,6 +75,12 @@
     private static final String PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS =
             "enable_rollback_timeout";
 
+    private static boolean hasRollbackInclude(List<RollbackInfo> rollbacks, String packageName) {
+        return rollbacks.stream().anyMatch(
+                ri -> ri.getPackages().stream().anyMatch(
+                        pri -> packageName.equals(pri.getPackageName())));
+    }
+
     /**
      * Test basic rollbacks.
      */
@@ -113,18 +119,14 @@
             // Uninstall TestApp.A
             Uninstall.packages(TestApp.A);
             assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
-            // TODO: There is currently a race condition between when the app is
-            // uninstalled and when rollback manager deletes the rollback. Fix it
-            // so that's not the case!
             for (int i = 0; i < 5; ++i) {
-                RollbackInfo rollback = getUniqueRollbackInfoForPackage(
-                        rm.getRecentlyCommittedRollbacks(), TestApp.A);
-                if (rollback != null) {
+                if (hasRollbackInclude(rm.getRecentlyCommittedRollbacks(), TestApp.A)) {
                     Log.i(TAG, "Sleeping 1 second to wait for uninstall to take effect.");
                     Thread.sleep(1000);
                 }
             }
 
+            assertThat(hasRollbackInclude(rm.getRecentlyCommittedRollbacks(), TestApp.A)).isFalse();
             // The app should not be available for rollback.
             waitForUnavailableRollback(TestApp.A);
 
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/tools/stats_log_api_gen/.clang-format b/tools/stats_log_api_gen/.clang-format
new file mode 100644
index 0000000..cead3a0
--- /dev/null
+++ b/tools/stats_log_api_gen/.clang-format
@@ -0,0 +1,17 @@
+BasedOnStyle: Google
+AllowShortIfStatementsOnASingleLine: true
+AllowShortFunctionsOnASingleLine: false
+AllowShortLoopsOnASingleLine: true
+BinPackArguments: true
+BinPackParameters: true
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+ContinuationIndentWidth: 8
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+AccessModifierOffset: -4
+IncludeCategories:
+  - Regex:    '^"Log\.h"'
+    Priority:    -1
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index 47eb63e..bf39093 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -15,11 +15,13 @@
  */
 
 #include "Collation.h"
-#include "frameworks/base/cmds/statsd/src/atoms.pb.h"
 
 #include <stdio.h>
+
 #include <map>
 
+#include "frameworks/base/cmds/statsd/src/atoms.pb.h"
+
 namespace android {
 namespace stats_log_api_gen {
 
@@ -32,55 +34,47 @@
 
 const bool dbg = false;
 
-
 //
 // AtomDecl class
 //
 
-AtomDecl::AtomDecl()
-    :code(0),
-     name()
-{
+AtomDecl::AtomDecl() : code(0), name() {
 }
 
-AtomDecl::AtomDecl(const AtomDecl &that)
-      : code(that.code),
-        name(that.name),
-        message(that.message),
-        fields(that.fields),
-        fieldNumberToAnnotations(that.fieldNumberToAnnotations),
-        primaryFields(that.primaryFields),
-        exclusiveField(that.exclusiveField),
-        defaultState(that.defaultState),
-        resetState(that.resetState),
-        nested(that.nested),
-        uidField(that.uidField),
-        whitelisted(that.whitelisted) {}
-
-AtomDecl::AtomDecl(int c, const string& n, const string& m)
-    :code(c),
-     name(n),
-     message(m)
-{
+AtomDecl::AtomDecl(const AtomDecl& that)
+    : code(that.code),
+      name(that.name),
+      message(that.message),
+      fields(that.fields),
+      fieldNumberToAnnotations(that.fieldNumberToAnnotations),
+      primaryFields(that.primaryFields),
+      exclusiveField(that.exclusiveField),
+      defaultState(that.defaultState),
+      resetState(that.resetState),
+      nested(that.nested),
+      uidField(that.uidField),
+      whitelisted(that.whitelisted),
+      truncateTimestamp(that.truncateTimestamp) {
 }
 
-AtomDecl::~AtomDecl()
-{
+AtomDecl::AtomDecl(int c, const string& n, const string& m) : code(c), name(n), message(m) {
 }
 
+AtomDecl::~AtomDecl() {
+}
 
 /**
- * Print an error message for a FieldDescriptor, including the file name and line number.
+ * Print an error message for a FieldDescriptor, including the file name and
+ * line number.
  */
-static void
-print_error(const FieldDescriptor* field, const char* format, ...)
-{
+static void print_error(const FieldDescriptor* field, const char* format, ...) {
     const Descriptor* message = field->containing_type();
     const FileDescriptor* file = message->file();
 
     SourceLocation loc;
     if (field->GetSourceLocation(&loc)) {
-        // TODO: this will work if we can figure out how to pass --include_source_info to protoc
+        // TODO: this will work if we can figure out how to pass
+        // --include_source_info to protoc
         fprintf(stderr, "%s:%d: ", file->name().c_str(), loc.start_line);
     } else {
         fprintf(stderr, "%s: ", file->name().c_str());
@@ -88,15 +82,13 @@
     va_list args;
     va_start(args, format);
     vfprintf(stderr, format, args);
-    va_end (args);
+    va_end(args);
 }
 
 /**
  * Convert a protobuf type into a java type.
  */
-static java_type_t
-java_type(const FieldDescriptor* field)
-{
+static java_type_t java_type(const FieldDescriptor* field) {
     int protoType = field->type();
     switch (protoType) {
         case FieldDescriptor::TYPE_DOUBLE:
@@ -121,12 +113,10 @@
             return JAVA_TYPE_UNKNOWN;
         case FieldDescriptor::TYPE_MESSAGE:
             // TODO: not the final package name
-            if (field->message_type()->full_name() ==
-                "android.os.statsd.AttributionNode") {
-              return JAVA_TYPE_ATTRIBUTION_CHAIN;
-            } else if (field->message_type()->full_name() ==
-                       "android.os.statsd.KeyValuePair") {
-              return JAVA_TYPE_KEY_VALUE_PAIR;
+            if (field->message_type()->full_name() == "android.os.statsd.AttributionNode") {
+                return JAVA_TYPE_ATTRIBUTION_CHAIN;
+            } else if (field->message_type()->full_name() == "android.os.statsd.KeyValuePair") {
+                return JAVA_TYPE_KEY_VALUE_PAIR;
             } else if (field->options().GetExtension(os::statsd::log_mode) ==
                        os::statsd::LogMode::MODE_BYTES) {
                 return JAVA_TYPE_BYTE_ARRAY;
@@ -155,307 +145,298 @@
 /**
  * Gather the enums info.
  */
-void collate_enums(const EnumDescriptor &enumDescriptor, AtomField *atomField) {
+void collate_enums(const EnumDescriptor& enumDescriptor, AtomField* atomField) {
     for (int i = 0; i < enumDescriptor.value_count(); i++) {
         atomField->enumValues[enumDescriptor.value(i)->number()] =
-            enumDescriptor.value(i)->name().c_str();
+                enumDescriptor.value(i)->name().c_str();
     }
 }
 
 static void addAnnotationToAtomDecl(AtomDecl* atomDecl, const int fieldNumber,
-        const int annotationId, const AnnotationType annotationType,
-        const AnnotationValue annotationValue) {
+                                    const int annotationId, const AnnotationType annotationType,
+                                    const AnnotationValue annotationValue) {
     if (dbg) {
-        printf("   Adding annotation to %s: [%d] = {id: %d, type: %d}\n",
-                atomDecl->name.c_str(), fieldNumber, annotationId, annotationType);
+        printf("   Adding annotation to %s: [%d] = {id: %d, type: %d}\n", atomDecl->name.c_str(),
+               fieldNumber, annotationId, annotationType);
     }
-    atomDecl->fieldNumberToAnnotations[fieldNumber].insert(make_shared<Annotation>(
-                annotationId, atomDecl->code, annotationType, annotationValue));
+    atomDecl->fieldNumberToAnnotations[fieldNumber].insert(
+            make_shared<Annotation>(annotationId, atomDecl->code, annotationType, annotationValue));
 }
 
-/**
- * Gather the info about an atom proto.
- */
-int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
-                 vector<java_type_t> *signature) {
-
-  int errorCount = 0;
-
-  // Build a sorted list of the fields. Descriptor has them in source file
-  // order.
-  map<int, const FieldDescriptor *> fields;
-  for (int j = 0; j < atom->field_count(); j++) {
-    const FieldDescriptor *field = atom->field(j);
-    fields[field->number()] = field;
-  }
-
-  // Check that the parameters start at 1 and go up sequentially.
-  int expectedNumber = 1;
-  for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
-       it != fields.end(); it++) {
-    const int number = it->first;
-    const FieldDescriptor *field = it->second;
-    if (number != expectedNumber) {
-      print_error(field,
-                  "Fields must be numbered consecutively starting at 1:"
-                  " '%s' is %d but should be %d\n",
-                  field->name().c_str(), number, expectedNumber);
-      errorCount++;
-      expectedNumber = number;
-      continue;
-    }
-    expectedNumber++;
-  }
-
-  // Check that only allowed types are present. Remove any invalid ones.
-  for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
-       it != fields.end(); it++) {
-    const FieldDescriptor *field = it->second;
-    bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) ==
-                         os::statsd::LogMode::MODE_BYTES;
-
-    java_type_t javaType = java_type(field);
-
-    if (javaType == JAVA_TYPE_UNKNOWN) {
-      print_error(field, "Unkown type for field: %s\n", field->name().c_str());
-      errorCount++;
-      continue;
-    } else if (javaType == JAVA_TYPE_OBJECT &&
-               atomDecl->code < PULL_ATOM_START_ID) {
-        // Allow attribution chain, but only at position 1.
-        print_error(field,
-                    "Message type not allowed for field in pushed atoms: %s\n",
-                    field->name().c_str());
-        errorCount++;
-        continue;
-    } else if (javaType == JAVA_TYPE_BYTE_ARRAY && !isBinaryField) {
-        print_error(field, "Raw bytes type not allowed for field: %s\n",
-                    field->name().c_str());
-        errorCount++;
-        continue;
-    }
-
-    if (isBinaryField && javaType != JAVA_TYPE_BYTE_ARRAY) {
-        print_error(field, "Cannot mark field %s as bytes.\n",
-                    field->name().c_str());
-        errorCount++;
-        continue;
-    }
-
-    // Doubles are not supported yet.
-    if (javaType == JAVA_TYPE_DOUBLE) {
-        print_error(field, "Doubles are not supported in atoms. Please change field %s to float\n",
-                    field->name().c_str());
-        errorCount++;
-        continue;
-    }
-
-    if (field->is_repeated() &&
-        !(javaType == JAVA_TYPE_ATTRIBUTION_CHAIN || javaType == JAVA_TYPE_KEY_VALUE_PAIR)) {
-        print_error(field,
-                    "Repeated fields are not supported in atoms. Please make field %s not "
-                    "repeated.\n",
-                    field->name().c_str());
-        errorCount++;
-        continue;
-    }
-  }
-
-  // Check that if there's an attribution chain, it's at position 1.
-  for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
-       it != fields.end(); it++) {
-    int number = it->first;
-    if (number != 1) {
-      const FieldDescriptor *field = it->second;
-      java_type_t javaType = java_type(field);
-      if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
-        print_error(
-            field,
-            "AttributionChain fields must have field id 1, in message: '%s'\n",
-            atom->name().c_str());
-        errorCount++;
-      }
-    }
-  }
-
-  // Build the type signature and the atom data.
-  for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
-       it != fields.end(); it++) {
-    const FieldDescriptor *field = it->second;
-    java_type_t javaType = java_type(field);
-    bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) ==
-                         os::statsd::LogMode::MODE_BYTES;
-
-    AtomField atField(field->name(), javaType);
-    // Generate signature for pushed atoms
-    if (atomDecl->code < PULL_ATOM_START_ID) {
-      if (javaType == JAVA_TYPE_ENUM) {
-        // All enums are treated as ints when it comes to function signatures.
-        signature->push_back(JAVA_TYPE_INT);
-      } else if (javaType == JAVA_TYPE_OBJECT && isBinaryField) {
-          signature->push_back(JAVA_TYPE_BYTE_ARRAY);
-      } else {
-          signature->push_back(javaType);
-      }
-    }
-    if (javaType == JAVA_TYPE_ENUM) {
-      // All enums are treated as ints when it comes to function signatures.
-      collate_enums(*field->enum_type(), &atField);
-    }
-    atomDecl->fields.push_back(atField);
+static int collate_field_annotations(AtomDecl* atomDecl, const FieldDescriptor* field,
+                                     const int fieldNumber, const java_type_t& javaType) {
+    int errorCount = 0;
 
     if (field->options().HasExtension(os::statsd::state_field_option)) {
         const int option = field->options().GetExtension(os::statsd::state_field_option).option();
         if (option != STATE_OPTION_UNSET) {
-            addAnnotationToAtomDecl(atomDecl, signature->size(), ANNOTATION_ID_STATE_OPTION,
-                    ANNOTATION_TYPE_INT, AnnotationValue(option));
+            addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_STATE_OPTION,
+                                    ANNOTATION_TYPE_INT, AnnotationValue(option));
         }
 
         if (option == STATE_OPTION_PRIMARY) {
-            if (javaType == JAVA_TYPE_UNKNOWN ||
-                    javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
-                    javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
-                print_error(
-                    field,
-                    "Invalid primary state field: '%s'\n",
-                    atom->name().c_str());
+            if (javaType == JAVA_TYPE_UNKNOWN || javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
+                javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
+                print_error(field, "Invalid primary state field: '%s'\n",
+                            atomDecl->message.c_str());
                 errorCount++;
-                continue;
             }
-            atomDecl->primaryFields.push_back(it->first);
-
+            atomDecl->primaryFields.push_back(fieldNumber);
         }
 
         if (option == STATE_OPTION_PRIMARY_FIELD_FIRST_UID) {
             if (javaType != JAVA_TYPE_ATTRIBUTION_CHAIN) {
-                print_error(
-                    field,
-                    "PRIMARY_FIELD_FIRST_UID annotation is only for AttributionChains: '%s'\n",
-                    atom->name().c_str());
+                print_error(field,
+                            "PRIMARY_FIELD_FIRST_UID annotation is only for AttributionChains: "
+                            "'%s'\n",
+                            atomDecl->message.c_str());
                 errorCount++;
-                continue;
             } else {
                 atomDecl->primaryFields.push_back(FIRST_UID_IN_CHAIN_ID);
             }
         }
 
         if (option == STATE_OPTION_EXCLUSIVE) {
-            if (javaType == JAVA_TYPE_UNKNOWN ||
-                    javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
-                    javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
-                print_error(
-                    field,
-                    "Invalid exclusive state field: '%s'\n",
-                    atom->name().c_str());
+            if (javaType == JAVA_TYPE_UNKNOWN || javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
+                javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
+                print_error(field, "Invalid exclusive state field: '%s'\n",
+                            atomDecl->message.c_str());
                 errorCount++;
-                continue;
             }
 
             if (atomDecl->exclusiveField == 0) {
-                atomDecl->exclusiveField = it->first;
+                atomDecl->exclusiveField = fieldNumber;
             } else {
-                print_error(
-                    field,
-                    "Cannot have more than one exclusive state field in an atom: '%s'\n",
-                    atom->name().c_str());
+                print_error(field,
+                            "Cannot have more than one exclusive state field in an "
+                            "atom: '%s'\n",
+                            atomDecl->message.c_str());
                 errorCount++;
-                continue;
             }
 
             if (field->options()
                         .GetExtension(os::statsd::state_field_option)
                         .has_default_state_value()) {
-                const int defaultState =
-                        field->options().GetExtension(os::statsd::state_field_option)
-                        .default_state_value();
+                const int defaultState = field->options()
+                                                 .GetExtension(os::statsd::state_field_option)
+                                                 .default_state_value();
                 atomDecl->defaultState = defaultState;
 
-                addAnnotationToAtomDecl(atomDecl, signature->size(), ANNOTATION_ID_DEFAULT_STATE,
-                        ANNOTATION_TYPE_INT, AnnotationValue(defaultState));
+                addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_DEFAULT_STATE,
+                                        ANNOTATION_TYPE_INT, AnnotationValue(defaultState));
             }
 
-            if (field->options().GetExtension(os::statsd::state_field_option)
-                    .has_reset_state_value()) {
-                const int resetState = field->options()
+            if (field->options()
                         .GetExtension(os::statsd::state_field_option)
-                        .reset_state_value();
+                        .has_reset_state_value()) {
+                const int resetState = field->options()
+                                               .GetExtension(os::statsd::state_field_option)
+                                               .reset_state_value();
 
                 atomDecl->resetState = resetState;
-                addAnnotationToAtomDecl(atomDecl, signature->size(), ANNOTATION_ID_RESET_STATE,
-                        ANNOTATION_TYPE_INT, AnnotationValue(resetState));
+                addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_RESET_STATE,
+                                        ANNOTATION_TYPE_INT, AnnotationValue(resetState));
             }
 
-            if (field->options().GetExtension(os::statsd::state_field_option)
-                    .has_nested()) {
+            if (field->options().GetExtension(os::statsd::state_field_option).has_nested()) {
                 const bool nested =
                         field->options().GetExtension(os::statsd::state_field_option).nested();
                 atomDecl->nested = nested;
 
-                addAnnotationToAtomDecl(atomDecl, signature->size(), ANNOTATION_ID_STATE_NESTED,
-                        ANNOTATION_TYPE_BOOL, AnnotationValue(nested));
+                addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_STATE_NESTED,
+                                        ANNOTATION_TYPE_BOOL, AnnotationValue(nested));
             }
         }
-
     }
+
     if (field->options().GetExtension(os::statsd::is_uid) == true) {
         if (javaType != JAVA_TYPE_INT) {
-            print_error(
-                field,
-                "is_uid annotation can only be applied to int32 fields: '%s'\n",
-                atom->name().c_str());
+            print_error(field, "is_uid annotation can only be applied to int32 fields: '%s'\n",
+                        atomDecl->message.c_str());
             errorCount++;
-            continue;
         }
 
         if (atomDecl->uidField == 0) {
-            atomDecl->uidField = it->first;
+            atomDecl->uidField = fieldNumber;
 
-            addAnnotationToAtomDecl(atomDecl, signature->size(), ANNOTATION_ID_IS_UID,
-                    ANNOTATION_TYPE_BOOL, AnnotationValue(true));
+            addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_IS_UID,
+                                    ANNOTATION_TYPE_BOOL, AnnotationValue(true));
         } else {
-            print_error(
-                field,
-                "Cannot have more than one field in an atom with is_uid annotation: '%s'\n",
-                atom->name().c_str());
+            print_error(field,
+                        "Cannot have more than one field in an atom with is_uid "
+                        "annotation: '%s'\n",
+                        atomDecl->message.c_str());
+            errorCount++;
+        }
+    }
+
+    return errorCount;
+}
+
+/**
+ * Gather the info about an atom proto.
+ */
+int collate_atom(const Descriptor* atom, AtomDecl* atomDecl, vector<java_type_t>* signature) {
+    int errorCount = 0;
+
+    // Build a sorted list of the fields. Descriptor has them in source file
+    // order.
+    map<int, const FieldDescriptor*> fields;
+    for (int j = 0; j < atom->field_count(); j++) {
+        const FieldDescriptor* field = atom->field(j);
+        fields[field->number()] = field;
+    }
+
+    // Check that the parameters start at 1 and go up sequentially.
+    int expectedNumber = 1;
+    for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
+         it++) {
+        const int number = it->first;
+        const FieldDescriptor* field = it->second;
+        if (number != expectedNumber) {
+            print_error(field,
+                        "Fields must be numbered consecutively starting at 1:"
+                        " '%s' is %d but should be %d\n",
+                        field->name().c_str(), number, expectedNumber);
+            errorCount++;
+            expectedNumber = number;
+            continue;
+        }
+        expectedNumber++;
+    }
+
+    // Check that only allowed types are present. Remove any invalid ones.
+    for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
+         it++) {
+        const FieldDescriptor* field = it->second;
+        bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) ==
+                             os::statsd::LogMode::MODE_BYTES;
+
+        java_type_t javaType = java_type(field);
+
+        if (javaType == JAVA_TYPE_UNKNOWN) {
+            print_error(field, "Unknown type for field: %s\n", field->name().c_str());
+            errorCount++;
+            continue;
+        } else if (javaType == JAVA_TYPE_OBJECT && atomDecl->code < PULL_ATOM_START_ID) {
+            // Allow attribution chain, but only at position 1.
+            print_error(field, "Message type not allowed for field in pushed atoms: %s\n",
+                        field->name().c_str());
+            errorCount++;
+            continue;
+        } else if (javaType == JAVA_TYPE_BYTE_ARRAY && !isBinaryField) {
+            print_error(field, "Raw bytes type not allowed for field: %s\n", field->name().c_str());
+            errorCount++;
+            continue;
+        }
+
+        if (isBinaryField && javaType != JAVA_TYPE_BYTE_ARRAY) {
+            print_error(field, "Cannot mark field %s as bytes.\n", field->name().c_str());
+            errorCount++;
+            continue;
+        }
+
+        // Doubles are not supported yet.
+        if (javaType == JAVA_TYPE_DOUBLE) {
+            print_error(field,
+                        "Doubles are not supported in atoms. Please change field %s "
+                        "to float\n",
+                        field->name().c_str());
+            errorCount++;
+            continue;
+        }
+
+        if (field->is_repeated() &&
+            !(javaType == JAVA_TYPE_ATTRIBUTION_CHAIN || javaType == JAVA_TYPE_KEY_VALUE_PAIR)) {
+            print_error(field,
+                        "Repeated fields are not supported in atoms. Please make "
+                        "field %s not "
+                        "repeated.\n",
+                        field->name().c_str());
             errorCount++;
             continue;
         }
     }
-  }
 
-  return errorCount;
+    // Check that if there's an attribution chain, it's at position 1.
+    for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
+         it++) {
+        int number = it->first;
+        if (number != 1) {
+            const FieldDescriptor* field = it->second;
+            java_type_t javaType = java_type(field);
+            if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                print_error(field,
+                            "AttributionChain fields must have field id 1, in message: '%s'\n",
+                            atom->name().c_str());
+                errorCount++;
+            }
+        }
+    }
+
+    // Build the type signature and the atom data.
+    for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
+         it++) {
+        const FieldDescriptor* field = it->second;
+        java_type_t javaType = java_type(field);
+        bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) ==
+                             os::statsd::LogMode::MODE_BYTES;
+
+        AtomField atField(field->name(), javaType);
+
+        if (javaType == JAVA_TYPE_ENUM) {
+            // All enums are treated as ints when it comes to function signatures.
+            collate_enums(*field->enum_type(), &atField);
+        }
+
+        // Generate signature for pushed atoms
+        if (atomDecl->code < PULL_ATOM_START_ID) {
+            if (javaType == JAVA_TYPE_ENUM) {
+                // All enums are treated as ints when it comes to function signatures.
+                signature->push_back(JAVA_TYPE_INT);
+            } else if (javaType == JAVA_TYPE_OBJECT && isBinaryField) {
+                signature->push_back(JAVA_TYPE_BYTE_ARRAY);
+            } else {
+                signature->push_back(javaType);
+            }
+        }
+
+        atomDecl->fields.push_back(atField);
+
+        errorCount += collate_field_annotations(atomDecl, field, it->first, javaType);
+    }
+
+    return errorCount;
 }
 
-// This function flattens the fields of the AttributionNode proto in an Atom proto and generates
-// the corresponding atom decl and signature.
-bool get_non_chained_node(const Descriptor *atom, AtomDecl *atomDecl,
-                          vector<java_type_t> *signature) {
+// This function flattens the fields of the AttributionNode proto in an Atom
+// proto and generates the corresponding atom decl and signature.
+bool get_non_chained_node(const Descriptor* atom, AtomDecl* atomDecl,
+                          vector<java_type_t>* signature) {
     // Build a sorted list of the fields. Descriptor has them in source file
     // order.
-    map<int, const FieldDescriptor *> fields;
+    map<int, const FieldDescriptor*> fields;
     for (int j = 0; j < atom->field_count(); j++) {
-        const FieldDescriptor *field = atom->field(j);
+        const FieldDescriptor* field = atom->field(j);
         fields[field->number()] = field;
     }
 
     AtomDecl attributionDecl;
     vector<java_type_t> attributionSignature;
-    collate_atom(android::os::statsd::AttributionNode::descriptor(),
-                 &attributionDecl, &attributionSignature);
+    collate_atom(android::os::statsd::AttributionNode::descriptor(), &attributionDecl,
+                 &attributionSignature);
 
     // Build the type signature and the atom data.
     bool has_attribution_node = false;
-    for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
-        it != fields.end(); it++) {
-        const FieldDescriptor *field = it->second;
+    for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
+         it++) {
+        const FieldDescriptor* field = it->second;
         java_type_t javaType = java_type(field);
         if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
-            atomDecl->fields.insert(
-                atomDecl->fields.end(),
-                attributionDecl.fields.begin(), attributionDecl.fields.end());
-            signature->insert(
-                signature->end(),
-                attributionSignature.begin(), attributionSignature.end());
+            atomDecl->fields.insert(atomDecl->fields.end(), attributionDecl.fields.begin(),
+                                    attributionDecl.fields.end());
+            signature->insert(signature->end(), attributionSignature.begin(),
+                              attributionSignature.end());
             has_attribution_node = true;
 
         } else {
@@ -473,118 +454,129 @@
     return has_attribution_node;
 }
 
-static void populateFieldNumberToAnnotations(
-        const AtomDecl& atomDecl,
-        FieldNumberToAnnotations* fieldNumberToAnnotations) {
+static void populateFieldNumberToAnnotations(const AtomDecl& atomDecl,
+                                             FieldNumberToAnnotations* fieldNumberToAnnotations) {
     for (FieldNumberToAnnotations::const_iterator it = atomDecl.fieldNumberToAnnotations.begin();
-            it != atomDecl.fieldNumberToAnnotations.end(); it++) {
+         it != atomDecl.fieldNumberToAnnotations.end(); it++) {
         const int fieldNumber = it->first;
         const set<shared_ptr<Annotation>>& insertAnnotationsSource = it->second;
         set<shared_ptr<Annotation>>& insertAnnotationsTarget =
                 (*fieldNumberToAnnotations)[fieldNumber];
-        insertAnnotationsTarget.insert(
-                insertAnnotationsSource.begin(),
-                insertAnnotationsSource.end());
+        insertAnnotationsTarget.insert(insertAnnotationsSource.begin(),
+                                       insertAnnotationsSource.end());
     }
 }
 
 /**
  * Gather the info about the atoms.
  */
-int collate_atoms(const Descriptor *descriptor, const string& moduleName, Atoms *atoms) {
-  int errorCount = 0;
+int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms* atoms) {
+    int errorCount = 0;
 
-  int maxPushedAtomId = 2;
-  for (int i = 0; i < descriptor->field_count(); i++) {
-    const FieldDescriptor *atomField = descriptor->field(i);
+    int maxPushedAtomId = 2;
+    for (int i = 0; i < descriptor->field_count(); i++) {
+        const FieldDescriptor* atomField = descriptor->field(i);
 
-    if (moduleName != DEFAULT_MODULE_NAME) {
-        const int moduleCount = atomField->options().ExtensionSize(os::statsd::module);
-        int j;
-        for (j = 0; j < moduleCount; ++j) {
-            const string atomModuleName = atomField->options().GetExtension(os::statsd::module, j);
-            if (atomModuleName == moduleName) {
-                break;
+        if (moduleName != DEFAULT_MODULE_NAME) {
+            const int moduleCount = atomField->options().ExtensionSize(os::statsd::module);
+            int j;
+            for (j = 0; j < moduleCount; ++j) {
+                const string atomModuleName =
+                        atomField->options().GetExtension(os::statsd::module, j);
+                if (atomModuleName == moduleName) {
+                    break;
+                }
+            }
+
+            // This atom is not in the module we're interested in; skip it.
+            if (moduleCount == j) {
+                if (dbg) {
+                    printf("   Skipping %s (%d)\n", atomField->name().c_str(), atomField->number());
+                }
+                continue;
             }
         }
 
-        // This atom is not in the module we're interested in; skip it.
-        if (moduleCount == j) {
-            if (dbg) {
-              printf("   Skipping %s (%d)\n", atomField->name().c_str(), atomField->number());
-            }
+        if (dbg) {
+            printf("   %s (%d)\n", atomField->name().c_str(), atomField->number());
+        }
+
+        // StatsEvent only has one oneof, which contains only messages. Don't allow
+        // other types.
+        if (atomField->type() != FieldDescriptor::TYPE_MESSAGE) {
+            print_error(atomField,
+                        "Bad type for atom. StatsEvent can only have message type "
+                        "fields: %s\n",
+                        atomField->name().c_str());
+            errorCount++;
             continue;
         }
+
+        const Descriptor* atom = atomField->message_type();
+        AtomDecl atomDecl(atomField->number(), atomField->name(), atom->name());
+
+        if (atomField->options().GetExtension(os::statsd::allow_from_any_uid) == true) {
+            atomDecl.whitelisted = true;
+            if (dbg) {
+                printf("%s is whitelisted\n", atomField->name().c_str());
+            }
+        }
+
+        if (atomDecl.code < PULL_ATOM_START_ID &&
+            atomField->options().GetExtension(os::statsd::truncate_timestamp)) {
+            addAnnotationToAtomDecl(&atomDecl, ATOM_ID_FIELD_NUMBER,
+                                    ANNOTATION_ID_TRUNCATE_TIMESTAMP, ANNOTATION_TYPE_BOOL,
+                                    AnnotationValue(true));
+            if (dbg) {
+                printf("%s can have timestamp truncated\n", atomField->name().c_str());
+            }
+        }
+
+        vector<java_type_t> signature;
+        errorCount += collate_atom(atom, &atomDecl, &signature);
+        if (atomDecl.primaryFields.size() != 0 && atomDecl.exclusiveField == 0) {
+            print_error(atomField, "Cannot have a primary field without an exclusive field: %s\n",
+                        atomField->name().c_str());
+            errorCount++;
+            continue;
+        }
+
+        atoms->decls.insert(atomDecl);
+        FieldNumberToAnnotations& fieldNumberToAnnotations = atoms->signatureInfoMap[signature];
+        populateFieldNumberToAnnotations(atomDecl, &fieldNumberToAnnotations);
+
+        AtomDecl nonChainedAtomDecl(atomField->number(), atomField->name(), atom->name());
+        vector<java_type_t> nonChainedSignature;
+        if (get_non_chained_node(atom, &nonChainedAtomDecl, &nonChainedSignature)) {
+            atoms->non_chained_decls.insert(nonChainedAtomDecl);
+            FieldNumberToAnnotations& fieldNumberToAnnotations =
+                    atoms->nonChainedSignatureInfoMap[nonChainedSignature];
+            populateFieldNumberToAnnotations(atomDecl, &fieldNumberToAnnotations);
+        }
+
+        if (atomDecl.code < PULL_ATOM_START_ID && atomDecl.code > maxPushedAtomId) {
+            maxPushedAtomId = atomDecl.code;
+        }
     }
 
+    atoms->maxPushedAtomId = maxPushedAtomId;
+
     if (dbg) {
-      printf("   %s (%d)\n", atomField->name().c_str(), atomField->number());
+        printf("signatures = [\n");
+        for (map<vector<java_type_t>, FieldNumberToAnnotations>::const_iterator it =
+                     atoms->signatureInfoMap.begin();
+             it != atoms->signatureInfoMap.end(); it++) {
+            printf("   ");
+            for (vector<java_type_t>::const_iterator jt = it->first.begin(); jt != it->first.end();
+                 jt++) {
+                printf(" %d", (int)*jt);
+            }
+            printf("\n");
+        }
+        printf("]\n");
     }
 
-    // StatsEvent only has one oneof, which contains only messages. Don't allow
-    // other types.
-    if (atomField->type() != FieldDescriptor::TYPE_MESSAGE) {
-      print_error(atomField,
-                  "Bad type for atom. StatsEvent can only have message type "
-                  "fields: %s\n",
-                  atomField->name().c_str());
-      errorCount++;
-      continue;
-    }
-
-    const Descriptor *atom = atomField->message_type();
-    AtomDecl atomDecl(atomField->number(), atomField->name(), atom->name());
-
-    if (atomField->options().GetExtension(os::statsd::allow_from_any_uid) == true) {
-        atomDecl.whitelisted = true;
-    }
-
-    vector<java_type_t> signature;
-    errorCount += collate_atom(atom, &atomDecl, &signature);
-    if (atomDecl.primaryFields.size() != 0 && atomDecl.exclusiveField == 0) {
-        print_error(atomField,
-                  "Cannot have a primary field without an exclusive field: %s\n",
-                  atomField->name().c_str());
-        errorCount++;
-        continue;
-    }
-
-    atoms->decls.insert(atomDecl);
-    FieldNumberToAnnotations& fieldNumberToAnnotations = atoms->signatureInfoMap[signature];
-    populateFieldNumberToAnnotations(atomDecl, &fieldNumberToAnnotations);
-
-    AtomDecl nonChainedAtomDecl(atomField->number(), atomField->name(), atom->name());
-    vector<java_type_t> nonChainedSignature;
-    if (get_non_chained_node(atom, &nonChainedAtomDecl, &nonChainedSignature)) {
-        atoms->non_chained_decls.insert(nonChainedAtomDecl);
-        FieldNumberToAnnotations& fieldNumberToAnnotations =
-                atoms->nonChainedSignatureInfoMap[nonChainedSignature];
-        populateFieldNumberToAnnotations(atomDecl, &fieldNumberToAnnotations);
-    }
-
-    if (atomDecl.code < PULL_ATOM_START_ID && atomDecl.code > maxPushedAtomId) {
-        maxPushedAtomId = atomDecl.code;
-    }
-  }
-
-  atoms->maxPushedAtomId = maxPushedAtomId;
-
-  if (dbg) {
-    printf("signatures = [\n");
-    for (map<vector<java_type_t>, FieldNumberToAnnotations>::const_iterator it =
-             atoms->signatureInfoMap.begin();
-         it != atoms->signatureInfoMap.end(); it++) {
-      printf("   ");
-      for (vector<java_type_t>::const_iterator jt = it->first.begin();
-           jt != it->first.end(); jt++){
-        printf(" %d", (int)*jt);
-      }
-      printf("\n");
-    }
-    printf("]\n");
-  }
-
-  return errorCount;
+    return errorCount;
 }
 
 }  // namespace stats_log_api_gen
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index c6dad1d..2aedb21 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -17,24 +17,24 @@
 #ifndef ANDROID_STATS_LOG_API_GEN_COLLATION_H
 #define ANDROID_STATS_LOG_API_GEN_COLLATION_H
 
-
 #include <google/protobuf/descriptor.h>
-#include "frameworks/base/cmds/statsd/src/atom_field_options.pb.h"
 
+#include <map>
 #include <set>
 #include <vector>
-#include <map>
+
+#include "frameworks/base/cmds/statsd/src/atom_field_options.pb.h"
 
 namespace android {
 namespace stats_log_api_gen {
 
+using google::protobuf::Descriptor;
+using google::protobuf::FieldDescriptor;
 using std::map;
 using std::set;
 using std::shared_ptr;
 using std::string;
 using std::vector;
-using google::protobuf::Descriptor;
-using google::protobuf::FieldDescriptor;
 
 const int PULL_ATOM_START_ID = 10000;
 
@@ -52,26 +52,28 @@
 const int STATE_OPTION_PRIMARY_FIELD_FIRST_UID = os::statsd::StateField::PRIMARY_FIELD_FIRST_UID;
 const int STATE_OPTION_PRIMARY = os::statsd::StateField::PRIMARY_FIELD;
 
+const int ATOM_ID_FIELD_NUMBER = -1;
+
 const string DEFAULT_MODULE_NAME = "DEFAULT";
 
 /**
  * The types for atom parameters.
  */
 typedef enum {
-  JAVA_TYPE_UNKNOWN = 0,
+    JAVA_TYPE_UNKNOWN = 0,
 
-  JAVA_TYPE_ATTRIBUTION_CHAIN = 1,
-  JAVA_TYPE_BOOLEAN = 2,
-  JAVA_TYPE_INT = 3,
-  JAVA_TYPE_LONG = 4,
-  JAVA_TYPE_FLOAT = 5,
-  JAVA_TYPE_DOUBLE = 6,
-  JAVA_TYPE_STRING = 7,
-  JAVA_TYPE_ENUM = 8,
-  JAVA_TYPE_KEY_VALUE_PAIR = 9,
+    JAVA_TYPE_ATTRIBUTION_CHAIN = 1,
+    JAVA_TYPE_BOOLEAN = 2,
+    JAVA_TYPE_INT = 3,
+    JAVA_TYPE_LONG = 4,
+    JAVA_TYPE_FLOAT = 5,
+    JAVA_TYPE_DOUBLE = 6,
+    JAVA_TYPE_STRING = 7,
+    JAVA_TYPE_ENUM = 8,
+    JAVA_TYPE_KEY_VALUE_PAIR = 9,
 
-  JAVA_TYPE_OBJECT = -1,
-  JAVA_TYPE_BYTE_ARRAY = -2,
+    JAVA_TYPE_OBJECT = -1,
+    JAVA_TYPE_BYTE_ARRAY = -2,
 } java_type_t;
 
 enum AnnotationType {
@@ -84,8 +86,10 @@
     int intValue;
     bool boolValue;
 
-    AnnotationValue(const int value): intValue(value) {}
-    AnnotationValue(const bool value): boolValue(value) {}
+    AnnotationValue(const int value) : intValue(value) {
+    }
+    AnnotationValue(const bool value) : boolValue(value) {
+    }
 };
 
 struct Annotation {
@@ -95,16 +99,18 @@
     AnnotationValue value;
 
     inline Annotation(unsigned char annotationId, int atomId, AnnotationType type,
-            AnnotationValue value):
-            annotationId(annotationId), atomId(atomId), type(type), value(value) {}
-    inline ~Annotation() {}
+                      AnnotationValue value)
+        : annotationId(annotationId), atomId(atomId), type(type), value(value) {
+    }
+    inline ~Annotation() {
+    }
 
     inline bool operator<(const Annotation& that) const {
         return atomId == that.atomId ? annotationId < that.annotationId : atomId < that.atomId;
     }
 };
 
-using FieldNumberToAnnotations =  map<int, set<shared_ptr<Annotation>>>;
+using FieldNumberToAnnotations = map<int, set<shared_ptr<Annotation>>>;
 
 /**
  * The name and type for an atom field.
@@ -113,16 +119,20 @@
     string name;
     java_type_t javaType;
 
-    // If the field is of type enum, the following map contains the list of enum values.
+    // If the field is of type enum, the following map contains the list of enum
+    // values.
     map<int /* numeric value */, string /* value name */> enumValues;
 
-    inline AtomField() :name(), javaType(JAVA_TYPE_UNKNOWN) {}
-    inline AtomField(const AtomField& that) :name(that.name),
-                                             javaType(that.javaType),
-                                             enumValues(that.enumValues) {}
+    inline AtomField() : name(), javaType(JAVA_TYPE_UNKNOWN) {
+    }
+    inline AtomField(const AtomField& that)
+        : name(that.name), javaType(that.javaType), enumValues(that.enumValues) {
+    }
 
-    inline AtomField(string n, java_type_t jt) :name(n), javaType(jt) {}
-    inline ~AtomField() {}
+    inline AtomField(string n, java_type_t jt) : name(n), javaType(jt) {
+    }
+    inline ~AtomField() {
+    }
 };
 
 /**
@@ -147,6 +157,8 @@
 
     bool whitelisted = false;
 
+    bool truncateTimestamp = false;
+
     AtomDecl();
     AtomDecl(const AtomDecl& that);
     AtomDecl(int code, const string& name, const string& message);
@@ -169,10 +181,9 @@
  * Gather the information about the atoms.  Returns the number of errors.
  */
 int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms* atoms);
-int collate_atom(const Descriptor *atom, AtomDecl *atomDecl, vector<java_type_t> *signature);
+int collate_atom(const Descriptor* atom, AtomDecl* atomDecl, vector<java_type_t>* signature);
 
 }  // namespace stats_log_api_gen
 }  // namespace android
 
-
-#endif // ANDROID_STATS_LOG_API_GEN_COLLATION_H
+#endif  // ANDROID_STATS_LOG_API_GEN_COLLATION_H
diff --git a/tools/stats_log_api_gen/atoms_info_writer.cpp b/tools/stats_log_api_gen/atoms_info_writer.cpp
index 4f66f68..6944752 100644
--- a/tools/stats_log_api_gen/atoms_info_writer.cpp
+++ b/tools/stats_log_api_gen/atoms_info_writer.cpp
@@ -15,12 +15,13 @@
  */
 
 #include "atoms_info_writer.h"
-#include "utils.h"
 
 #include <map>
 #include <set>
 #include <vector>
 
+#include "utils.h"
+
 namespace android {
 namespace stats_log_api_gen {
 
@@ -42,32 +43,27 @@
             "  const static std::set<int> "
             "kTruncatingTimestampAtomBlackList;\n");
     fprintf(out, "  const static std::map<int, int> kAtomsWithUidField;\n");
-    fprintf(out,
-            "  const static std::set<int> kAtomsWithAttributionChain;\n");
+    fprintf(out, "  const static std::set<int> kAtomsWithAttributionChain;\n");
     fprintf(out,
             "  const static std::map<int, StateAtomFieldOptions> "
             "kStateAtomsFieldOptions;\n");
-    fprintf(out,
-            "  const static std::set<int> kWhitelistedAtoms;\n");
+    fprintf(out, "  const static std::set<int> kWhitelistedAtoms;\n");
     fprintf(out, "};\n");
     fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n", atoms.maxPushedAtomId);
-
 }
 
 static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) {
-    std::set<string> kTruncatingAtomNames = {
-            "mobile_radio_power_state_changed",
-            "audio_state_changed",
-            "call_state_changed",
-            "phone_signal_strength_changed",
-            "mobile_bytes_transfer_by_fg_bg",
-            "mobile_bytes_transfer"
-    };
+    std::set<string> kTruncatingAtomNames = {"mobile_radio_power_state_changed",
+                                             "audio_state_changed",
+                                             "call_state_changed",
+                                             "phone_signal_strength_changed",
+                                             "mobile_bytes_transfer_by_fg_bg",
+                                             "mobile_bytes_transfer"};
     fprintf(out,
             "const std::set<int> "
             "AtomsInfo::kTruncatingTimestampAtomBlackList = {\n");
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-         atom != atoms.decls.end(); atom++) {
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end();
+         atom++) {
         if (kTruncatingAtomNames.find(atom->name) != kTruncatingAtomNames.end()) {
             const string constant = make_constant_name(atom->name);
             fprintf(out, "    %d, // %s\n", atom->code, constant.c_str());
@@ -77,10 +73,9 @@
     fprintf(out, "};\n");
     fprintf(out, "\n");
 
-    fprintf(out,
-            "const std::set<int> AtomsInfo::kAtomsWithAttributionChain = {\n");
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-         atom != atoms.decls.end(); atom++) {
+    fprintf(out, "const std::set<int> AtomsInfo::kAtomsWithAttributionChain = {\n");
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end();
+         atom++) {
         for (vector<AtomField>::const_iterator field = atom->fields.begin();
              field != atom->fields.end(); field++) {
             if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
@@ -94,10 +89,9 @@
     fprintf(out, "};\n");
     fprintf(out, "\n");
 
-    fprintf(out,
-            "const std::set<int> AtomsInfo::kWhitelistedAtoms = {\n");
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-         atom != atoms.decls.end(); atom++) {
+    fprintf(out, "const std::set<int> AtomsInfo::kWhitelistedAtoms = {\n");
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end();
+         atom++) {
         if (atom->whitelisted) {
             const string constant = make_constant_name(atom->name);
             fprintf(out, "    %d, // %s\n", atom->code, constant.c_str());
@@ -109,8 +103,8 @@
 
     fprintf(out, "static std::map<int, int> getAtomUidField() {\n");
     fprintf(out, "    std::map<int, int> uidField;\n");
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-         atom != atoms.decls.end(); atom++) {
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end();
+         atom++) {
         if (atom->uidField == 0) {
             continue;
         }
@@ -118,8 +112,8 @@
                 "\n    // Adding uid field for atom "
                 "(%d)%s\n",
                 atom->code, atom->name.c_str());
-        fprintf(out, "    uidField[%d /* %s */] = %d;\n",
-                atom->code, make_constant_name(atom->name).c_str(), atom->uidField);
+        fprintf(out, "    uidField[%d /* %s */] = %d;\n", atom->code,
+                make_constant_name(atom->name).c_str(), atom->uidField);
     }
 
     fprintf(out, "    return uidField;\n");
@@ -134,8 +128,8 @@
             "getStateAtomFieldOptions() {\n");
     fprintf(out, "    std::map<int, StateAtomFieldOptions> options;\n");
     fprintf(out, "    StateAtomFieldOptions* opt;\n");
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-         atom != atoms.decls.end(); atom++) {
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end();
+         atom++) {
         if (atom->primaryFields.size() == 0 && atom->exclusiveField == 0) {
             continue;
         }
@@ -143,8 +137,8 @@
                 "\n    // Adding primary and exclusive fields for atom "
                 "(%d)%s\n",
                 atom->code, atom->name.c_str());
-        fprintf(out, "    opt = &(options[%d /* %s */]);\n",
-                atom->code, make_constant_name(atom->name).c_str());
+        fprintf(out, "    opt = &(options[%d /* %s */]);\n", atom->code,
+                make_constant_name(atom->name).c_str());
         fprintf(out, "    opt->primaryFields.reserve(%lu);\n", atom->primaryFields.size());
         for (const auto& field : atom->primaryFields) {
             fprintf(out, "    opt->primaryFields.push_back(%d);\n", field);
@@ -174,7 +168,7 @@
             "getStateAtomFieldOptions();\n");
 }
 
-int write_atoms_info_header(FILE* out, const Atoms &atoms, const string& namespaceStr) {
+int write_atoms_info_header(FILE* out, const Atoms& atoms, const string& namespaceStr) {
     // Print prelude
     fprintf(out, "// This file is autogenerated\n");
     fprintf(out, "\n");
@@ -195,8 +189,8 @@
     return 0;
 }
 
-int write_atoms_info_cpp(FILE *out, const Atoms &atoms, const string& namespaceStr,
-        const string& importHeader) {
+int write_atoms_info_cpp(FILE* out, const Atoms& atoms, const string& namespaceStr,
+                         const string& importHeader) {
     // Print prelude
     fprintf(out, "// This file is autogenerated\n");
     fprintf(out, "\n");
diff --git a/tools/stats_log_api_gen/atoms_info_writer.h b/tools/stats_log_api_gen/atoms_info_writer.h
index d04e65a..ffe9e43 100644
--- a/tools/stats_log_api_gen/atoms_info_writer.h
+++ b/tools/stats_log_api_gen/atoms_info_writer.h
@@ -16,18 +16,18 @@
 
 #pragma once
 
-#include "Collation.h"
-
 #include <stdio.h>
 #include <string.h>
 
+#include "Collation.h"
+
 namespace android {
 namespace stats_log_api_gen {
 
 using namespace std;
 
 int write_atoms_info_cpp(FILE* out, const Atoms& atoms, const string& namespaceStr,
-        const string& importHeader);
+                         const string& importHeader);
 
 int write_atoms_info_header(FILE* out, const Atoms& atoms, const string& namespaceStr);
 
diff --git a/tools/stats_log_api_gen/java_writer.cpp b/tools/stats_log_api_gen/java_writer.cpp
index 18508d2..5a22b5c 100644
--- a/tools/stats_log_api_gen/java_writer.cpp
+++ b/tools/stats_log_api_gen/java_writer.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "java_writer.h"
+
 #include "java_writer_q.h"
 #include "utils.h"
 
@@ -22,9 +23,8 @@
 namespace stats_log_api_gen {
 
 static int write_java_q_logger_class(
-        FILE* out,
-        const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap,
-        const AtomDecl &attributionDecl) {
+        FILE* out, const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap,
+        const AtomDecl& attributionDecl) {
     fprintf(out, "\n");
     fprintf(out, "    // Write logging helper methods for statsd in Q and earlier.\n");
     fprintf(out, "    private static class QLogger {\n");
@@ -34,29 +34,27 @@
     // Print Q write methods.
     fprintf(out, "\n");
     fprintf(out, "        // Write methods.\n");
-    write_java_methods_q_schema(
-            out, signatureInfoMap, attributionDecl, "        ");
+    write_java_methods_q_schema(out, signatureInfoMap, attributionDecl, "        ");
 
     fprintf(out, "    }\n");
     return 0;
 }
 
-static void write_annotations(
-        FILE* out, int argIndex,
-        const FieldNumberToAnnotations& fieldNumberToAnnotations) {
+static void write_annotations(FILE* out, int argIndex,
+                              const FieldNumberToAnnotations& fieldNumberToAnnotations) {
     auto it = fieldNumberToAnnotations.find(argIndex);
     if (it == fieldNumberToAnnotations.end()) {
         return;
     }
     const set<shared_ptr<Annotation>>& annotations = it->second;
-    for (auto& annotation: annotations) {
+    for (auto& annotation : annotations) {
         // TODO(b/151744250): Group annotations for same atoms.
         // TODO(b/151786433): Write atom constant name instead of atom id literal.
         fprintf(out, "        if (code == %d) {\n", annotation->atomId);
-        switch(annotation->type) {
+        switch (annotation->type) {
             case ANNOTATION_TYPE_INT:
-                // TODO(b/151776731): Check for reset state annotation and only include reset state
-                // when field value == default state annotation value.
+                // TODO(b/151776731): Check for reset state annotation and only include
+                // reset state when field value == default state annotation value.
                 // TODO(b/151786433): Write annotation constant name instead of
                 // annotation id literal.
                 fprintf(out, "            builder.addIntAnnotation((byte) %d, %d);\n",
@@ -66,8 +64,7 @@
                 // TODO(b/151786433): Write annotation constant name instead of
                 // annotation id literal.
                 fprintf(out, "            builder.addBooleanAnnotation((byte) %d, %s);\n",
-                        annotation->annotationId,
-                        annotation->value.boolValue ? "true" : "false");
+                        annotation->annotationId, annotation->value.boolValue ? "true" : "false");
                 break;
             default:
                 break;
@@ -77,24 +74,21 @@
 }
 
 static int write_java_methods(
-        FILE* out,
-        const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap,
-        const AtomDecl &attributionDecl,
-        const bool supportQ
-        ) {
+        FILE* out, const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap,
+        const AtomDecl& attributionDecl, const bool supportQ) {
     for (auto signatureInfoMapIt = signatureInfoMap.begin();
-            signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
+         signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
         // Print method signature.
         fprintf(out, "    public static void write(int code");
         const vector<java_type_t>& signature = signatureInfoMapIt->first;
         const FieldNumberToAnnotations& fieldNumberToAnnotations = signatureInfoMapIt->second;
         int argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin();
-                arg != signature.end(); arg++) {
+        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+             arg++) {
             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
                 for (auto chainField : attributionDecl.fields) {
-                    fprintf(out, ", %s[] %s",
-                        java_type_name(chainField.javaType), chainField.name.c_str());
+                    fprintf(out, ", %s[] %s", java_type_name(chainField.javaType),
+                            chainField.name.c_str());
                 }
             } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
                 fprintf(out, ", android.util.SparseArray<Object> valueMap");
@@ -108,134 +102,134 @@
         // Print method body.
         string indent("");
         if (supportQ) {
-            // TODO(b/146235828): Use just SDK_INT check once it is incremented from Q.
+            // TODO(b/146235828): Use just SDK_INT check once it is incremented from
+            // Q.
             fprintf(out, "        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q\n");
-            fprintf(out, "                || (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q\n");
+            fprintf(out,
+                    "                || (Build.VERSION.SDK_INT == "
+                    "Build.VERSION_CODES.Q\n");
             fprintf(out, "                    && Build.VERSION.PREVIEW_SDK_INT > 0)) {\n");
             indent = "    ";
         }
 
         // Start StatsEvent.Builder.
-        fprintf(out, "%s        final StatsEvent.Builder builder = StatsEvent.newBuilder();\n",
+        fprintf(out,
+                "%s        final StatsEvent.Builder builder = "
+                "StatsEvent.newBuilder();\n",
                 indent.c_str());
 
         // Write atom code.
         fprintf(out, "%s        builder.setAtomId(code);\n", indent.c_str());
+        write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAnnotations);
 
         // Write the args.
         argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin();
-                arg != signature.end(); arg++) {
+        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+             arg++) {
             switch (*arg) {
-            case JAVA_TYPE_BOOLEAN:
-                fprintf(out, "%s        builder.writeBoolean(arg%d);\n", indent.c_str(), argIndex);
-                break;
-            case JAVA_TYPE_INT:
-            case JAVA_TYPE_ENUM:
-                fprintf(out, "%s        builder.writeInt(arg%d);\n", indent.c_str(), argIndex);
-                break;
-            case JAVA_TYPE_FLOAT:
-                fprintf(out, "%s        builder.writeFloat(arg%d);\n", indent.c_str(), argIndex);
-                break;
-            case JAVA_TYPE_LONG:
-                fprintf(out, "%s        builder.writeLong(arg%d);\n", indent.c_str(), argIndex);
-                break;
-            case JAVA_TYPE_STRING:
-                fprintf(out, "%s        builder.writeString(arg%d);\n", indent.c_str(), argIndex);
-                break;
-            case JAVA_TYPE_BYTE_ARRAY:
-                fprintf(out, "%s        builder.writeByteArray(null == arg%d ? new byte[0] : arg%d);\n",
-                        indent.c_str(), argIndex, argIndex);
-                break;
-            case JAVA_TYPE_ATTRIBUTION_CHAIN:
-            {
-                const char* uidName = attributionDecl.fields.front().name.c_str();
-                const char* tagName = attributionDecl.fields.back().name.c_str();
+                case JAVA_TYPE_BOOLEAN:
+                    fprintf(out, "%s        builder.writeBoolean(arg%d);\n", indent.c_str(),
+                            argIndex);
+                    break;
+                case JAVA_TYPE_INT:
+                case JAVA_TYPE_ENUM:
+                    fprintf(out, "%s        builder.writeInt(arg%d);\n", indent.c_str(), argIndex);
+                    break;
+                case JAVA_TYPE_FLOAT:
+                    fprintf(out, "%s        builder.writeFloat(arg%d);\n", indent.c_str(),
+                            argIndex);
+                    break;
+                case JAVA_TYPE_LONG:
+                    fprintf(out, "%s        builder.writeLong(arg%d);\n", indent.c_str(), argIndex);
+                    break;
+                case JAVA_TYPE_STRING:
+                    fprintf(out, "%s        builder.writeString(arg%d);\n", indent.c_str(),
+                            argIndex);
+                    break;
+                case JAVA_TYPE_BYTE_ARRAY:
+                    fprintf(out,
+                            "%s        builder.writeByteArray(null == arg%d ? new byte[0] : "
+                            "arg%d);\n",
+                            indent.c_str(), argIndex, argIndex);
+                    break;
+                case JAVA_TYPE_ATTRIBUTION_CHAIN: {
+                    const char* uidName = attributionDecl.fields.front().name.c_str();
+                    const char* tagName = attributionDecl.fields.back().name.c_str();
 
-                fprintf(out, "%s        builder.writeAttributionChain(\n", indent.c_str());
-                fprintf(out, "%s                null == %s ? new int[0] : %s,\n",
-                        indent.c_str(), uidName, uidName);
-                fprintf(out, "%s                null == %s ? new String[0] : %s);\n",
-                        indent.c_str(), tagName, tagName);
-                break;
-            }
-            case JAVA_TYPE_KEY_VALUE_PAIR:
-                fprintf(out, "\n");
-                fprintf(out,
-                        "%s        // Write KeyValuePairs.\n", indent.c_str());
-                fprintf(out,
-                        "%s        final int count = valueMap.size();\n", indent.c_str());
-                fprintf(out,
-                        "%s        android.util.SparseIntArray intMap = null;\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s        android.util.SparseLongArray longMap = null;\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s        android.util.SparseArray<String> stringMap = null;\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s        android.util.SparseArray<Float> floatMap = null;\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s        for (int i = 0; i < count; i++) {\n", indent.c_str());
-                fprintf(out,
-                        "%s            final int key = valueMap.keyAt(i);\n", indent.c_str());
-                fprintf(out,
-                        "%s            final Object value = valueMap.valueAt(i);\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s            if (value instanceof Integer) {\n", indent.c_str());
-                fprintf(out,
-                        "%s                if (null == intMap) {\n", indent.c_str());
-                fprintf(out,
-                        "%s                    intMap = new android.util.SparseIntArray();\n", indent.c_str());
-                fprintf(out,
-                        "%s                }\n", indent.c_str());
-                fprintf(out,
-                        "%s                intMap.put(key, (Integer) value);\n", indent.c_str());
-                fprintf(out,
-                        "%s            } else if (value instanceof Long) {\n", indent.c_str());
-                fprintf(out,
-                        "%s                if (null == longMap) {\n", indent.c_str());
-                fprintf(out,
-                        "%s                    longMap = new android.util.SparseLongArray();\n", indent.c_str());
-                fprintf(out,
-                        "%s                }\n", indent.c_str());
-                fprintf(out,
-                        "%s                longMap.put(key, (Long) value);\n", indent.c_str());
-                fprintf(out,
-                        "%s            } else if (value instanceof String) {\n", indent.c_str());
-                fprintf(out,
-                        "%s                if (null == stringMap) {\n", indent.c_str());
-                fprintf(out,
-                        "%s                    stringMap = new android.util.SparseArray<>();\n", indent.c_str());
-                fprintf(out,
-                        "%s                }\n", indent.c_str());
-                fprintf(out,
-                        "%s                stringMap.put(key, (String) value);\n", indent.c_str());
-                fprintf(out,
-                        "%s            } else if (value instanceof Float) {\n", indent.c_str());
-                fprintf(out,
-                        "%s                if (null == floatMap) {\n", indent.c_str());
-                fprintf(out,
-                        "%s                    floatMap = new android.util.SparseArray<>();\n", indent.c_str());
-                fprintf(out,
-                        "%s                }\n", indent.c_str());
-                fprintf(out,
-                        "%s                floatMap.put(key, (Float) value);\n", indent.c_str());
-                fprintf(out,
-                        "%s            }\n", indent.c_str());
-                fprintf(out,
-                        "%s        }\n", indent.c_str());
-                fprintf(out,
-                        "%s        builder.writeKeyValuePairs("
-                        "intMap, longMap, stringMap, floatMap);\n", indent.c_str());
-                break;
-            default:
-                // Unsupported types: OBJECT, DOUBLE.
-                fprintf(stderr, "Encountered unsupported type.");
-                return 1;
+                    fprintf(out, "%s        builder.writeAttributionChain(\n", indent.c_str());
+                    fprintf(out, "%s                null == %s ? new int[0] : %s,\n",
+                            indent.c_str(), uidName, uidName);
+                    fprintf(out, "%s                null == %s ? new String[0] : %s);\n",
+                            indent.c_str(), tagName, tagName);
+                    break;
+                }
+                case JAVA_TYPE_KEY_VALUE_PAIR:
+                    fprintf(out, "\n");
+                    fprintf(out, "%s        // Write KeyValuePairs.\n", indent.c_str());
+                    fprintf(out, "%s        final int count = valueMap.size();\n", indent.c_str());
+                    fprintf(out, "%s        android.util.SparseIntArray intMap = null;\n",
+                            indent.c_str());
+                    fprintf(out, "%s        android.util.SparseLongArray longMap = null;\n",
+                            indent.c_str());
+                    fprintf(out, "%s        android.util.SparseArray<String> stringMap = null;\n",
+                            indent.c_str());
+                    fprintf(out, "%s        android.util.SparseArray<Float> floatMap = null;\n",
+                            indent.c_str());
+                    fprintf(out, "%s        for (int i = 0; i < count; i++) {\n", indent.c_str());
+                    fprintf(out, "%s            final int key = valueMap.keyAt(i);\n",
+                            indent.c_str());
+                    fprintf(out, "%s            final Object value = valueMap.valueAt(i);\n",
+                            indent.c_str());
+                    fprintf(out, "%s            if (value instanceof Integer) {\n", indent.c_str());
+                    fprintf(out, "%s                if (null == intMap) {\n", indent.c_str());
+                    fprintf(out,
+                            "%s                    intMap = new "
+                            "android.util.SparseIntArray();\n",
+                            indent.c_str());
+                    fprintf(out, "%s                }\n", indent.c_str());
+                    fprintf(out, "%s                intMap.put(key, (Integer) value);\n",
+                            indent.c_str());
+                    fprintf(out, "%s            } else if (value instanceof Long) {\n",
+                            indent.c_str());
+                    fprintf(out, "%s                if (null == longMap) {\n", indent.c_str());
+                    fprintf(out,
+                            "%s                    longMap = new "
+                            "android.util.SparseLongArray();\n",
+                            indent.c_str());
+                    fprintf(out, "%s                }\n", indent.c_str());
+                    fprintf(out, "%s                longMap.put(key, (Long) value);\n",
+                            indent.c_str());
+                    fprintf(out, "%s            } else if (value instanceof String) {\n",
+                            indent.c_str());
+                    fprintf(out, "%s                if (null == stringMap) {\n", indent.c_str());
+                    fprintf(out,
+                            "%s                    stringMap = new "
+                            "android.util.SparseArray<>();\n",
+                            indent.c_str());
+                    fprintf(out, "%s                }\n", indent.c_str());
+                    fprintf(out, "%s                stringMap.put(key, (String) value);\n",
+                            indent.c_str());
+                    fprintf(out, "%s            } else if (value instanceof Float) {\n",
+                            indent.c_str());
+                    fprintf(out, "%s                if (null == floatMap) {\n", indent.c_str());
+                    fprintf(out,
+                            "%s                    floatMap = new "
+                            "android.util.SparseArray<>();\n",
+                            indent.c_str());
+                    fprintf(out, "%s                }\n", indent.c_str());
+                    fprintf(out, "%s                floatMap.put(key, (Float) value);\n",
+                            indent.c_str());
+                    fprintf(out, "%s            }\n", indent.c_str());
+                    fprintf(out, "%s        }\n", indent.c_str());
+                    fprintf(out,
+                            "%s        builder.writeKeyValuePairs("
+                            "intMap, longMap, stringMap, floatMap);\n",
+                            indent.c_str());
+                    break;
+                default:
+                    // Unsupported types: OBJECT, DOUBLE.
+                    fprintf(stderr, "Encountered unsupported type.");
+                    return 1;
             }
             write_annotations(out, argIndex, fieldNumberToAnnotations);
             argIndex++;
@@ -251,7 +245,7 @@
             fprintf(out, "            QLogger.write(code");
             argIndex = 1;
             for (vector<java_type_t>::const_iterator arg = signature.begin();
-                arg != signature.end(); arg++) {
+                 arg != signature.end(); arg++) {
                 if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
                     const char* uidName = attributionDecl.fields.front().name.c_str();
                     const char* tagName = attributionDecl.fields.back().name.c_str();
@@ -266,20 +260,18 @@
                 argIndex++;
             }
             fprintf(out, ");\n");
-            fprintf(out, "        }\n"); // if
+            fprintf(out, "        }\n");  // if
         }
 
-        fprintf(out, "    }\n"); // method
+        fprintf(out, "    }\n");  // method
         fprintf(out, "\n");
     }
     return 0;
-
 }
 
-int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl,
-                                    const string& javaClass,
-                                    const string& javaPackage, const bool supportQ,
-                                    const bool supportWorkSource) {
+int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
+                         const string& javaClass, const string& javaPackage, const bool supportQ,
+                         const bool supportWorkSource) {
     // Print prelude
     fprintf(out, "// This file is autogenerated\n");
     fprintf(out, "\n");
@@ -308,17 +300,14 @@
 
     // Print write methods.
     fprintf(out, "    // Write methods\n");
-    errors += write_java_methods(
-            out, atoms.signatureInfoMap, attributionDecl, supportQ);
-    errors += write_java_non_chained_methods(
-            out, atoms.nonChainedSignatureInfoMap);
+    errors += write_java_methods(out, atoms.signatureInfoMap, attributionDecl, supportQ);
+    errors += write_java_non_chained_methods(out, atoms.nonChainedSignatureInfoMap);
     if (supportWorkSource) {
         errors += write_java_work_source_methods(out, atoms.signatureInfoMap);
     }
 
     if (supportQ) {
-        errors += write_java_q_logger_class(
-                out, atoms.signatureInfoMap, attributionDecl);
+        errors += write_java_q_logger_class(out, atoms.signatureInfoMap, attributionDecl);
     }
 
     fprintf(out, "}\n");
diff --git a/tools/stats_log_api_gen/java_writer.h b/tools/stats_log_api_gen/java_writer.h
index 4e1365e..8b3b505 100644
--- a/tools/stats_log_api_gen/java_writer.h
+++ b/tools/stats_log_api_gen/java_writer.h
@@ -16,25 +16,23 @@
 
 #pragma once
 
-#include "Collation.h"
+#include <stdio.h>
+#include <string.h>
 
 #include <map>
 #include <set>
 #include <vector>
 
-#include <stdio.h>
-#include <string.h>
+#include "Collation.h"
 
 namespace android {
 namespace stats_log_api_gen {
 
 using namespace std;
 
-int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl,
-                         const string& javaClass,
-                         const string& javaPackage, const bool supportQ,
+int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
+                         const string& javaClass, const string& javaPackage, const bool supportQ,
                          const bool supportWorkSource);
 
 }  // namespace stats_log_api_gen
 }  // namespace android
-
diff --git a/tools/stats_log_api_gen/java_writer_q.cpp b/tools/stats_log_api_gen/java_writer_q.cpp
index 329c25d..7d22583 100644
--- a/tools/stats_log_api_gen/java_writer_q.cpp
+++ b/tools/stats_log_api_gen/java_writer_q.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "java_writer_q.h"
+
 #include "utils.h"
 
 namespace android {
@@ -24,7 +25,8 @@
     fprintf(out, "%s// Payload limits.\n", indent.c_str());
     fprintf(out, "%sprivate static final int LOGGER_ENTRY_MAX_PAYLOAD = 4068;\n", indent.c_str());
     fprintf(out,
-            "%sprivate static final int MAX_EVENT_PAYLOAD = LOGGER_ENTRY_MAX_PAYLOAD - 4;\n",
+            "%sprivate static final int MAX_EVENT_PAYLOAD = "
+            "LOGGER_ENTRY_MAX_PAYLOAD - 4;\n",
             indent.c_str());
 
     // Value types. Must match with EventLog.java and log.h.
@@ -37,36 +39,36 @@
     fprintf(out, "%sprivate static final byte FLOAT_TYPE = 4;\n", indent.c_str());
 
     // Size of each value type.
-    // Booleans, ints, floats, and enums take 5 bytes, 1 for the type and 4 for the value.
+    // Booleans, ints, floats, and enums take 5 bytes, 1 for the type and 4 for
+    // the value.
     fprintf(out, "\n");
     fprintf(out, "%s// Size of each value type.\n", indent.c_str());
     fprintf(out, "%sprivate static final int INT_TYPE_SIZE = 5;\n", indent.c_str());
     fprintf(out, "%sprivate static final int FLOAT_TYPE_SIZE = 5;\n", indent.c_str());
     // Longs take 9 bytes, 1 for the type and 8 for the value.
     fprintf(out, "%sprivate static final int LONG_TYPE_SIZE = 9;\n", indent.c_str());
-    // Strings take 5 metadata bytes: 1 byte is for the type, 4 are for the length.
+    // Strings take 5 metadata bytes: 1 byte is for the type, 4 are for the
+    // length.
     fprintf(out, "%sprivate static final int STRING_TYPE_OVERHEAD = 5;\n", indent.c_str());
     fprintf(out, "%sprivate static final int LIST_TYPE_OVERHEAD = 2;\n", indent.c_str());
 }
 
 int write_java_methods_q_schema(
-        FILE* out,
-        const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap,
-        const AtomDecl &attributionDecl,
-        const string& indent) {
+        FILE* out, const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap,
+        const AtomDecl& attributionDecl, const string& indent) {
     int requiredHelpers = 0;
     for (auto signatureInfoMapIt = signatureInfoMap.begin();
-            signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
+         signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
         // Print method signature.
         vector<java_type_t> signature = signatureInfoMapIt->first;
         fprintf(out, "%spublic static void write(int code", indent.c_str());
         int argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin();
-                arg != signature.end(); arg++) {
+        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+             arg++) {
             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
                 for (auto chainField : attributionDecl.fields) {
-                    fprintf(out, ", %s[] %s",
-                        java_type_name(chainField.javaType), chainField.name.c_str());
+                    fprintf(out, ", %s[] %s", java_type_name(chainField.javaType),
+                            chainField.name.c_str());
                 }
             } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
                 fprintf(out, ", android.util.SparseArray<Object> valueMap");
@@ -81,190 +83,174 @@
         fprintf(out, "%s    // Initial overhead of the list, timestamp, and atom tag.\n",
                 indent.c_str());
         fprintf(out,
-                "%s    int needed = LIST_TYPE_OVERHEAD + LONG_TYPE_SIZE + INT_TYPE_SIZE;\n",
+                "%s    int needed = LIST_TYPE_OVERHEAD + LONG_TYPE_SIZE + "
+                "INT_TYPE_SIZE;\n",
                 indent.c_str());
         argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin();
-                arg != signature.end(); arg++) {
+        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+             arg++) {
             switch (*arg) {
-            case JAVA_TYPE_BOOLEAN:
-            case JAVA_TYPE_INT:
-            case JAVA_TYPE_FLOAT:
-            case JAVA_TYPE_ENUM:
-                fprintf(out, "%s    needed += INT_TYPE_SIZE;\n", indent.c_str());
-                break;
-            case JAVA_TYPE_LONG:
-                // Longs take 9 bytes, 1 for the type and 8 for the value.
-                fprintf(out, "%s    needed += LONG_TYPE_SIZE;\n", indent.c_str());
-                break;
-            case JAVA_TYPE_STRING:
-                // Strings take 5 metadata bytes + length of byte encoded string.
-                fprintf(out, "%s    if (arg%d == null) {\n", indent.c_str(), argIndex);
-                fprintf(out, "%s        arg%d = \"\";\n", indent.c_str(), argIndex);
-                fprintf(out, "%s    }\n", indent.c_str());
-                fprintf(out,
-                        "%s    byte[] arg%dBytes = "
-                        "arg%d.getBytes(java.nio.charset.StandardCharsets.UTF_8);\n",
-                        indent.c_str(), argIndex, argIndex);
-                fprintf(out, "%s    needed += STRING_TYPE_OVERHEAD + arg%dBytes.length;\n",
-                        indent.c_str(), argIndex);
-                break;
-            case JAVA_TYPE_BYTE_ARRAY:
-                // Byte arrays take 5 metadata bytes + length of byte array.
-                fprintf(out, "%s    if (arg%d == null) {\n", indent.c_str(), argIndex);
-                fprintf(out, "%s        arg%d = new byte[0];\n", indent.c_str(), argIndex);
-                fprintf(out, "%s    }\n", indent.c_str());
-                fprintf(out, "%s    needed += STRING_TYPE_OVERHEAD + arg%d.length;\n",
-                        indent.c_str(), argIndex);
-                break;
-            case JAVA_TYPE_ATTRIBUTION_CHAIN:
-            {
-                const char* uidName = attributionDecl.fields.front().name.c_str();
-                const char* tagName = attributionDecl.fields.back().name.c_str();
-                // Null checks on the params.
-                fprintf(out, "%s    if (%s == null) {\n", indent.c_str(), uidName);
-                fprintf(out, "%s        %s = new %s[0];\n", indent.c_str(), uidName,
-                        java_type_name(attributionDecl.fields.front().javaType));
-                fprintf(out, "%s    }\n", indent.c_str());
-                fprintf(out, "%s    if (%s == null) {\n", indent.c_str(), tagName);
-                fprintf(out, "%s        %s = new %s[0];\n", indent.c_str(), tagName,
-                        java_type_name(attributionDecl.fields.back().javaType));
-                fprintf(out, "%s    }\n", indent.c_str());
+                case JAVA_TYPE_BOOLEAN:
+                case JAVA_TYPE_INT:
+                case JAVA_TYPE_FLOAT:
+                case JAVA_TYPE_ENUM:
+                    fprintf(out, "%s    needed += INT_TYPE_SIZE;\n", indent.c_str());
+                    break;
+                case JAVA_TYPE_LONG:
+                    // Longs take 9 bytes, 1 for the type and 8 for the value.
+                    fprintf(out, "%s    needed += LONG_TYPE_SIZE;\n", indent.c_str());
+                    break;
+                case JAVA_TYPE_STRING:
+                    // Strings take 5 metadata bytes + length of byte encoded string.
+                    fprintf(out, "%s    if (arg%d == null) {\n", indent.c_str(), argIndex);
+                    fprintf(out, "%s        arg%d = \"\";\n", indent.c_str(), argIndex);
+                    fprintf(out, "%s    }\n", indent.c_str());
+                    fprintf(out,
+                            "%s    byte[] arg%dBytes = "
+                            "arg%d.getBytes(java.nio.charset.StandardCharsets.UTF_8);\n",
+                            indent.c_str(), argIndex, argIndex);
+                    fprintf(out, "%s    needed += STRING_TYPE_OVERHEAD + arg%dBytes.length;\n",
+                            indent.c_str(), argIndex);
+                    break;
+                case JAVA_TYPE_BYTE_ARRAY:
+                    // Byte arrays take 5 metadata bytes + length of byte array.
+                    fprintf(out, "%s    if (arg%d == null) {\n", indent.c_str(), argIndex);
+                    fprintf(out, "%s        arg%d = new byte[0];\n", indent.c_str(), argIndex);
+                    fprintf(out, "%s    }\n", indent.c_str());
+                    fprintf(out, "%s    needed += STRING_TYPE_OVERHEAD + arg%d.length;\n",
+                            indent.c_str(), argIndex);
+                    break;
+                case JAVA_TYPE_ATTRIBUTION_CHAIN: {
+                    const char* uidName = attributionDecl.fields.front().name.c_str();
+                    const char* tagName = attributionDecl.fields.back().name.c_str();
+                    // Null checks on the params.
+                    fprintf(out, "%s    if (%s == null) {\n", indent.c_str(), uidName);
+                    fprintf(out, "%s        %s = new %s[0];\n", indent.c_str(), uidName,
+                            java_type_name(attributionDecl.fields.front().javaType));
+                    fprintf(out, "%s    }\n", indent.c_str());
+                    fprintf(out, "%s    if (%s == null) {\n", indent.c_str(), tagName);
+                    fprintf(out, "%s        %s = new %s[0];\n", indent.c_str(), tagName,
+                            java_type_name(attributionDecl.fields.back().javaType));
+                    fprintf(out, "%s    }\n", indent.c_str());
 
-                // First check that the lengths of the uid and tag arrays are the same.
-                fprintf(out, "%s    if (%s.length != %s.length) {\n",
-                        indent.c_str(), uidName, tagName);
-                fprintf(out, "%s        return;\n", indent.c_str());
-                fprintf(out, "%s    }\n", indent.c_str());
-                fprintf(out, "%s    int attrSize = LIST_TYPE_OVERHEAD;\n", indent.c_str());
-                fprintf(out, "%s    for (int i = 0; i < %s.length; i++) {\n",
-                        indent.c_str(), tagName);
-                fprintf(out, "%s        String str%d = (%s[i] == null) ? \"\" : %s[i];\n",
-                        indent.c_str(), argIndex, tagName, tagName);
-                fprintf(out,
-                        "%s        int str%dlen = "
-                        "str%d.getBytes(java.nio.charset.StandardCharsets.UTF_8).length;\n",
-                        indent.c_str(), argIndex, argIndex);
-                fprintf(out,
-                        "%s        attrSize += "
-                        "LIST_TYPE_OVERHEAD + INT_TYPE_SIZE + STRING_TYPE_OVERHEAD + str%dlen;\n",
-                        indent.c_str(), argIndex);
-                fprintf(out, "%s    }\n", indent.c_str());
-                fprintf(out, "%s    needed += attrSize;\n", indent.c_str());
-                break;
-            }
-            case JAVA_TYPE_KEY_VALUE_PAIR:
-            {
-                fprintf(out,
-                        "%s    // Calculate bytes needed by Key Value Pairs.\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s    final int count = valueMap.size();\n", indent.c_str());
-                fprintf(out,
-                        "%s    android.util.SparseIntArray intMap = null;\n", indent.c_str());
-                fprintf(out,
-                        "%s    android.util.SparseLongArray longMap = null;\n", indent.c_str());
-                fprintf(out,
-                        "%s    android.util.SparseArray<String> stringMap = null;\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s    android.util.SparseArray<Float> floatMap = null;\n", indent.c_str());
-                fprintf(out, "%s    int keyValuePairSize = LIST_TYPE_OVERHEAD;\n", indent.c_str());
-                fprintf(out,
-                        "%s    for (int i = 0; i < count; i++) {\n", indent.c_str());
-                fprintf(out,
-                        "%s        final int key = valueMap.keyAt(i);\n", indent.c_str());
-                fprintf(out,
-                        "%s        final Object value = valueMap.valueAt(i);\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s        if (value instanceof Integer) {\n", indent.c_str());
-                fprintf(out,
-                        "%s            keyValuePairSize += LIST_TYPE_OVERHEAD\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s                    + INT_TYPE_SIZE + INT_TYPE_SIZE;\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s            if (null == intMap) {\n", indent.c_str());
-                fprintf(out,
-                        "%s                intMap = new android.util.SparseIntArray();\n", indent.c_str());
-                fprintf(out,
-                        "%s            }\n", indent.c_str());
-                fprintf(out,
-                        "%s            intMap.put(key, (Integer) value);\n", indent.c_str());
-                fprintf(out,
-                        "%s        } else if (value instanceof Long) {\n", indent.c_str());
-                fprintf(out,
-                        "%s            keyValuePairSize += LIST_TYPE_OVERHEAD\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s                    + INT_TYPE_SIZE + LONG_TYPE_SIZE;\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s            if (null == longMap) {\n", indent.c_str());
-                fprintf(out,
-                        "%s                longMap = new android.util.SparseLongArray();\n", indent.c_str());
-                fprintf(out,
-                        "%s            }\n", indent.c_str());
-                fprintf(out,
-                        "%s            longMap.put(key, (Long) value);\n", indent.c_str());
-                fprintf(out,
-                        "%s        } else if (value instanceof String) {\n", indent.c_str());
-                fprintf(out,
-                        "%s            final String str = (value == null) ? \"\" : "
-                        "(String) value;\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s            final int len = "
-                        "str.getBytes(java.nio.charset.StandardCharsets.UTF_8).length;\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s            keyValuePairSize += LIST_TYPE_OVERHEAD + INT_TYPE_SIZE\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s                    + STRING_TYPE_OVERHEAD + len;\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s            if (null == stringMap) {\n", indent.c_str());
-                fprintf(out,
-                        "%s                stringMap = new android.util.SparseArray<>();\n", indent.c_str());
-                fprintf(out,
-                        "%s            }\n", indent.c_str());
-                fprintf(out,
-                        "%s            stringMap.put(key, str);\n", indent.c_str());
-                fprintf(out,
-                        "%s        } else if (value instanceof Float) {\n", indent.c_str());
-                fprintf(out,
-                        "%s            keyValuePairSize += LIST_TYPE_OVERHEAD\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s                    + INT_TYPE_SIZE + FLOAT_TYPE_SIZE;\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s            if (null == floatMap) {\n", indent.c_str());
-                fprintf(out,
-                        "%s                floatMap = new android.util.SparseArray<>();\n", indent.c_str());
-                fprintf(out,
-                        "%s            }\n", indent.c_str());
-                fprintf(out,
-                        "%s            floatMap.put(key, (Float) value);\n", indent.c_str());
-                fprintf(out,
-                        "%s        }\n", indent.c_str());
-                fprintf(out,
-                        "%s    }\n", indent.c_str());
-                fprintf(out, "%s    needed += keyValuePairSize;\n", indent.c_str());
-                break;
-            }
-            default:
-                // Unsupported types: OBJECT, DOUBLE.
-                fprintf(stderr, "Module logging does not yet support Object and Double.\n");
-                return 1;
+                    // First check that the lengths of the uid and tag arrays are the
+                    // same.
+                    fprintf(out, "%s    if (%s.length != %s.length) {\n", indent.c_str(), uidName,
+                            tagName);
+                    fprintf(out, "%s        return;\n", indent.c_str());
+                    fprintf(out, "%s    }\n", indent.c_str());
+                    fprintf(out, "%s    int attrSize = LIST_TYPE_OVERHEAD;\n", indent.c_str());
+                    fprintf(out, "%s    for (int i = 0; i < %s.length; i++) {\n", indent.c_str(),
+                            tagName);
+                    fprintf(out, "%s        String str%d = (%s[i] == null) ? \"\" : %s[i];\n",
+                            indent.c_str(), argIndex, tagName, tagName);
+                    fprintf(out,
+                            "%s        int str%dlen = "
+                            "str%d.getBytes(java.nio.charset.StandardCharsets.UTF_8)."
+                            "length;\n",
+                            indent.c_str(), argIndex, argIndex);
+                    fprintf(out,
+                            "%s        attrSize += "
+                            "LIST_TYPE_OVERHEAD + INT_TYPE_SIZE + STRING_TYPE_OVERHEAD + "
+                            "str%dlen;\n",
+                            indent.c_str(), argIndex);
+                    fprintf(out, "%s    }\n", indent.c_str());
+                    fprintf(out, "%s    needed += attrSize;\n", indent.c_str());
+                    break;
+                }
+                case JAVA_TYPE_KEY_VALUE_PAIR: {
+                    fprintf(out, "%s    // Calculate bytes needed by Key Value Pairs.\n",
+                            indent.c_str());
+                    fprintf(out, "%s    final int count = valueMap.size();\n", indent.c_str());
+                    fprintf(out, "%s    android.util.SparseIntArray intMap = null;\n",
+                            indent.c_str());
+                    fprintf(out, "%s    android.util.SparseLongArray longMap = null;\n",
+                            indent.c_str());
+                    fprintf(out, "%s    android.util.SparseArray<String> stringMap = null;\n",
+                            indent.c_str());
+                    fprintf(out, "%s    android.util.SparseArray<Float> floatMap = null;\n",
+                            indent.c_str());
+                    fprintf(out, "%s    int keyValuePairSize = LIST_TYPE_OVERHEAD;\n",
+                            indent.c_str());
+                    fprintf(out, "%s    for (int i = 0; i < count; i++) {\n", indent.c_str());
+                    fprintf(out, "%s        final int key = valueMap.keyAt(i);\n", indent.c_str());
+                    fprintf(out, "%s        final Object value = valueMap.valueAt(i);\n",
+                            indent.c_str());
+                    fprintf(out, "%s        if (value instanceof Integer) {\n", indent.c_str());
+                    fprintf(out, "%s            keyValuePairSize += LIST_TYPE_OVERHEAD\n",
+                            indent.c_str());
+                    fprintf(out, "%s                    + INT_TYPE_SIZE + INT_TYPE_SIZE;\n",
+                            indent.c_str());
+                    fprintf(out, "%s            if (null == intMap) {\n", indent.c_str());
+                    fprintf(out, "%s                intMap = new android.util.SparseIntArray();\n",
+                            indent.c_str());
+                    fprintf(out, "%s            }\n", indent.c_str());
+                    fprintf(out, "%s            intMap.put(key, (Integer) value);\n",
+                            indent.c_str());
+                    fprintf(out, "%s        } else if (value instanceof Long) {\n", indent.c_str());
+                    fprintf(out, "%s            keyValuePairSize += LIST_TYPE_OVERHEAD\n",
+                            indent.c_str());
+                    fprintf(out, "%s                    + INT_TYPE_SIZE + LONG_TYPE_SIZE;\n",
+                            indent.c_str());
+                    fprintf(out, "%s            if (null == longMap) {\n", indent.c_str());
+                    fprintf(out,
+                            "%s                longMap = new "
+                            "android.util.SparseLongArray();\n",
+                            indent.c_str());
+                    fprintf(out, "%s            }\n", indent.c_str());
+                    fprintf(out, "%s            longMap.put(key, (Long) value);\n", indent.c_str());
+                    fprintf(out, "%s        } else if (value instanceof String) {\n",
+                            indent.c_str());
+                    fprintf(out,
+                            "%s            final String str = (value == null) ? \"\" : "
+                            "(String) value;\n",
+                            indent.c_str());
+                    fprintf(out,
+                            "%s            final int len = "
+                            "str.getBytes(java.nio.charset.StandardCharsets.UTF_8).length;\n",
+                            indent.c_str());
+                    fprintf(out,
+                            "%s            keyValuePairSize += LIST_TYPE_OVERHEAD + "
+                            "INT_TYPE_SIZE\n",
+                            indent.c_str());
+                    fprintf(out, "%s                    + STRING_TYPE_OVERHEAD + len;\n",
+                            indent.c_str());
+                    fprintf(out, "%s            if (null == stringMap) {\n", indent.c_str());
+                    fprintf(out,
+                            "%s                stringMap = new "
+                            "android.util.SparseArray<>();\n",
+                            indent.c_str());
+                    fprintf(out, "%s            }\n", indent.c_str());
+                    fprintf(out, "%s            stringMap.put(key, str);\n", indent.c_str());
+                    fprintf(out, "%s        } else if (value instanceof Float) {\n",
+                            indent.c_str());
+                    fprintf(out, "%s            keyValuePairSize += LIST_TYPE_OVERHEAD\n",
+                            indent.c_str());
+                    fprintf(out, "%s                    + INT_TYPE_SIZE + FLOAT_TYPE_SIZE;\n",
+                            indent.c_str());
+                    fprintf(out, "%s            if (null == floatMap) {\n", indent.c_str());
+                    fprintf(out,
+                            "%s                floatMap = new "
+                            "android.util.SparseArray<>();\n",
+                            indent.c_str());
+                    fprintf(out, "%s            }\n", indent.c_str());
+                    fprintf(out, "%s            floatMap.put(key, (Float) value);\n",
+                            indent.c_str());
+                    fprintf(out, "%s        }\n", indent.c_str());
+                    fprintf(out, "%s    }\n", indent.c_str());
+                    fprintf(out, "%s    needed += keyValuePairSize;\n", indent.c_str());
+                    break;
+                }
+                default:
+                    // Unsupported types: OBJECT, DOUBLE.
+                    fprintf(stderr, "Module logging does not yet support Object and Double.\n");
+                    return 1;
             }
             argIndex++;
         }
 
-        // Now we have the size that is needed. Check for overflow and return if needed.
+        // Now we have the size that is needed. Check for overflow and return if
+        // needed.
         fprintf(out, "%s    if (needed > MAX_EVENT_PAYLOAD) {\n", indent.c_str());
         fprintf(out, "%s        return;\n", indent.c_str());
         fprintf(out, "%s    }\n", indent.c_str());
@@ -279,7 +265,8 @@
         fprintf(out, "%s    pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
 
         // Write timestamp.
-        fprintf(out, "%s    long elapsedRealtime = SystemClock.elapsedRealtimeNanos();\n", indent.c_str());
+        fprintf(out, "%s    long elapsedRealtime = SystemClock.elapsedRealtimeNanos();\n",
+                indent.c_str());
         fprintf(out, "%s    buff[pos] = LONG_TYPE;\n", indent.c_str());
         fprintf(out, "%s    copyLong(buff, pos + 1, elapsedRealtime);\n", indent.c_str());
         fprintf(out, "%s    pos += LONG_TYPE_SIZE;\n", indent.c_str());
@@ -291,77 +278,82 @@
 
         // Write the args.
         argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin();
-                arg != signature.end(); arg++) {
+        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+             arg++) {
             switch (*arg) {
-            case JAVA_TYPE_BOOLEAN:
-                fprintf(out, "%s    buff[pos] = INT_TYPE;\n", indent.c_str());
-                fprintf(out, "%s    copyInt(buff, pos + 1, arg%d? 1 : 0);\n",
-                        indent.c_str(), argIndex);
-                fprintf(out, "%s    pos += INT_TYPE_SIZE;\n", indent.c_str());
-                break;
-            case JAVA_TYPE_INT:
-            case JAVA_TYPE_ENUM:
-                fprintf(out, "%s    buff[pos] = INT_TYPE;\n", indent.c_str());
-                fprintf(out, "%s    copyInt(buff, pos + 1, arg%d);\n", indent.c_str(), argIndex);
-                fprintf(out, "%s    pos += INT_TYPE_SIZE;\n", indent.c_str());
-                break;
-            case JAVA_TYPE_FLOAT:
-                requiredHelpers |= JAVA_MODULE_REQUIRES_FLOAT;
-                fprintf(out, "%s    buff[pos] = FLOAT_TYPE;\n", indent.c_str());
-                fprintf(out, "%s    copyFloat(buff, pos + 1, arg%d);\n", indent.c_str(), argIndex);
-                fprintf(out, "%s    pos += FLOAT_TYPE_SIZE;\n", indent.c_str());
-                break;
-            case JAVA_TYPE_LONG:
-                fprintf(out, "%s    buff[pos] = LONG_TYPE;\n", indent.c_str());
-                fprintf(out, "%s    copyLong(buff, pos + 1, arg%d);\n", indent.c_str(), argIndex);
-                fprintf(out, "%s    pos += LONG_TYPE_SIZE;\n", indent.c_str());
-                break;
-            case JAVA_TYPE_STRING:
-                fprintf(out, "%s    buff[pos] = STRING_TYPE;\n", indent.c_str());
-                fprintf(out, "%s    copyInt(buff, pos + 1, arg%dBytes.length);\n",
-                        indent.c_str(), argIndex);
-                fprintf(out, "%s    System.arraycopy("
-                        "arg%dBytes, 0, buff, pos + STRING_TYPE_OVERHEAD, arg%dBytes.length);\n",
-                        indent.c_str(), argIndex, argIndex);
-                fprintf(out, "%s    pos += STRING_TYPE_OVERHEAD + arg%dBytes.length;\n",
-                        indent.c_str(), argIndex);
-                break;
-            case JAVA_TYPE_BYTE_ARRAY:
-                fprintf(out, "%s    buff[pos] = STRING_TYPE;\n", indent.c_str());
-                fprintf(out, "%s    copyInt(buff, pos + 1, arg%d.length);\n",
-                        indent.c_str(), argIndex);
-                fprintf(out, "%s    System.arraycopy("
-                        "arg%d, 0, buff, pos + STRING_TYPE_OVERHEAD, arg%d.length);\n",
-                        indent.c_str(), argIndex, argIndex);
-                fprintf(out, "%s    pos += STRING_TYPE_OVERHEAD + arg%d.length;\n",
-                        indent.c_str(), argIndex);
-                break;
-            case JAVA_TYPE_ATTRIBUTION_CHAIN:
-            {
-                requiredHelpers |= JAVA_MODULE_REQUIRES_ATTRIBUTION;
-                const char* uidName = attributionDecl.fields.front().name.c_str();
-                const char* tagName = attributionDecl.fields.back().name.c_str();
+                case JAVA_TYPE_BOOLEAN:
+                    fprintf(out, "%s    buff[pos] = INT_TYPE;\n", indent.c_str());
+                    fprintf(out, "%s    copyInt(buff, pos + 1, arg%d? 1 : 0);\n", indent.c_str(),
+                            argIndex);
+                    fprintf(out, "%s    pos += INT_TYPE_SIZE;\n", indent.c_str());
+                    break;
+                case JAVA_TYPE_INT:
+                case JAVA_TYPE_ENUM:
+                    fprintf(out, "%s    buff[pos] = INT_TYPE;\n", indent.c_str());
+                    fprintf(out, "%s    copyInt(buff, pos + 1, arg%d);\n", indent.c_str(),
+                            argIndex);
+                    fprintf(out, "%s    pos += INT_TYPE_SIZE;\n", indent.c_str());
+                    break;
+                case JAVA_TYPE_FLOAT:
+                    requiredHelpers |= JAVA_MODULE_REQUIRES_FLOAT;
+                    fprintf(out, "%s    buff[pos] = FLOAT_TYPE;\n", indent.c_str());
+                    fprintf(out, "%s    copyFloat(buff, pos + 1, arg%d);\n", indent.c_str(),
+                            argIndex);
+                    fprintf(out, "%s    pos += FLOAT_TYPE_SIZE;\n", indent.c_str());
+                    break;
+                case JAVA_TYPE_LONG:
+                    fprintf(out, "%s    buff[pos] = LONG_TYPE;\n", indent.c_str());
+                    fprintf(out, "%s    copyLong(buff, pos + 1, arg%d);\n", indent.c_str(),
+                            argIndex);
+                    fprintf(out, "%s    pos += LONG_TYPE_SIZE;\n", indent.c_str());
+                    break;
+                case JAVA_TYPE_STRING:
+                    fprintf(out, "%s    buff[pos] = STRING_TYPE;\n", indent.c_str());
+                    fprintf(out, "%s    copyInt(buff, pos + 1, arg%dBytes.length);\n",
+                            indent.c_str(), argIndex);
+                    fprintf(out,
+                            "%s    System.arraycopy("
+                            "arg%dBytes, 0, buff, pos + STRING_TYPE_OVERHEAD, "
+                            "arg%dBytes.length);\n",
+                            indent.c_str(), argIndex, argIndex);
+                    fprintf(out, "%s    pos += STRING_TYPE_OVERHEAD + arg%dBytes.length;\n",
+                            indent.c_str(), argIndex);
+                    break;
+                case JAVA_TYPE_BYTE_ARRAY:
+                    fprintf(out, "%s    buff[pos] = STRING_TYPE;\n", indent.c_str());
+                    fprintf(out, "%s    copyInt(buff, pos + 1, arg%d.length);\n", indent.c_str(),
+                            argIndex);
+                    fprintf(out,
+                            "%s    System.arraycopy("
+                            "arg%d, 0, buff, pos + STRING_TYPE_OVERHEAD, arg%d.length);\n",
+                            indent.c_str(), argIndex, argIndex);
+                    fprintf(out, "%s    pos += STRING_TYPE_OVERHEAD + arg%d.length;\n",
+                            indent.c_str(), argIndex);
+                    break;
+                case JAVA_TYPE_ATTRIBUTION_CHAIN: {
+                    requiredHelpers |= JAVA_MODULE_REQUIRES_ATTRIBUTION;
+                    const char* uidName = attributionDecl.fields.front().name.c_str();
+                    const char* tagName = attributionDecl.fields.back().name.c_str();
 
-                fprintf(out, "%s    writeAttributionChain(buff, pos, %s, %s);\n", indent.c_str(),
-                        uidName, tagName);
-                fprintf(out, "%s    pos += attrSize;\n", indent.c_str());
-                break;
-            }
-            case JAVA_TYPE_KEY_VALUE_PAIR:
-                requiredHelpers |= JAVA_MODULE_REQUIRES_FLOAT;
-                requiredHelpers |= JAVA_MODULE_REQUIRES_KEY_VALUE_PAIRS;
-                fprintf(out,
-                        "%s    writeKeyValuePairs(buff, pos, (byte) count, intMap, longMap, "
-                        "stringMap, floatMap);\n",
-                        indent.c_str());
-                fprintf(out, "%s    pos += keyValuePairSize;\n", indent.c_str());
-                break;
-            default:
-                // Unsupported types: OBJECT, DOUBLE.
-                fprintf(stderr,
-                        "Object and Double are not supported in module logging");
-                return 1;
+                    fprintf(out, "%s    writeAttributionChain(buff, pos, %s, %s);\n",
+                            indent.c_str(), uidName, tagName);
+                    fprintf(out, "%s    pos += attrSize;\n", indent.c_str());
+                    break;
+                }
+                case JAVA_TYPE_KEY_VALUE_PAIR:
+                    requiredHelpers |= JAVA_MODULE_REQUIRES_FLOAT;
+                    requiredHelpers |= JAVA_MODULE_REQUIRES_KEY_VALUE_PAIRS;
+                    fprintf(out,
+                            "%s    writeKeyValuePairs(buff, pos, (byte) count, intMap, "
+                            "longMap, "
+                            "stringMap, floatMap);\n",
+                            indent.c_str());
+                    fprintf(out, "%s    pos += keyValuePairSize;\n", indent.c_str());
+                    break;
+                default:
+                    // Unsupported types: OBJECT, DOUBLE.
+                    fprintf(stderr, "Object and Double are not supported in module logging");
+                    return 1;
             }
             argIndex++;
         }
@@ -376,11 +368,8 @@
     return 0;
 }
 
-void write_java_helpers_for_q_schema_methods(
-        FILE* out,
-        const AtomDecl &attributionDecl,
-        const int requiredHelpers,
-        const string& indent) {
+void write_java_helpers_for_q_schema_methods(FILE* out, const AtomDecl& attributionDecl,
+                                             const int requiredHelpers, const string& indent) {
     fprintf(out, "\n");
     fprintf(out, "%s// Helper methods for copying primitives\n", indent.c_str());
     fprintf(out, "%sprivate static void copyInt(byte[] buff, int pos, int val) {\n",
@@ -420,8 +409,7 @@
         fprintf(out, "%sprivate static void writeAttributionChain(byte[] buff, int pos",
                 indent.c_str());
         for (auto chainField : attributionDecl.fields) {
-            fprintf(out, ", %s[] %s",
-                java_type_name(chainField.javaType), chainField.name.c_str());
+            fprintf(out, ", %s[] %s", java_type_name(chainField.javaType), chainField.name.c_str());
         }
         fprintf(out, ") {\n");
 
@@ -437,8 +425,8 @@
         fprintf(out, "%s    for (int i = 0; i < %s.length; i++) {\n", indent.c_str(), tagName);
         // Write the list begin.
         fprintf(out, "%s        buff[pos] = LIST_TYPE;\n", indent.c_str());
-        fprintf(out, "%s        buff[pos + 1] = %lu;\n",
-                indent.c_str(), attributionDecl.fields.size());
+        fprintf(out, "%s        buff[pos + 1] = %lu;\n", indent.c_str(),
+                attributionDecl.fields.size());
         fprintf(out, "%s        pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
 
         // Write the uid.
@@ -447,18 +435,20 @@
         fprintf(out, "%s        pos += INT_TYPE_SIZE;\n", indent.c_str());
 
         // Write the tag.
-        fprintf(out, "%s        String %sStr = (%s[i] == null) ? \"\" : %s[i];\n",
-                indent.c_str(), tagName, tagName, tagName);
-        fprintf(out, "%s        byte[] %sByte = "
+        fprintf(out, "%s        String %sStr = (%s[i] == null) ? \"\" : %s[i];\n", indent.c_str(),
+                tagName, tagName, tagName);
+        fprintf(out,
+                "%s        byte[] %sByte = "
                 "%sStr.getBytes(java.nio.charset.StandardCharsets.UTF_8);\n",
                 indent.c_str(), tagName, tagName);
         fprintf(out, "%s        buff[pos] = STRING_TYPE;\n", indent.c_str());
         fprintf(out, "%s        copyInt(buff, pos + 1, %sByte.length);\n", indent.c_str(), tagName);
-        fprintf(out, "%s        System.arraycopy("
+        fprintf(out,
+                "%s        System.arraycopy("
                 "%sByte, 0, buff, pos + STRING_TYPE_OVERHEAD, %sByte.length);\n",
                 indent.c_str(), tagName, tagName);
-        fprintf(out, "%s        pos += STRING_TYPE_OVERHEAD + %sByte.length;\n",
-                indent.c_str(), tagName);
+        fprintf(out, "%s        pos += STRING_TYPE_OVERHEAD + %sByte.length;\n", indent.c_str(),
+                tagName);
         fprintf(out, "%s    }\n", indent.c_str());
         fprintf(out, "%s}\n", indent.c_str());
         fprintf(out, "\n");
@@ -466,7 +456,8 @@
 
     if (requiredHelpers & JAVA_MODULE_REQUIRES_KEY_VALUE_PAIRS) {
         fprintf(out,
-                "%sprivate static void writeKeyValuePairs(byte[] buff, int pos, byte numPairs,\n",
+                "%sprivate static void writeKeyValuePairs(byte[] buff, int pos, "
+                "byte numPairs,\n",
                 indent.c_str());
         fprintf(out, "%s        final android.util.SparseIntArray intMap,\n", indent.c_str());
         fprintf(out, "%s        final android.util.SparseLongArray longMap,\n", indent.c_str());
@@ -515,7 +506,9 @@
         fprintf(out, "%s    }\n", indent.c_str());
 
         // Write Strings.
-        fprintf(out, "%s    final int stringMapSize = null == stringMap ? 0 : stringMap.size();\n",
+        fprintf(out,
+                "%s    final int stringMapSize = null == stringMap ? 0 : "
+                "stringMap.size();\n",
                 indent.c_str());
         fprintf(out, "%s    for (int i = 0; i < stringMapSize; i++) {\n", indent.c_str());
         fprintf(out, "%s        buff[pos] = LIST_TYPE;\n", indent.c_str());
@@ -523,7 +516,8 @@
         fprintf(out, "%s        pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
         fprintf(out, "%s        final int key = stringMap.keyAt(i);\n", indent.c_str());
         fprintf(out, "%s        final String value = stringMap.valueAt(i);\n", indent.c_str());
-        fprintf(out, "%s        final byte[] valueBytes = "
+        fprintf(out,
+                "%s        final byte[] valueBytes = "
                 "value.getBytes(java.nio.charset.StandardCharsets.UTF_8);\n",
                 indent.c_str());
         fprintf(out, "%s        buff[pos] = INT_TYPE;\n", indent.c_str());
@@ -531,15 +525,19 @@
         fprintf(out, "%s        pos += INT_TYPE_SIZE;\n", indent.c_str());
         fprintf(out, "%s        buff[pos] = STRING_TYPE;\n", indent.c_str());
         fprintf(out, "%s        copyInt(buff, pos + 1, valueBytes.length);\n", indent.c_str());
-        fprintf(out, "%s        System.arraycopy("
-                "valueBytes, 0, buff, pos + STRING_TYPE_OVERHEAD, valueBytes.length);\n",
+        fprintf(out,
+                "%s        System.arraycopy("
+                "valueBytes, 0, buff, pos + STRING_TYPE_OVERHEAD, "
+                "valueBytes.length);\n",
                 indent.c_str());
         fprintf(out, "%s        pos += STRING_TYPE_OVERHEAD + valueBytes.length;\n",
                 indent.c_str());
         fprintf(out, "%s    }\n", indent.c_str());
 
         // Write floats.
-        fprintf(out, "%s    final int floatMapSize = null == floatMap ? 0 : floatMap.size();\n",
+        fprintf(out,
+                "%s    final int floatMapSize = null == floatMap ? 0 : "
+                "floatMap.size();\n",
                 indent.c_str());
         fprintf(out, "%s    for (int i = 0; i < floatMapSize; i++) {\n", indent.c_str());
         fprintf(out, "%s        buff[pos] = LIST_TYPE;\n", indent.c_str());
@@ -559,12 +557,11 @@
     }
 }
 
-// This method is called in main.cpp to generate StatsLog for modules that's compatible with
-// Q at compile-time.
+// This method is called in main.cpp to generate StatsLog for modules that's
+// compatible with Q at compile-time.
 int write_stats_log_java_q_for_module(FILE* out, const Atoms& atoms,
-                                      const AtomDecl &attributionDecl,
-                                      const string& javaClass, const string& javaPackage,
-                                      const bool supportWorkSource) {
+                                      const AtomDecl& attributionDecl, const string& javaClass,
+                                      const string& javaPackage, const bool supportWorkSource) {
     // Print prelude
     fprintf(out, "// This file is autogenerated\n");
     fprintf(out, "\n");
@@ -590,8 +587,7 @@
     int errors = 0;
     // Print write methods
     fprintf(out, "    // Write methods\n");
-    errors += write_java_methods_q_schema(out, atoms.signatureInfoMap, attributionDecl,
-            "    ");
+    errors += write_java_methods_q_schema(out, atoms.signatureInfoMap, attributionDecl, "    ");
     errors += write_java_non_chained_methods(out, atoms.nonChainedSignatureInfoMap);
     if (supportWorkSource) {
         errors += write_java_work_source_methods(out, atoms.signatureInfoMap);
diff --git a/tools/stats_log_api_gen/java_writer_q.h b/tools/stats_log_api_gen/java_writer_q.h
index 0f33b6c..f1cfc44 100644
--- a/tools/stats_log_api_gen/java_writer_q.h
+++ b/tools/stats_log_api_gen/java_writer_q.h
@@ -16,14 +16,14 @@
 
 #pragma once
 
-#include "Collation.h"
+#include <stdio.h>
+#include <string.h>
 
 #include <map>
 #include <set>
 #include <vector>
 
-#include <stdio.h>
-#include <string.h>
+#include "Collation.h"
 
 namespace android {
 namespace stats_log_api_gen {
@@ -33,20 +33,15 @@
 void write_java_q_logging_constants(FILE* out, const string& indent);
 
 int write_java_methods_q_schema(
-        FILE* out,
-        const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap,
-        const AtomDecl &attributionDecl,
-        const string& indent);
+        FILE* out, const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap,
+        const AtomDecl& attributionDecl, const string& indent);
 
-void write_java_helpers_for_q_schema_methods(
-        FILE * out,
-        const AtomDecl &attributionDecl,
-        const int requiredHelpers,
-        const string& indent);
+void write_java_helpers_for_q_schema_methods(FILE* out, const AtomDecl& attributionDecl,
+                                             const int requiredHelpers, const string& indent);
 
 int write_stats_log_java_q_for_module(FILE* out, const Atoms& atoms,
-        const AtomDecl &attributionDecl, const string& javaClass,
-        const string& javaPackage, const bool supportWorkSource);
+                                      const AtomDecl& attributionDecl, const string& javaClass,
+                                      const string& javaPackage, const bool supportWorkSource);
 
 }  // namespace stats_log_api_gen
 }  // namespace android
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 4f791a3..fda5736 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -1,21 +1,20 @@
 
-#include "Collation.h"
-#include "atoms_info_writer.h"
-#include "java_writer.h"
-#include "java_writer_q.h"
-#include "native_writer.h"
-#include "utils.h"
-
-#include "frameworks/base/cmds/statsd/src/atoms.pb.h"
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 
 #include <map>
 #include <set>
 #include <vector>
 
-#include <getopt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include "Collation.h"
+#include "atoms_info_writer.h"
+#include "frameworks/base/cmds/statsd/src/atoms.pb.h"
+#include "java_writer.h"
+#include "java_writer_q.h"
+#include "native_writer.h"
+#include "utils.h"
 
 using namespace google::protobuf;
 using namespace std;
@@ -25,25 +24,34 @@
 
 using android::os::statsd::Atom;
 
-static void
-print_usage()
-{
+static void print_usage() {
     fprintf(stderr, "usage: stats-log-api-gen OPTIONS\n");
     fprintf(stderr, "\n");
     fprintf(stderr, "OPTIONS\n");
     fprintf(stderr, "  --cpp FILENAME       the header file to output for write helpers\n");
     fprintf(stderr, "  --header FILENAME    the cpp file to output for write helpers\n");
     fprintf(stderr,
-            "  --atomsInfoCpp FILENAME       the header file to output for statsd metadata\n");
-    fprintf(stderr, "  --atomsInfoHeader FILENAME    the cpp file to output for statsd metadata\n");
+            "  --atomsInfoCpp FILENAME       the header file to output for "
+            "statsd metadata\n");
+    fprintf(stderr,
+            "  --atomsInfoHeader FILENAME    the cpp file to output for statsd "
+            "metadata\n");
     fprintf(stderr, "  --help               this message\n");
     fprintf(stderr, "  --java FILENAME      the java file to output\n");
     fprintf(stderr, "  --module NAME        optional, module name to generate outputs for\n");
-    fprintf(stderr, "  --namespace COMMA,SEP,NAMESPACE   required for cpp/header with module\n");
-    fprintf(stderr, "                                    comma separated namespace of the files\n");
-    fprintf(stderr,"  --importHeader NAME  required for cpp/jni to say which header to import "
+    fprintf(stderr,
+            "  --namespace COMMA,SEP,NAMESPACE   required for cpp/header with "
+            "module\n");
+    fprintf(stderr,
+            "                                    comma separated namespace of "
+            "the files\n");
+    fprintf(stderr,
+            "  --importHeader NAME  required for cpp/jni to say which header to "
+            "import "
             "for write helpers\n");
-    fprintf(stderr,"  --atomsInfoImportHeader NAME  required for cpp to say which header to import "
+    fprintf(stderr,
+            "  --atomsInfoImportHeader NAME  required for cpp to say which "
+            "header to import "
             "for statsd metadata\n");
     fprintf(stderr, "  --javaPackage PACKAGE             the package for the java file.\n");
     fprintf(stderr, "                                    required for java with module\n");
@@ -51,17 +59,18 @@
     fprintf(stderr, "                       Optional for Java with module.\n");
     fprintf(stderr, "                       Default is \"StatsLogInternal\"\n");
     fprintf(stderr, "  --supportQ           Include runtime support for Android Q.\n");
-    fprintf(stderr, "  --worksource         Include support for logging WorkSource objects.\n");
-    fprintf(stderr, "  --compileQ           Include compile-time support for Android Q "
+    fprintf(stderr,
+            "  --worksource         Include support for logging WorkSource "
+            "objects.\n");
+    fprintf(stderr,
+            "  --compileQ           Include compile-time support for Android Q "
             "(Java only).\n");
 }
 
 /**
  * Do the argument parsing and execute the tasks.
  */
-static int
-run(int argc, char const*const* argv)
-{
+static int run(int argc, char const* const* argv) {
     string cppFilename;
     string headerFilename;
     string javaFilename;
@@ -171,11 +180,8 @@
         index++;
     }
 
-    if (cppFilename.size() == 0
-            && headerFilename.size() == 0
-            && javaFilename.size() == 0
-            && atomsInfoHeaderFilename.size() == 0
-            && atomsInfoCppFilename.size() == 0) {
+    if (cppFilename.size() == 0 && headerFilename.size() == 0 && javaFilename.size() == 0 &&
+        atomsInfoHeaderFilename.size() == 0 && atomsInfoCppFilename.size() == 0) {
         print_usage();
         return 1;
     }
@@ -201,8 +207,8 @@
 
     AtomDecl attributionDecl;
     vector<java_type_t> attributionSignature;
-    collate_atom(android::os::statsd::AttributionNode::descriptor(),
-                 &attributionDecl, &attributionSignature);
+    collate_atom(android::os::statsd::AttributionNode::descriptor(), &attributionDecl,
+                 &attributionSignature);
 
     // Write the atoms info .cpp file
     if (atomsInfoCppFilename.size() != 0) {
@@ -211,8 +217,8 @@
             fprintf(stderr, "Unable to open file for write: %s\n", atomsInfoCppFilename.c_str());
             return 1;
         }
-        errorCount = android::stats_log_api_gen::write_atoms_info_cpp(
-            out, atoms, cppNamespace, atomsInfoCppHeaderImport);
+        errorCount = android::stats_log_api_gen::write_atoms_info_cpp(out, atoms, cppNamespace,
+                                                                      atomsInfoCppHeaderImport);
         fclose(out);
     }
 
@@ -227,7 +233,6 @@
         fclose(out);
     }
 
-
     // Write the .cpp file
     if (cppFilename.size() != 0) {
         FILE* out = fopen(cppFilename.c_str(), "w");
@@ -240,13 +245,14 @@
             fprintf(stderr, "Must supply --namespace if supplying a specific module\n");
             return 1;
         }
-        // If this is for a specific module, the header file to import must also be provided.
+        // If this is for a specific module, the header file to import must also be
+        // provided.
         if (moduleName != DEFAULT_MODULE_NAME && cppHeaderImport == DEFAULT_CPP_HEADER_IMPORT) {
             fprintf(stderr, "Must supply --headerImport if supplying a specific module\n");
             return 1;
         }
         errorCount = android::stats_log_api_gen::write_stats_log_cpp(
-            out, atoms, attributionDecl, cppNamespace, cppHeaderImport, supportQ);
+                out, atoms, attributionDecl, cppNamespace, cppHeaderImport, supportQ);
         fclose(out);
     }
 
@@ -261,8 +267,8 @@
         if (moduleName != DEFAULT_MODULE_NAME && cppNamespace == DEFAULT_CPP_NAMESPACE) {
             fprintf(stderr, "Must supply --namespace if supplying a specific module\n");
         }
-        errorCount = android::stats_log_api_gen::write_stats_log_header(
-            out, atoms, attributionDecl, cppNamespace);
+        errorCount = android::stats_log_api_gen::write_stats_log_header(out, atoms, attributionDecl,
+                                                                        cppNamespace);
         fclose(out);
     }
 
@@ -291,8 +297,7 @@
 
         if (compileQ) {
             errorCount = android::stats_log_api_gen::write_stats_log_java_q_for_module(
-                    out, atoms, attributionDecl, javaClass, javaPackage,
-                    supportWorkSource);
+                    out, atoms, attributionDecl, javaClass, javaPackage, supportWorkSource);
         } else {
             errorCount = android::stats_log_api_gen::write_stats_log_java(
                     out, atoms, attributionDecl, javaClass, javaPackage, supportQ,
@@ -311,9 +316,7 @@
 /**
  * Main.
  */
-int
-main(int argc, char const*const* argv)
-{
+int main(int argc, char const* const* argv) {
     GOOGLE_PROTOBUF_VERIFY_VERSION;
 
     return android::stats_log_api_gen::run(argc, argv);
diff --git a/tools/stats_log_api_gen/native_writer.cpp b/tools/stats_log_api_gen/native_writer.cpp
index 90dcae4..0cf3225 100644
--- a/tools/stats_log_api_gen/native_writer.cpp
+++ b/tools/stats_log_api_gen/native_writer.cpp
@@ -15,45 +15,38 @@
  */
 
 #include "native_writer.h"
+
 #include "utils.h"
 
 namespace android {
 namespace stats_log_api_gen {
 
-static void write_annotations(
-        FILE* out, int argIndex,
-        const FieldNumberToAnnotations& fieldNumberToAnnotations,
-        const string& methodPrefix,
-        const string& methodSuffix) {
+static void write_annotations(FILE* out, int argIndex,
+                              const FieldNumberToAnnotations& fieldNumberToAnnotations,
+                              const string& methodPrefix, const string& methodSuffix) {
     auto fieldNumberToAnnotationsIt = fieldNumberToAnnotations.find(argIndex);
     if (fieldNumberToAnnotationsIt == fieldNumberToAnnotations.end()) {
         return;
     }
-    const set<shared_ptr<Annotation>>& annotations =
-            fieldNumberToAnnotationsIt->second;
+    const set<shared_ptr<Annotation>>& annotations = fieldNumberToAnnotationsIt->second;
     for (const shared_ptr<Annotation>& annotation : annotations) {
         // TODO(b/151744250): Group annotations for same atoms.
         // TODO(b/151786433): Write atom constant name instead of atom id literal.
         fprintf(out, "    if (code == %d) {\n", annotation->atomId);
-        switch(annotation->type) {
-            // TODO(b/151776731): Check for reset state annotation and only include reset state
-            // when field value == default state annotation value.
+        switch (annotation->type) {
+            // TODO(b/151776731): Check for reset state annotation and only include
+            // reset state when field value == default state annotation value.
             case ANNOTATION_TYPE_INT:
                 // TODO(b/151786433): Write annotation constant name instead of
                 // annotation id literal.
-                fprintf(out, "        %saddInt32Annotation(%s%d, %d);\n",
-                        methodPrefix.c_str(),
-                        methodSuffix.c_str(),
-                        annotation->annotationId,
-                        annotation->value.intValue);
+                fprintf(out, "        %saddInt32Annotation(%s%d, %d);\n", methodPrefix.c_str(),
+                        methodSuffix.c_str(), annotation->annotationId, annotation->value.intValue);
                 break;
             case ANNOTATION_TYPE_BOOL:
                 // TODO(b/151786433): Write annotation constant name instead of
                 // annotation id literal.
-                fprintf(out, "        %saddBoolAnnotation(%s%d, %s);\n",
-                        methodPrefix.c_str(),
-                        methodSuffix.c_str(),
-                        annotation->annotationId,
+                fprintf(out, "        %saddBoolAnnotation(%s%d, %s);\n", methodPrefix.c_str(),
+                        methodSuffix.c_str(), annotation->annotationId,
                         annotation->value.boolValue ? "true" : "false");
                 break;
             default:
@@ -61,29 +54,28 @@
         }
         fprintf(out, "    }\n");
     }
-
 }
 
 static int write_native_stats_write_methods(FILE* out, const Atoms& atoms,
-        const AtomDecl& attributionDecl, const bool supportQ) {
+                                            const AtomDecl& attributionDecl, const bool supportQ) {
     fprintf(out, "\n");
     for (auto signatureInfoMapIt = atoms.signatureInfoMap.begin();
-            signatureInfoMapIt != atoms.signatureInfoMap.end(); signatureInfoMapIt++) {
+         signatureInfoMapIt != atoms.signatureInfoMap.end(); signatureInfoMapIt++) {
         vector<java_type_t> signature = signatureInfoMapIt->first;
         const FieldNumberToAnnotations& fieldNumberToAnnotations = signatureInfoMapIt->second;
         // Key value pairs not supported in native.
         if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) {
             continue;
         }
-        write_native_method_signature(out, "int stats_write", signature,
-                attributionDecl, " {");
+        write_native_method_signature(out, "int stats_write", signature, attributionDecl, " {");
 
         int argIndex = 1;
         if (supportQ) {
             fprintf(out, "    StatsEventCompat event;\n");
             fprintf(out, "    event.setAtomId(code);\n");
+            write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAnnotations, "event.", "");
             for (vector<java_type_t>::const_iterator arg = signature.begin();
-                    arg != signature.end(); arg++) {
+                 arg != signature.end(); arg++) {
                 switch (*arg) {
                     case JAVA_TYPE_ATTRIBUTION_CHAIN: {
                         const char* uidName = attributionDecl.fields.front().name.c_str();
@@ -99,7 +91,7 @@
                     case JAVA_TYPE_BOOLEAN:
                         fprintf(out, "    event.writeBool(arg%d);\n", argIndex);
                         break;
-                    case JAVA_TYPE_INT: // Fall through.
+                    case JAVA_TYPE_INT:  // Fall through.
                     case JAVA_TYPE_ENUM:
                         fprintf(out, "    event.writeInt32(arg%d);\n", argIndex);
                         break;
@@ -124,8 +116,10 @@
         } else {
             fprintf(out, "    AStatsEvent* event = AStatsEvent_obtain();\n");
             fprintf(out, "    AStatsEvent_setAtomId(event, code);\n");
+            write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAnnotations, "AStatsEvent_",
+                              "event, ");
             for (vector<java_type_t>::const_iterator arg = signature.begin();
-                    arg != signature.end(); arg++) {
+                 arg != signature.end(); arg++) {
                 switch (*arg) {
                     case JAVA_TYPE_ATTRIBUTION_CHAIN: {
                         const char* uidName = attributionDecl.fields.front().name.c_str();
@@ -140,13 +134,14 @@
                     case JAVA_TYPE_BYTE_ARRAY:
                         fprintf(out,
                                 "    AStatsEvent_writeByteArray(event, "
-                                "reinterpret_cast<const uint8_t*>(arg%d.arg), arg%d.arg_length);\n",
+                                "reinterpret_cast<const uint8_t*>(arg%d.arg), "
+                                "arg%d.arg_length);\n",
                                 argIndex, argIndex);
                         break;
                     case JAVA_TYPE_BOOLEAN:
                         fprintf(out, "    AStatsEvent_writeBool(event, arg%d);\n", argIndex);
                         break;
-                    case JAVA_TYPE_INT: // Fall through.
+                    case JAVA_TYPE_INT:  // Fall through.
                     case JAVA_TYPE_ENUM:
                         fprintf(out, "    AStatsEvent_writeInt32(event, arg%d);\n", argIndex);
                         break;
@@ -165,7 +160,7 @@
                         return 1;
                 }
                 write_annotations(out, argIndex, fieldNumberToAnnotations, "AStatsEvent_",
-                        "event, ");
+                                  "event, ");
                 argIndex++;
             }
             fprintf(out, "    const int ret = AStatsEvent_write(event);\n");
@@ -178,10 +173,10 @@
 }
 
 static void write_native_stats_write_non_chained_methods(FILE* out, const Atoms& atoms,
-        const AtomDecl& attributionDecl) {
+                                                         const AtomDecl& attributionDecl) {
     fprintf(out, "\n");
     for (auto signature_it = atoms.nonChainedSignatureInfoMap.begin();
-            signature_it != atoms.nonChainedSignatureInfoMap.end(); signature_it++) {
+         signature_it != atoms.nonChainedSignatureInfoMap.end(); signature_it++) {
         vector<java_type_t> signature = signature_it->first;
         // Key value pairs not supported in native.
         if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) {
@@ -189,7 +184,7 @@
         }
 
         write_native_method_signature(out, "int stats_write_non_chained", signature,
-                attributionDecl, " {");
+                                      attributionDecl, " {");
 
         vector<java_type_t> newSignature;
 
@@ -212,17 +207,14 @@
 
         fprintf(out, "}\n\n");
     }
-
 }
 
 static void write_native_method_header(
-        FILE* out,
-        const string& methodName,
+        FILE* out, const string& methodName,
         const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap,
-        const AtomDecl &attributionDecl) {
-
+        const AtomDecl& attributionDecl) {
     for (auto signatureInfoMapIt = signatureInfoMap.begin();
-            signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
+         signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
         vector<java_type_t> signature = signatureInfoMapIt->first;
 
         // Key value pairs not supported in native.
@@ -233,9 +225,9 @@
     }
 }
 
-int write_stats_log_cpp(FILE *out, const Atoms &atoms, const AtomDecl &attributionDecl,
-                        const string& cppNamespace,
-                        const string& importHeader, const bool supportQ) {
+int write_stats_log_cpp(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
+                        const string& cppNamespace, const string& importHeader,
+                        const bool supportQ) {
     // Print prelude
     fprintf(out, "// This file is autogenerated\n");
     fprintf(out, "\n");
@@ -260,8 +252,8 @@
     return 0;
 }
 
-int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl,
-        const string& cppNamespace) {
+int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
+                           const string& cppNamespace) {
     // Print prelude
     fprintf(out, "// This file is autogenerated\n");
     fprintf(out, "\n");
@@ -286,21 +278,18 @@
     fprintf(out, "//\n");
     fprintf(out, "// Constants for enum values\n");
     fprintf(out, "//\n\n");
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-        atom != atoms.decls.end(); atom++) {
-
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end();
+         atom++) {
         for (vector<AtomField>::const_iterator field = atom->fields.begin();
-            field != atom->fields.end(); field++) {
+             field != atom->fields.end(); field++) {
             if (field->javaType == JAVA_TYPE_ENUM) {
-                fprintf(out, "// Values for %s.%s\n", atom->message.c_str(),
-                    field->name.c_str());
+                fprintf(out, "// Values for %s.%s\n", atom->message.c_str(), field->name.c_str());
                 for (map<int, string>::const_iterator value = field->enumValues.begin();
-                    value != field->enumValues.end(); value++) {
+                     value != field->enumValues.end(); value++) {
                     fprintf(out, "const int32_t %s__%s__%s = %d;\n",
-                        make_constant_name(atom->message).c_str(),
-                        make_constant_name(field->name).c_str(),
-                        make_constant_name(value->second).c_str(),
-                        value->first);
+                            make_constant_name(atom->message).c_str(),
+                            make_constant_name(field->name).c_str(),
+                            make_constant_name(value->second).c_str(), value->first);
                 }
                 fprintf(out, "\n");
             }
@@ -325,8 +314,8 @@
     fprintf(out, "//\n");
     fprintf(out, "// Write flattened methods\n");
     fprintf(out, "//\n");
-    write_native_method_header(out, "int stats_write_non_chained",
-            atoms.nonChainedSignatureInfoMap, attributionDecl);
+    write_native_method_header(out, "int stats_write_non_chained", atoms.nonChainedSignatureInfoMap,
+                               attributionDecl);
 
     fprintf(out, "\n");
     write_closing_namespace(out, cppNamespace);
diff --git a/tools/stats_log_api_gen/native_writer.h b/tools/stats_log_api_gen/native_writer.h
index 6e60325..264d4db 100644
--- a/tools/stats_log_api_gen/native_writer.h
+++ b/tools/stats_log_api_gen/native_writer.h
@@ -16,22 +16,22 @@
 
 #pragma once
 
-#include "Collation.h"
-
 #include <stdio.h>
 #include <string.h>
 
+#include "Collation.h"
+
 namespace android {
 namespace stats_log_api_gen {
 
 using namespace std;
 
-int write_stats_log_cpp(FILE *out, const Atoms &atoms, const AtomDecl &attributionDecl,
-        const string& cppNamespace, const string& importHeader,
-        const bool supportQ);
+int write_stats_log_cpp(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
+                        const string& cppNamespace, const string& importHeader,
+                        const bool supportQ);
 
-int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl,
-        const string& cppNamespace);
+int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
+                           const string& cppNamespace);
 
 }  // namespace stats_log_api_gen
 }  // namespace android
diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp
index 5032ac0..9878926 100644
--- a/tools/stats_log_api_gen/test_collation.cpp
+++ b/tools/stats_log_api_gen/test_collation.cpp
@@ -15,12 +15,11 @@
  */
 
 #include <gtest/gtest.h>
-
-#include "frameworks/base/tools/stats_log_api_gen/test.pb.h"
-#include "Collation.h"
-
 #include <stdio.h>
 
+#include "Collation.h"
+#include "frameworks/base/tools/stats_log_api_gen/test.pb.h"
+
 namespace android {
 namespace stats_log_api_gen {
 
@@ -31,14 +30,13 @@
 /**
  * Return whether the map contains a vector of the elements provided.
  */
-static bool
-map_contains_vector(const map<vector<java_type_t>, FieldNumberToAnnotations>& s, int count, ...)
-{
+static bool map_contains_vector(const map<vector<java_type_t>, FieldNumberToAnnotations>& s,
+                                int count, ...) {
     va_list args;
     vector<java_type_t> v;
 
     va_start(args, count);
-    for (int i=0; i<count; i++) {
+    for (int i = 0; i < count; i++) {
         v.push_back((java_type_t)va_arg(args, int));
     }
     va_end(args);
@@ -49,34 +47,33 @@
 /**
  * Expect that the provided map contains the elements provided.
  */
-#define EXPECT_MAP_CONTAINS_SIGNATURE(s, ...) \
-    do { \
-        int count = sizeof((int[]){__VA_ARGS__})/sizeof(int); \
+#define EXPECT_MAP_CONTAINS_SIGNATURE(s, ...)                    \
+    do {                                                         \
+        int count = sizeof((int[]){__VA_ARGS__}) / sizeof(int);  \
         EXPECT_TRUE(map_contains_vector(s, count, __VA_ARGS__)); \
-    } while(0)
+    } while (0)
 
 /** Expects that the provided atom has no enum values for any field. */
-#define EXPECT_NO_ENUM_FIELD(atom) \
-    do { \
+#define EXPECT_NO_ENUM_FIELD(atom)                                           \
+    do {                                                                     \
         for (vector<AtomField>::const_iterator field = atom->fields.begin(); \
-             field != atom->fields.end(); field++) { \
-            EXPECT_TRUE(field->enumValues.empty()); \
-        } \
-    } while(0)
+             field != atom->fields.end(); field++) {                         \
+            EXPECT_TRUE(field->enumValues.empty());                          \
+        }                                                                    \
+    } while (0)
 
 /** Expects that exactly one specific field has expected enum values. */
-#define EXPECT_HAS_ENUM_FIELD(atom, field_name, values)        \
-    do { \
+#define EXPECT_HAS_ENUM_FIELD(atom, field_name, values)                      \
+    do {                                                                     \
         for (vector<AtomField>::const_iterator field = atom->fields.begin(); \
-             field != atom->fields.end(); field++) { \
-            if (field->name == field_name) { \
-                EXPECT_EQ(field->enumValues, values); \
-            } else { \
-                EXPECT_TRUE(field->enumValues.empty()); \
-            } \
-        } \
-    } while(0)
-
+             field != atom->fields.end(); field++) {                         \
+            if (field->name == field_name) {                                 \
+                EXPECT_EQ(field->enumValues, values);                        \
+            } else {                                                         \
+                EXPECT_TRUE(field->enumValues.empty());                      \
+            }                                                                \
+        }                                                                    \
+    } while (0)
 
 /**
  * Test a correct collation, with all the types.
@@ -95,23 +92,22 @@
     EXPECT_MAP_CONTAINS_SIGNATURE(atoms.signatureInfoMap, JAVA_TYPE_INT, JAVA_TYPE_INT);
 
     // AllTypesAtom
-    EXPECT_MAP_CONTAINS_SIGNATURE(
-        atoms.signatureInfoMap,
-        JAVA_TYPE_ATTRIBUTION_CHAIN, // AttributionChain
-        JAVA_TYPE_FLOAT,             // float
-        JAVA_TYPE_LONG,              // int64
-        JAVA_TYPE_LONG,              // uint64
-        JAVA_TYPE_INT,               // int32
-        JAVA_TYPE_LONG,              // fixed64
-        JAVA_TYPE_INT,               // fixed32
-        JAVA_TYPE_BOOLEAN,           // bool
-        JAVA_TYPE_STRING,            // string
-        JAVA_TYPE_INT,               // uint32
-        JAVA_TYPE_INT,               // AnEnum
-        JAVA_TYPE_INT,               // sfixed32
-        JAVA_TYPE_LONG,              // sfixed64
-        JAVA_TYPE_INT,               // sint32
-        JAVA_TYPE_LONG               // sint64
+    EXPECT_MAP_CONTAINS_SIGNATURE(atoms.signatureInfoMap,
+                                  JAVA_TYPE_ATTRIBUTION_CHAIN,  // AttributionChain
+                                  JAVA_TYPE_FLOAT,              // float
+                                  JAVA_TYPE_LONG,               // int64
+                                  JAVA_TYPE_LONG,               // uint64
+                                  JAVA_TYPE_INT,                // int32
+                                  JAVA_TYPE_LONG,               // fixed64
+                                  JAVA_TYPE_INT,                // fixed32
+                                  JAVA_TYPE_BOOLEAN,            // bool
+                                  JAVA_TYPE_STRING,             // string
+                                  JAVA_TYPE_INT,                // uint32
+                                  JAVA_TYPE_INT,                // AnEnum
+                                  JAVA_TYPE_INT,                // sfixed32
+                                  JAVA_TYPE_LONG,               // sfixed64
+                                  JAVA_TYPE_INT,                // sint32
+                                  JAVA_TYPE_LONG                // sint64
     );
 
     set<AtomDecl>::const_iterator atom = atoms.decls.begin();
@@ -156,7 +152,8 @@
 }
 
 /**
- * Test that atoms that have non-primitive types or repeated fields are rejected.
+ * Test that atoms that have non-primitive types or repeated fields are
+ * rejected.
  */
 TEST(CollationTest, FailOnBadTypes) {
     Atoms atoms;
@@ -170,18 +167,20 @@
  */
 TEST(CollationTest, FailOnSkippedFieldsSingle) {
     Atoms atoms;
-    int errorCount = collate_atoms(BadSkippedFieldSingle::descriptor(), DEFAULT_MODULE_NAME, &atoms);
+    int errorCount =
+            collate_atoms(BadSkippedFieldSingle::descriptor(), DEFAULT_MODULE_NAME, &atoms);
 
     EXPECT_EQ(1, errorCount);
 }
 
 /**
- * Test that atoms that skip field numbers (not in the first position, and multiple
- * times) are rejected.
+ * Test that atoms that skip field numbers (not in the first position, and
+ * multiple times) are rejected.
  */
 TEST(CollationTest, FailOnSkippedFieldsMultiple) {
     Atoms atoms;
-    int errorCount = collate_atoms(BadSkippedFieldMultiple::descriptor(), DEFAULT_MODULE_NAME, &atoms);
+    int errorCount =
+            collate_atoms(BadSkippedFieldMultiple::descriptor(), DEFAULT_MODULE_NAME, &atoms);
 
     EXPECT_EQ(2, errorCount);
 }
@@ -191,11 +190,11 @@
  * rejected.
  */
 TEST(CollationTest, FailBadAttributionNodePosition) {
-  Atoms atoms;
-  int errorCount =
-      collate_atoms(BadAttributionNodePosition::descriptor(), DEFAULT_MODULE_NAME, &atoms);
+    Atoms atoms;
+    int errorCount =
+            collate_atoms(BadAttributionNodePosition::descriptor(), DEFAULT_MODULE_NAME, &atoms);
 
-  EXPECT_EQ(1, errorCount);
+    EXPECT_EQ(1, errorCount);
 }
 
 TEST(CollationTest, FailOnBadStateAtomOptions) {
@@ -270,8 +269,8 @@
             const set<shared_ptr<Annotation>>& annotations = fieldNumberToAnnotations.at(1);
             EXPECT_EQ(2u, annotations.size());
             for (const shared_ptr<Annotation> annotation : annotations) {
-                EXPECT_TRUE(annotation->annotationId == ANNOTATION_ID_IS_UID
-                        || annotation->annotationId == ANNOTATION_ID_STATE_OPTION);
+                EXPECT_TRUE(annotation->annotationId == ANNOTATION_ID_IS_UID ||
+                            annotation->annotationId == ANNOTATION_ID_STATE_OPTION);
                 if (ANNOTATION_ID_IS_UID == annotation->annotationId) {
                     EXPECT_EQ(1, annotation->atomId);
                     EXPECT_EQ(ANNOTATION_TYPE_BOOL, annotation->type);
@@ -303,12 +302,11 @@
         EXPECT_EQ(1u, fieldNumberToAnnotations.size());
         int fieldNumber = 1;
         EXPECT_NE(fieldNumberToAnnotations.end(), fieldNumberToAnnotations.find(fieldNumber));
-        const set<shared_ptr<Annotation>>& annotations =
-                fieldNumberToAnnotations.at(fieldNumber);
+        const set<shared_ptr<Annotation>>& annotations = fieldNumberToAnnotations.at(fieldNumber);
         EXPECT_EQ(2u, annotations.size());
         for (const shared_ptr<Annotation> annotation : annotations) {
-            EXPECT_TRUE(annotation->annotationId == ANNOTATION_ID_IS_UID
-                    || annotation->annotationId == ANNOTATION_ID_STATE_OPTION);
+            EXPECT_TRUE(annotation->annotationId == ANNOTATION_ID_IS_UID ||
+                        annotation->annotationId == ANNOTATION_ID_STATE_OPTION);
             if (ANNOTATION_ID_IS_UID == annotation->annotationId) {
                 EXPECT_EQ(1, annotation->atomId);
                 EXPECT_EQ(ANNOTATION_TYPE_BOOL, annotation->type);
diff --git a/tools/stats_log_api_gen/utils.cpp b/tools/stats_log_api_gen/utils.cpp
index 7314127..0262488 100644
--- a/tools/stats_log_api_gen/utils.cpp
+++ b/tools/stats_log_api_gen/utils.cpp
@@ -22,9 +22,9 @@
 namespace stats_log_api_gen {
 
 static void build_non_chained_decl_map(const Atoms& atoms,
-                                std::map<int, set<AtomDecl>::const_iterator>* decl_map) {
+                                       std::map<int, set<AtomDecl>::const_iterator>* decl_map) {
     for (set<AtomDecl>::const_iterator atom = atoms.non_chained_decls.begin();
-        atom != atoms.non_chained_decls.end(); atom++) {
+         atom != atoms.non_chained_decls.end(); atom++) {
         decl_map->insert(std::make_pair(atom->code, atom));
     }
 }
@@ -36,7 +36,7 @@
     string result;
     const int N = str.size();
     bool underscore_next = false;
-    for (int i=0; i<N; i++) {
+    for (int i = 0; i < N; i++) {
         char c = str[i];
         if (c >= 'A' && c <= 'Z') {
             if (underscore_next) {
@@ -99,7 +99,8 @@
 }
 
 // Native
-// Writes namespaces for the cpp and header files, returning the number of namespaces written.
+// Writes namespaces for the cpp and header files, returning the number of
+// namespaces written.
 void write_namespace(FILE* out, const string& cppNamespaces) {
     vector<string> cppNamespaceVec = android::base::Split(cppNamespaces, ",");
     for (string cppNamespace : cppNamespaceVec) {
@@ -115,35 +116,31 @@
     }
 }
 
-static void write_cpp_usage(
-    FILE* out, const string& method_name, const string& atom_code_name,
-    const AtomDecl& atom, const AtomDecl &attributionDecl) {
-    fprintf(out, "     * Usage: %s(StatsLog.%s", method_name.c_str(),
-            atom_code_name.c_str());
+static void write_cpp_usage(FILE* out, const string& method_name, const string& atom_code_name,
+                            const AtomDecl& atom, const AtomDecl& attributionDecl) {
+    fprintf(out, "     * Usage: %s(StatsLog.%s", method_name.c_str(), atom_code_name.c_str());
 
-    for (vector<AtomField>::const_iterator field = atom.fields.begin();
-            field != atom.fields.end(); field++) {
+    for (vector<AtomField>::const_iterator field = atom.fields.begin(); field != atom.fields.end();
+         field++) {
         if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
             for (auto chainField : attributionDecl.fields) {
                 if (chainField.javaType == JAVA_TYPE_STRING) {
-                    fprintf(out, ", const std::vector<%s>& %s",
-                         cpp_type_name(chainField.javaType),
-                         chainField.name.c_str());
+                    fprintf(out, ", const std::vector<%s>& %s", cpp_type_name(chainField.javaType),
+                            chainField.name.c_str());
                 } else {
                     fprintf(out, ", const %s* %s, size_t %s_length",
-                         cpp_type_name(chainField.javaType),
-                         chainField.name.c_str(), chainField.name.c_str());
+                            cpp_type_name(chainField.javaType), chainField.name.c_str(),
+                            chainField.name.c_str());
                 }
             }
         } else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) {
-            fprintf(out, ", const std::map<int, int32_t>& %s_int"
-                         ", const std::map<int, int64_t>& %s_long"
-                         ", const std::map<int, char const*>& %s_str"
-                         ", const std::map<int, float>& %s_float",
-                         field->name.c_str(),
-                         field->name.c_str(),
-                         field->name.c_str(),
-                         field->name.c_str());
+            fprintf(out,
+                    ", const std::map<int, int32_t>& %s_int"
+                    ", const std::map<int, int64_t>& %s_long"
+                    ", const std::map<int, char const*>& %s_str"
+                    ", const std::map<int, float>& %s_float",
+                    field->name.c_str(), field->name.c_str(), field->name.c_str(),
+                    field->name.c_str());
         } else {
             fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
         }
@@ -162,8 +159,8 @@
 
     size_t i = 0;
     // Print atom constants
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-        atom != atoms.decls.end(); atom++) {
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end();
+         atom++) {
         string constant = make_constant_name(atom->name);
         fprintf(out, "\n");
         fprintf(out, "    /**\n");
@@ -173,7 +170,7 @@
         auto non_chained_decl = atom_code_to_non_chained_decl_map.find(atom->code);
         if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
             write_cpp_usage(out, "stats_write_non_chained", constant, *non_chained_decl->second,
-                attributionDecl);
+                            attributionDecl);
         }
         fprintf(out, "     */\n");
         char const* const comma = (i == atoms.decls.size() - 1) ? "" : ",";
@@ -186,30 +183,30 @@
 }
 
 void write_native_method_signature(FILE* out, const string& methodName,
-        const vector<java_type_t>& signature, const AtomDecl& attributionDecl,
-        const string& closer) {
+                                   const vector<java_type_t>& signature,
+                                   const AtomDecl& attributionDecl, const string& closer) {
     fprintf(out, "%s(int32_t code", methodName.c_str());
     int argIndex = 1;
-    for (vector<java_type_t>::const_iterator arg = signature.begin();
-        arg != signature.end(); arg++) {
+    for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+         arg++) {
         if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
             for (auto chainField : attributionDecl.fields) {
                 if (chainField.javaType == JAVA_TYPE_STRING) {
-                        fprintf(out, ", const std::vector<%s>& %s",
-                             cpp_type_name(chainField.javaType),
-                             chainField.name.c_str());
+                    fprintf(out, ", const std::vector<%s>& %s", cpp_type_name(chainField.javaType),
+                            chainField.name.c_str());
                 } else {
-                        fprintf(out, ", const %s* %s, size_t %s_length",
-                             cpp_type_name(chainField.javaType),
-                             chainField.name.c_str(), chainField.name.c_str());
+                    fprintf(out, ", const %s* %s, size_t %s_length",
+                            cpp_type_name(chainField.javaType), chainField.name.c_str(),
+                            chainField.name.c_str());
                 }
             }
         } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
-            fprintf(out, ", const std::map<int, int32_t>& arg%d_1, "
-                         "const std::map<int, int64_t>& arg%d_2, "
-                         "const std::map<int, char const*>& arg%d_3, "
-                         "const std::map<int, float>& arg%d_4",
-                         argIndex, argIndex, argIndex, argIndex);
+            fprintf(out,
+                    ", const std::map<int, int32_t>& arg%d_1, "
+                    "const std::map<int, int64_t>& arg%d_2, "
+                    "const std::map<int, char const*>& arg%d_3, "
+                    "const std::map<int, float>& arg%d_4",
+                    argIndex, argIndex, argIndex, argIndex);
         } else {
             fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
         }
@@ -219,27 +216,27 @@
 }
 
 void write_native_method_call(FILE* out, const string& methodName,
-        const vector<java_type_t>& signature, const AtomDecl& attributionDecl, int argIndex) {
+                              const vector<java_type_t>& signature, const AtomDecl& attributionDecl,
+                              int argIndex) {
     fprintf(out, "%s(code", methodName.c_str());
-    for (vector<java_type_t>::const_iterator arg = signature.begin();
-       arg != signature.end(); arg++) {
-       if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
-           for (auto chainField : attributionDecl.fields) {
-               if (chainField.javaType == JAVA_TYPE_STRING) {
-                       fprintf(out, ", %s",
+    for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+         arg++) {
+        if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+            for (auto chainField : attributionDecl.fields) {
+                if (chainField.javaType == JAVA_TYPE_STRING) {
+                    fprintf(out, ", %s", chainField.name.c_str());
+                } else {
+                    fprintf(out, ",  %s,  %s_length", chainField.name.c_str(),
                             chainField.name.c_str());
-               } else {
-                       fprintf(out, ",  %s,  %s_length",
-                            chainField.name.c_str(), chainField.name.c_str());
-               }
-           }
-       } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
-           fprintf(out, ", arg%d_1, arg%d_2, arg%d_3, arg%d_4", argIndex,
-                   argIndex, argIndex, argIndex);
-       } else {
-           fprintf(out, ", arg%d", argIndex);
-       }
-       argIndex++;
+                }
+            }
+        } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
+            fprintf(out, ", arg%d_1, arg%d_2, arg%d_3, arg%d_4", argIndex, argIndex, argIndex,
+                    argIndex);
+        } else {
+            fprintf(out, ", arg%d", argIndex);
+        }
+        argIndex++;
     }
     fprintf(out, ");\n");
 }
@@ -252,8 +249,8 @@
     build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
 
     // Print constants for the atom codes.
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-            atom != atoms.decls.end(); atom++) {
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end();
+         atom++) {
         string constant = make_constant_name(atom->name);
         fprintf(out, "\n");
         fprintf(out, "    /**\n");
@@ -271,20 +268,19 @@
 
 void write_java_enum_values(FILE* out, const Atoms& atoms) {
     fprintf(out, "    // Constants for enum values.\n\n");
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-        atom != atoms.decls.end(); atom++) {
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end();
+         atom++) {
         for (vector<AtomField>::const_iterator field = atom->fields.begin();
-            field != atom->fields.end(); field++) {
+             field != atom->fields.end(); field++) {
             if (field->javaType == JAVA_TYPE_ENUM) {
                 fprintf(out, "    // Values for %s.%s\n", atom->message.c_str(),
-                    field->name.c_str());
+                        field->name.c_str());
                 for (map<int, string>::const_iterator value = field->enumValues.begin();
-                    value != field->enumValues.end(); value++) {
+                     value != field->enumValues.end(); value++) {
                     fprintf(out, "    public static final int %s__%s__%s = %d;\n",
-                        make_constant_name(atom->message).c_str(),
-                        make_constant_name(field->name).c_str(),
-                        make_constant_name(value->second).c_str(),
-                        value->first);
+                            make_constant_name(atom->message).c_str(),
+                            make_constant_name(field->name).c_str(),
+                            make_constant_name(value->second).c_str(), value->first);
                 }
                 fprintf(out, "\n");
             }
@@ -293,11 +289,11 @@
 }
 
 void write_java_usage(FILE* out, const string& method_name, const string& atom_code_name,
-        const AtomDecl& atom) {
-    fprintf(out, "     * Usage: StatsLog.%s(StatsLog.%s",
-        method_name.c_str(), atom_code_name.c_str());
-    for (vector<AtomField>::const_iterator field = atom.fields.begin();
-        field != atom.fields.end(); field++) {
+                      const AtomDecl& atom) {
+    fprintf(out, "     * Usage: StatsLog.%s(StatsLog.%s", method_name.c_str(),
+            atom_code_name.c_str());
+    for (vector<AtomField>::const_iterator field = atom.fields.begin(); field != atom.fields.end();
+         field++) {
         if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
             fprintf(out, ", android.os.WorkSource workSource");
         } else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) {
@@ -312,16 +308,15 @@
 }
 
 int write_java_non_chained_methods(
-        FILE* out,
-        const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap) {
+        FILE* out, const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap) {
     for (auto signatureInfoMapIt = signatureInfoMap.begin();
-            signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
+         signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
         // Print method signature.
         fprintf(out, "    public static void write_non_chained(int code");
         vector<java_type_t> signature = signatureInfoMapIt->first;
         int argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin();
-                arg != signature.end(); arg++) {
+        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+             arg++) {
             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
                 fprintf(stderr, "Non chained signatures should not have attribution chains.\n");
                 return 1;
@@ -337,8 +332,8 @@
 
         fprintf(out, "        write(code");
         argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin();
-                arg != signature.end(); arg++) {
+        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+             arg++) {
             // First two args are uid and tag of attribution chain.
             if (argIndex == 1) {
                 fprintf(out, ", new int[] {arg%d}", argIndex);
@@ -357,23 +352,24 @@
 }
 
 int write_java_work_source_methods(
-        FILE* out,
-        const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap) {
+        FILE* out, const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap) {
     fprintf(out, "    // WorkSource methods.\n");
     for (auto signatureInfoMapIt = signatureInfoMap.begin();
-            signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
+         signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
         vector<java_type_t> signature = signatureInfoMapIt->first;
         // Determine if there is Attribution in this signature.
         int attributionArg = -1;
         int argIndexMax = 0;
-        for (vector<java_type_t>::const_iterator arg = signature.begin();
-                arg != signature.end(); arg++) {
+        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+             arg++) {
             argIndexMax++;
             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
                 if (attributionArg > -1) {
                     fprintf(stderr, "An atom contains multiple AttributionNode fields.\n");
                     fprintf(stderr, "This is not supported. Aborting WorkSource method writing.\n");
-                    fprintf(out, "\n// Invalid for WorkSource: more than one attribution chain.\n");
+                    fprintf(out,
+                            "\n// Invalid for WorkSource: more than one attribution "
+                            "chain.\n");
                     return 1;
                 }
                 attributionArg = argIndexMax;
@@ -387,8 +383,8 @@
         // Method header (signature)
         fprintf(out, "    public static void write(int code");
         int argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin();
-                arg != signature.end(); arg++) {
+        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+             arg++) {
             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
                 fprintf(out, ", android.os.WorkSource ws");
             } else {
@@ -398,36 +394,40 @@
         }
         fprintf(out, ") {\n");
 
-        // write_non_chained() component. TODO: Remove when flat uids are no longer needed.
+        // write_non_chained() component. TODO: Remove when flat uids are no longer
+        // needed.
         fprintf(out, "        for (int i = 0; i < ws.size(); ++i) {\n");
         fprintf(out, "            write_non_chained(code");
         for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) {
             if (argIndex == attributionArg) {
                 fprintf(out, ", ws.getUid(i), ws.getPackageName(i)");
             } else {
-               fprintf(out, ", arg%d", argIndex);
+                fprintf(out, ", arg%d", argIndex);
             }
         }
         fprintf(out, ");\n");
-        fprintf(out, "        }\n"); // close for-loop
+        fprintf(out, "        }\n");  // close for-loop
 
         // write() component.
-        fprintf(out, "        java.util.List<android.os.WorkSource.WorkChain> workChains = "
+        fprintf(out,
+                "        java.util.List<android.os.WorkSource.WorkChain> workChains = "
                 "ws.getWorkChains();\n");
         fprintf(out, "        if (workChains != null) {\n");
-        fprintf(out, "            for (android.os.WorkSource.WorkChain wc : workChains) {\n");
+        fprintf(out,
+                "            for (android.os.WorkSource.WorkChain wc : workChains) "
+                "{\n");
         fprintf(out, "                write(code");
         for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) {
             if (argIndex == attributionArg) {
                 fprintf(out, ", wc.getUids(), wc.getTags()");
             } else {
-               fprintf(out, ", arg%d", argIndex);
+                fprintf(out, ", arg%d", argIndex);
             }
         }
         fprintf(out, ");\n");
-        fprintf(out, "            }\n"); // close for-loop
-        fprintf(out, "        }\n"); // close if
-        fprintf(out, "    }\n"); // close method
+        fprintf(out, "            }\n");  // close for-loop
+        fprintf(out, "        }\n");      // close if
+        fprintf(out, "    }\n");          // close method
     }
     return 0;
 }
diff --git a/tools/stats_log_api_gen/utils.h b/tools/stats_log_api_gen/utils.h
index a6b3ef9..468f323 100644
--- a/tools/stats_log_api_gen/utils.h
+++ b/tools/stats_log_api_gen/utils.h
@@ -16,14 +16,14 @@
 
 #pragma once
 
-#include "Collation.h"
+#include <stdio.h>
+#include <string.h>
 
 #include <map>
 #include <set>
 #include <vector>
 
-#include <stdio.h>
-#include <string.h>
+#include "Collation.h"
 
 namespace android {
 namespace stats_log_api_gen {
@@ -52,11 +52,12 @@
 void write_native_atom_constants(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl);
 
 void write_native_method_signature(FILE* out, const string& methodName,
-        const vector<java_type_t>& signature, const AtomDecl& attributionDecl,
-        const string& closer);
+                                   const vector<java_type_t>& signature,
+                                   const AtomDecl& attributionDecl, const string& closer);
 
 void write_native_method_call(FILE* out, const string& methodName,
-        const vector<java_type_t>& signature, const AtomDecl& attributionDecl, int argIndex = 1);
+                              const vector<java_type_t>& signature, const AtomDecl& attributionDecl,
+                              int argIndex = 1);
 
 // Common Java helpers.
 void write_java_atom_codes(FILE* out, const Atoms& atoms);
@@ -64,14 +65,13 @@
 void write_java_enum_values(FILE* out, const Atoms& atoms);
 
 void write_java_usage(FILE* out, const string& method_name, const string& atom_code_name,
-        const AtomDecl& atom);
+                      const AtomDecl& atom);
 
-int write_java_non_chained_methods(FILE* out, const map<vector<java_type_t>,
-        FieldNumberToAnnotations>& signatureInfoMap);
+int write_java_non_chained_methods(
+        FILE* out, const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap);
 
 int write_java_work_source_methods(
-        FILE* out,
-        const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap);
+        FILE* out, const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap);
 
 }  // namespace stats_log_api_gen
 }  // namespace android
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 9d9a495..d0f1a26 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -170,24 +170,23 @@
 java_library {
     name: "framework-wifi-stubs-publicapi",
     srcs: [":framework-wifi-stubs-srcs-publicapi"],
+    defaults: ["framework-module-stubs-lib-defaults-publicapi"],
+    // TODO(b/151134996): remove this
     sdk_version: "current",
-    installable: false,
 }
 
 java_library {
     name: "framework-wifi-stubs-systemapi",
     srcs: [":framework-wifi-stubs-srcs-systemapi"],
-    sdk_version: "system_current",
     libs: ["framework-annotations-lib"],
-    installable: false,
+    defaults: ["framework-module-stubs-lib-defaults-systemapi"],
 }
 
 java_library {
     name: "framework-wifi-stubs-module_libs_api",
     srcs: [":framework-wifi-stubs-srcs-module_libs_api"],
-    sdk_version: "module_current",
     libs: ["framework-annotations-lib"],
-    installable: false,
+    defaults: ["framework-module-stubs-lib-defaults-module_libs_api"],
 }
 
 // defaults for tests that need to build against framework-wifi's @hide APIs
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index ba68d17..b110a61 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -2356,6 +2356,8 @@
         sbuf.append(" lcuid=" + lastConnectUid);
         sbuf.append(" allowAutojoin=" + allowAutojoin);
         sbuf.append(" noInternetAccessExpected=" + noInternetAccessExpected);
+        sbuf.append(" mostRecentlyConnected=" + isMostRecentlyConnected);
+
         sbuf.append(" ");
 
         if (this.lastConnected != 0) {
@@ -2964,4 +2966,11 @@
         return mPasspointUniqueId;
     }
 
+    /**
+     * If network is one of the most recently connected.
+     * For framework internal use only. Do not parcel.
+     * @hide
+     */
+    public boolean isMostRecentlyConnected = false;
+
 }
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>