Merge "CellularDataService: mCallbackMap is not thread safe" into rvc-dev
diff --git a/Android.bp b/Android.bp
index 78d38c5..53417e8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -281,17 +281,33 @@
 filegroup {
     name: "framework-updatable-sources",
     srcs: [
-        ":framework-sdkextensions-sources",
-        ":framework-statsd-sources",
-        ":framework-tethering-srcs",
-        ":updatable-media-srcs",
         ":framework-mediaprovider-sources",
         ":framework-permission-sources",
-        ":framework-wifi-updatable-sources",
+        ":framework-sdkextensions-sources",
+        ":framework-statsd-sources",
         ":framework-telephony-sources",
+        ":framework-tethering-srcs",
+        ":framework-wifi-updatable-sources",
+        ":updatable-media-srcs",
     ]
 }
 
+java_library {
+    name: "framework-updatable-stubs-module_libs_api",
+    static_libs: [
+        "framework-media-stubs-module_libs_api",
+        "framework-mediaprovider-stubs-module_libs_api",
+        "framework-permission-stubs-module_libs_api",
+        "framework-sdkextensions-stubs-module_libs_api",
+        "framework-statsd-stubs-module_libs_api",
+        "framework-telephony-stubs", // TODO: Update to module_libs_api when there is one.
+        "framework-tethering-stubs-module_libs_api",
+        "framework-wifi-stubs-module_libs_api",
+    ],
+    sdk_version: "module_current",
+    visibility: [":__pkg__"],
+}
+
 filegroup {
     name: "framework-all-sources",
     srcs: [
@@ -307,7 +323,6 @@
     name: "framework-aidl-export-defaults",
     aidl: {
         export_include_dirs: [
-            "apex/media/framework/java",
             "core/java",
             "drm/java",
             "graphics/java",
@@ -324,6 +339,12 @@
             "rs/java",
             "sax/java",
             "telecomm/java",
+
+            // TODO(b/148660295): remove this
+            "apex/media/framework/java",
+
+            // TODO(b/147699819): remove this
+            "telephony/java",
         ],
     },
 }
@@ -397,9 +418,7 @@
         "app-compat-annotations",
         "ext",
         "unsupportedappusage",
-        "framework-media-stubs-systemapi",
-        "framework-mediaprovider-stubs-systemapi",
-        "framework-telephony-stubs",
+        "framework-updatable-stubs-module_libs_api",
     ],
 
     jarjar_rules: ":framework-jarjar-rules",
@@ -465,13 +484,6 @@
     name: "framework-minus-apex",
     defaults: ["framework-defaults"],
     srcs: [":framework-non-updatable-sources"],
-    libs: [
-        "framework-sdkextensions-stubs-systemapi",
-        "framework-statsd-stubs-module_libs_api",
-        "framework-permission-stubs-systemapi",
-        "framework-wifi-stubs-systemapi",
-        "framework-tethering-stubs",
-    ],
     installable: true,
     javac_shard_size: 150,
     required: [
@@ -512,16 +524,9 @@
     installable: false, // this lib is a build-only library
     static_libs: [
         "framework-minus-apex",
-        "framework-media-stubs-systemapi",
-        "framework-mediaprovider-stubs-systemapi",
-        "framework-permission-stubs-systemapi",
-        "framework-sdkextensions-stubs-systemapi",
-        "framework-statsd-stubs-module_libs_api",
-        "framework-wifi-stubs-systemapi",
-        "framework-tethering-stubs",
-        // TODO (b/147688669) should be framework-telephony-stubs
+        // TODO (b/147688669) should be removed
         "framework-telephony",
-        // TODO(jiyong): add stubs for APEXes here
+        "framework-updatable-stubs-module_libs_api",
     ],
     sdk_version: "core_platform",
     apex_available: ["//apex_available:platform"],
@@ -540,9 +545,7 @@
     visibility: [
         // DO NOT ADD ANY MORE ENTRIES TO THIS LIST
         "//external/robolectric-shadows:__subpackages__",
-        "//frameworks/base/packages/Tethering/common/TetheringLib:__subpackages__",
         "//frameworks/layoutlib:__subpackages__",
-        "//frameworks/opt/net/ike:__subpackages__",
     ],
 }
 
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index c458d11..e042782 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -670,7 +670,7 @@
     private void startApp(int userId, String packageName) throws RemoteException {
         final Context context = InstrumentationRegistry.getContext();
         final WaitResult result = ActivityTaskManager.getService().startActivityAndWait(null,
-                context.getPackageName(), context.getFeatureId(),
+                context.getPackageName(), context.getAttributionTag(),
                 context.getPackageManager().getLaunchIntentForPackage(packageName), null, null,
                 null, 0, 0, null, null, userId);
         attestTrue("User " + userId + " failed to start " + packageName,
diff --git a/apex/Android.bp b/apex/Android.bp
index 051986e..1510911 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -43,6 +43,7 @@
     name: "framework-module-stubs-defaults-publicapi",
     args: mainline_stubs_args,
     installable: false,
+    sdk_version: "current",
 }
 
 stubs_defaults {
@@ -50,6 +51,7 @@
     args: mainline_stubs_args + priv_apps,
     srcs: [":framework-annotations"],
     installable: false,
+    sdk_version: "system_current",
 }
 
 // The defaults for module_libs comes in two parts - defaults for API checks
@@ -62,6 +64,7 @@
     args: mainline_stubs_args + module_libs,
     srcs: [":framework-annotations"],
     installable: false,
+    sdk_version: "module_current",
 }
 
 stubs_defaults {
@@ -69,4 +72,5 @@
     args: mainline_stubs_args + module_libs + priv_apps,
     srcs: [":framework-annotations"],
     installable: false,
+    sdk_version: "module_current",
 }
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index b905273..ae8976a 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -53,9 +53,8 @@
  * Container of data passed to the {@link android.app.job.JobScheduler} fully encapsulating the
  * parameters required to schedule work against the calling application. These are constructed
  * using the {@link JobInfo.Builder}.
- * You must specify at least one sort of constraint on the JobInfo object that you are creating.
  * The goal here is to provide the scheduler with high-level semantics about the work you want to
- * accomplish. Doing otherwise with throw an exception in your app.
+ * accomplish.
  */
 public class JobInfo implements Parcelable {
     private static String TAG = "JobInfo";
@@ -147,7 +146,7 @@
 
     /**
      * Query the minimum interval allowed for periodic scheduled jobs.  Attempting
-     * to declare a smaller period that this when scheduling a job will result in a
+     * to declare a smaller period than this when scheduling a job will result in a
      * job that is still periodic, but will run with this effective period.
      *
      * @return The minimum available interval for scheduling periodic jobs, in milliseconds.
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index b219fd41..29c48ad 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -139,7 +139,7 @@
  *     @Override
  *     public void onSampleCompleted(
  *         int trackIndex,
- *         long timeUs,
+ *         long timeMicros,
  *         int flags,
  *         int size,
  *         int offset,
@@ -163,7 +163,7 @@
  *           /* destPos= */ 0,
  *           /* size= */ offset);
  *       bytesWrittenCount = bytesWrittenCount - offset;
- *       publishSample(sampleData, timeUs, flags);
+ *       publishSample(sampleData, timeMicros, flags);
  *     }
  *
  *    private void ensureSpaceInBuffer(int numberOfBytesToRead) {
@@ -187,7 +187,7 @@
      */
     public static final class SeekMap {
 
-        /** Returned by {@link #getDurationUs()} when the duration is unknown. */
+        /** Returned by {@link #getDurationMicros()} when the duration is unknown. */
         public static final int UNKNOWN_DURATION = Integer.MIN_VALUE;
 
         private final com.google.android.exoplayer2.extractor.SeekMap mExoPlayerSeekMap;
@@ -205,26 +205,26 @@
          * Returns the duration of the stream in microseconds or {@link #UNKNOWN_DURATION} if the
          * duration is unknown.
          */
-        public long getDurationUs() {
+        public long getDurationMicros() {
             return mExoPlayerSeekMap.getDurationUs();
         }
 
         /**
          * Obtains {@link SeekPoint SeekPoints} for the specified seek time in microseconds.
          *
-         * <p>{@code getSeekPoints(timeUs).first} contains the latest seek point for samples with
-         * timestamp equal to or smaller than {@code timeUs}.
+         * <p>{@code getSeekPoints(timeMicros).first} contains the latest seek point for samples
+         * with timestamp equal to or smaller than {@code timeMicros}.
          *
-         * <p>{@code getSeekPoints(timeUs).second} contains the earliest seek point for samples with
-         * timestamp equal to or greater than {@code timeUs}. If a seek point exists for {@code
-         * timeUs}, the returned pair will contain the same {@link SeekPoint} twice.
+         * <p>{@code getSeekPoints(timeMicros).second} contains the earliest seek point for samples
+         * with timestamp equal to or greater than {@code timeMicros}. If a seek point exists for
+         * {@code timeMicros}, the returned pair will contain the same {@link SeekPoint} twice.
          *
-         * @param timeUs A seek time in microseconds.
+         * @param timeMicros A seek time in microseconds.
          * @return The corresponding {@link SeekPoint SeekPoints}.
          */
         @NonNull
-        public Pair<SeekPoint, SeekPoint> getSeekPoints(long timeUs) {
-            SeekPoints seekPoints = mExoPlayerSeekMap.getSeekPoints(timeUs);
+        public Pair<SeekPoint, SeekPoint> getSeekPoints(long timeMicros) {
+            SeekPoints seekPoints = mExoPlayerSeekMap.getSeekPoints(timeMicros);
             return new Pair<>(toSeekPoint(seekPoints.first), toSeekPoint(seekPoints.second));
         }
     }
@@ -254,24 +254,24 @@
         @NonNull public static final SeekPoint START = new SeekPoint(0, 0);
 
         /** The time of the seek point, in microseconds. */
-        public final long timeUs;
+        public final long timeMicros;
 
         /** The byte offset of the seek point. */
         public final long position;
 
         /**
-         * @param timeUs The time of the seek point, in microseconds.
+         * @param timeMicros The time of the seek point, in microseconds.
          * @param position The byte offset of the seek point.
          */
-        private SeekPoint(long timeUs, long position) {
-            this.timeUs = timeUs;
+        private SeekPoint(long timeMicros, long position) {
+            this.timeMicros = timeMicros;
             this.position = position;
         }
 
         @Override
         @NonNull
         public String toString() {
-            return "[timeUs=" + timeUs + ", position=" + position + "]";
+            return "[timeMicros=" + timeMicros + ", position=" + position + "]";
         }
 
         @Override
@@ -283,12 +283,12 @@
                 return false;
             }
             SeekPoint other = (SeekPoint) obj;
-            return timeUs == other.timeUs && position == other.position;
+            return timeMicros == other.timeMicros && position == other.position;
         }
 
         @Override
         public int hashCode() {
-            int result = (int) timeUs;
+            int result = (int) timeMicros;
             result = 31 * result + (int) position;
             return result;
         }
@@ -345,25 +345,25 @@
          *
          * @param seekMap The extracted {@link SeekMap}.
          */
-        void onSeekMap(@NonNull SeekMap seekMap);
+        void onSeekMapFound(@NonNull SeekMap seekMap);
 
         /**
          * Called when the number of tracks is found.
          *
          * @param numberOfTracks The number of tracks in the stream.
          */
-        void onTracksFound(int numberOfTracks);
+        void onTrackCountFound(int numberOfTracks);
 
         /**
-         * Called when new {@link TrackData} is extracted from the stream.
+         * Called when new {@link TrackData} is found in the stream.
          *
          * @param trackIndex The index of the track for which the {@link TrackData} was extracted.
          * @param trackData The extracted {@link TrackData}.
          */
-        void onTrackData(int trackIndex, @NonNull TrackData trackData);
+        void onTrackDataFound(int trackIndex, @NonNull TrackData trackData);
 
         /**
-         * Called to write sample data to the output.
+         * Called when sample data is found in the stream.
          *
          * <p>If the invocation of this method returns before the entire {@code inputReader} {@link
          * InputReader#getLength() length} is consumed, the method will be called again for the
@@ -374,15 +374,15 @@
          * @param inputReader The {@link InputReader} from which to read the data.
          * @throws IOException If an exception occurs while reading from {@code inputReader}.
          */
-        void onSampleData(int trackIndex, @NonNull InputReader inputReader) throws IOException;
+        void onSampleDataFound(int trackIndex, @NonNull InputReader inputReader) throws IOException;
 
         /**
-         * Called once all the data of a sample has been passed to {@link #onSampleData}.
+         * Called once all the data of a sample has been passed to {@link #onSampleDataFound}.
          *
          * <p>Also includes sample metadata, like presentation timestamp and flags.
          *
          * @param trackIndex The index of the track to which the sample corresponds.
-         * @param timeUs The media timestamp associated with the sample, in microseconds.
+         * @param timeMicros The media timestamp associated with the sample, in microseconds.
          * @param flags Flags associated with the sample. See {@link MediaCodec
          *     MediaCodec.BUFFER_FLAG_*}.
          * @param size The size of the sample data, in bytes.
@@ -394,7 +394,7 @@
          */
         void onSampleCompleted(
                 int trackIndex,
-                long timeUs,
+                long timeMicros,
                 int flags,
                 int size,
                 int offset,
@@ -632,7 +632,7 @@
     private Extractor mExtractor;
     private ExtractorInput mExtractorInput;
     private long mPendingSeekPosition;
-    private long mPendingSeekTimeUs;
+    private long mPendingSeekTimeMicros;
 
     // Public methods.
 
@@ -760,7 +760,7 @@
         }
 
         if (isPendingSeek()) {
-            mExtractor.seek(mPendingSeekPosition, mPendingSeekTimeUs);
+            mExtractor.seek(mPendingSeekPosition, mPendingSeekTimeMicros);
             removePendingSeek();
         }
 
@@ -786,7 +786,7 @@
      * Seeks within the media container being extracted.
      *
      * <p>{@link SeekPoint SeekPoints} can be obtained from the {@link SeekMap} passed to {@link
-     * OutputConsumer#onSeekMap(SeekMap)}.
+     * OutputConsumer#onSeekMapFound(SeekMap)}.
      *
      * <p>Following a call to this method, the {@link InputReader} passed to the next invocation of
      * {@link #advance} must provide data starting from {@link SeekPoint#position} in the stream.
@@ -796,9 +796,9 @@
     public void seek(@NonNull SeekPoint seekPoint) {
         if (mExtractor == null) {
             mPendingSeekPosition = seekPoint.position;
-            mPendingSeekTimeUs = seekPoint.timeUs;
+            mPendingSeekTimeMicros = seekPoint.timeMicros;
         } else {
-            mExtractor.seek(seekPoint.position, seekPoint.timeUs);
+            mExtractor.seek(seekPoint.position, seekPoint.timeMicros);
         }
     }
 
@@ -836,7 +836,7 @@
 
     private void removePendingSeek() {
         mPendingSeekPosition = -1;
-        mPendingSeekTimeUs = -1;
+        mPendingSeekTimeMicros = -1;
     }
 
     // Private classes.
@@ -897,12 +897,12 @@
 
         @Override
         public void endTracks() {
-            mOutputConsumer.onTracksFound(mTrackOutputAdapters.size());
+            mOutputConsumer.onTrackCountFound(mTrackOutputAdapters.size());
         }
 
         @Override
         public void seekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) {
-            mOutputConsumer.onSeekMap(new SeekMap(exoplayerSeekMap));
+            mOutputConsumer.onSeekMapFound(new SeekMap(exoplayerSeekMap));
         }
     }
 
@@ -916,7 +916,7 @@
 
         @Override
         public void format(Format format) {
-            mOutputConsumer.onTrackData(
+            mOutputConsumer.onTrackDataFound(
                     mTrackIndex,
                     new TrackData(
                             toMediaFormat(format), toFrameworkDrmInitData(format.drmInitData)));
@@ -927,7 +927,7 @@
                 throws IOException {
             mScratchExtractorInputAdapter.setExtractorInput(input, length);
             long positionBeforeReading = mScratchExtractorInputAdapter.getPosition();
-            mOutputConsumer.onSampleData(mTrackIndex, mScratchExtractorInputAdapter);
+            mOutputConsumer.onSampleDataFound(mTrackIndex, mScratchExtractorInputAdapter);
             return (int) (mScratchExtractorInputAdapter.getPosition() - positionBeforeReading);
         }
 
@@ -935,7 +935,7 @@
         public void sampleData(ParsableByteArray data, int length) {
             mScratchParsableByteArrayAdapter.resetWithByteArray(data, length);
             try {
-                mOutputConsumer.onSampleData(mTrackIndex, mScratchParsableByteArrayAdapter);
+                mOutputConsumer.onSampleDataFound(mTrackIndex, mScratchParsableByteArrayAdapter);
             } catch (IOException e) {
                 // Unexpected.
                 throw new RuntimeException(e);
diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java
index 0d163cf..aedba29 100644
--- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java
+++ b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java
@@ -40,7 +40,7 @@
      * @return the runtime permissions read
      */
     @Nullable
-    RuntimePermissionsState readAsUser(@NonNull UserHandle user);
+    RuntimePermissionsState readForUser(@NonNull UserHandle user);
 
     /**
      * Write the runtime permissions to persistence.
@@ -50,7 +50,8 @@
      * @param runtimePermissions the runtime permissions to write
      * @param user the user to write for
      */
-    void writeAsUser(@NonNull RuntimePermissionsState runtimePermissions, @NonNull UserHandle user);
+    void writeForUser(@NonNull RuntimePermissionsState runtimePermissions,
+            @NonNull UserHandle user);
 
     /**
      * Delete the runtime permissions from persistence.
@@ -59,7 +60,7 @@
      *
      * @param user the user to delete for
      */
-    void deleteAsUser(@NonNull UserHandle user);
+    void deleteForUser(@NonNull UserHandle user);
 
     /**
      * Create a new instance of {@link RuntimePermissionsPersistence} implementation.
diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
index 0ac0c73..e43f59a 100644
--- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
+++ b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
@@ -67,7 +67,7 @@
 
     @Nullable
     @Override
-    public RuntimePermissionsState readAsUser(@NonNull UserHandle user) {
+    public RuntimePermissionsState readForUser(@NonNull UserHandle user) {
         File file = getFile(user);
         try (FileInputStream inputStream = new AtomicFile(file).openRead()) {
             XmlPullParser parser = Xml.newPullParser();
@@ -172,7 +172,7 @@
     }
 
     @Override
-    public void writeAsUser(@NonNull RuntimePermissionsState runtimePermissions,
+    public void writeForUser(@NonNull RuntimePermissionsState runtimePermissions,
             @NonNull UserHandle user) {
         File file = getFile(user);
         AtomicFile atomicFile = new AtomicFile(file);
@@ -252,7 +252,7 @@
     }
 
     @Override
-    public void deleteAsUser(@NonNull UserHandle user) {
+    public void deleteForUser(@NonNull UserHandle user) {
         getFile(user).delete();
     }
 
diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java
index cd2750a..c6bfc6d 100644
--- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java
+++ b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java
@@ -23,6 +23,7 @@
 
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 /**
  * State of all runtime permissions.
@@ -61,6 +62,14 @@
     @NonNull
     private final Map<String, List<PermissionState>> mSharedUserPermissions;
 
+    /**
+     * Create a new instance of this class.
+     *
+     * @param version the version of the runtime permissions
+     * @param fingerprint the fingerprint of the runtime permissions
+     * @param packagePermissions the runtime permissions by packages
+     * @param sharedUserPermissions the runtime permissions by shared users
+     */
     public RuntimePermissionsState(int version, @Nullable String fingerprint,
             @NonNull Map<String, List<PermissionState>> packagePermissions,
             @NonNull Map<String, List<PermissionState>> sharedUserPermissions) {
@@ -70,32 +79,72 @@
         mSharedUserPermissions = sharedUserPermissions;
     }
 
+    /**
+     * Get the version of the runtime permissions.
+     *
+     * @return the version of the runtime permissions
+     */
     public int getVersion() {
         return mVersion;
     }
 
+    /**
+     * Get the fingerprint of the runtime permissions.
+     *
+     * @return the fingerprint of the runtime permissions
+     */
     @Nullable
     public String getFingerprint() {
         return mFingerprint;
     }
 
+    /**
+     * Get the runtime permissions by packages.
+     *
+     * @return the runtime permissions by packages
+     */
     @NonNull
     public Map<String, List<PermissionState>> getPackagePermissions() {
         return mPackagePermissions;
     }
 
+    /**
+     * Get the runtime permissions by shared users.
+     *
+     * @return the runtime permissions by shared users
+     */
     @NonNull
     public Map<String, List<PermissionState>> getSharedUserPermissions() {
         return mSharedUserPermissions;
     }
 
+    @Override
+    public boolean equals(Object object) {
+        if (this == object) {
+            return true;
+        }
+        if (object == null || getClass() != object.getClass()) {
+            return false;
+        }
+        RuntimePermissionsState that = (RuntimePermissionsState) object;
+        return mVersion == that.mVersion
+                && Objects.equals(mFingerprint, that.mFingerprint)
+                && Objects.equals(mPackagePermissions, that.mPackagePermissions)
+                && Objects.equals(mSharedUserPermissions, that.mSharedUserPermissions);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mVersion, mFingerprint, mPackagePermissions, mSharedUserPermissions);
+    }
+
     /**
      * State of a single permission.
      */
-    public static class PermissionState {
+    public static final class PermissionState {
 
         /**
-         * Name of the permission.
+         * The name of the permission.
          */
         @NonNull
         private final String mName;
@@ -106,27 +155,68 @@
         private final boolean mGranted;
 
         /**
-         * Flags of the permission.
+         * The flags of the permission.
          */
         private final int mFlags;
 
+        /**
+         * Create a new instance of this class.
+         *
+         * @param name the name of the permission
+         * @param granted whether the permission is granted
+         * @param flags the flags of the permission
+         */
         public PermissionState(@NonNull String name, boolean granted, int flags) {
             mName = name;
             mGranted = granted;
             mFlags = flags;
         }
 
+        /**
+         * Get the name of the permission.
+         *
+         * @return the name of the permission
+         */
         @NonNull
         public String getName() {
             return mName;
         }
 
+        /**
+         * Get whether the permission is granted.
+         *
+         * @return whether the permission is granted
+         */
         public boolean isGranted() {
             return mGranted;
         }
 
+        /**
+         * Get the flags of the permission.
+         *
+         * @return the flags of the permission
+         */
         public int getFlags() {
             return mFlags;
         }
+
+        @Override
+        public boolean equals(Object object) {
+            if (this == object) {
+                return true;
+            }
+            if (object == null || getClass() != object.getClass()) {
+                return false;
+            }
+            PermissionState that = (PermissionState) object;
+            return mGranted == that.mGranted
+                    && mFlags == that.mFlags
+                    && Objects.equals(mName, that.mName);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mName, mGranted, mFlags);
+        }
     }
 }
diff --git a/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java b/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java
index 64d6545..2e5a28a 100644
--- a/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java
+++ b/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java
@@ -40,7 +40,7 @@
      * @return the roles read
      */
     @Nullable
-    RolesState readAsUser(@NonNull UserHandle user);
+    RolesState readForUser(@NonNull UserHandle user);
 
     /**
      * Write the roles to persistence.
@@ -50,7 +50,7 @@
      * @param roles the roles to write
      * @param user the user to write for
      */
-    void writeAsUser(@NonNull RolesState roles, @NonNull UserHandle user);
+    void writeForUser(@NonNull RolesState roles, @NonNull UserHandle user);
 
     /**
      * Delete the roles from persistence.
@@ -59,7 +59,7 @@
      *
      * @param user the user to delete for
      */
-    void deleteAsUser(@NonNull UserHandle user);
+    void deleteForUser(@NonNull UserHandle user);
 
     /**
      * Create a new instance of {@link RolesPersistence} implementation.
diff --git a/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java b/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java
index 2346c11..f66257f 100644
--- a/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java
+++ b/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java
@@ -65,7 +65,7 @@
 
     @Nullable
     @Override
-    public RolesState readAsUser(@NonNull UserHandle user) {
+    public RolesState readForUser(@NonNull UserHandle user) {
         File file = getFile(user);
         try (FileInputStream inputStream = new AtomicFile(file).openRead()) {
             XmlPullParser parser = Xml.newPullParser();
@@ -146,7 +146,7 @@
     }
 
     @Override
-    public void writeAsUser(@NonNull RolesState roles, @NonNull UserHandle user) {
+    public void writeForUser(@NonNull RolesState roles, @NonNull UserHandle user) {
         File file = getFile(user);
         AtomicFile atomicFile = new AtomicFile(file);
         FileOutputStream outputStream = null;
@@ -205,7 +205,7 @@
     }
 
     @Override
-    public void deleteAsUser(@NonNull UserHandle user) {
+    public void deleteForUser(@NonNull UserHandle user) {
         getFile(user).delete();
     }
 
diff --git a/apex/permission/service/java/com/android/role/persistence/RolesState.java b/apex/permission/service/java/com/android/role/persistence/RolesState.java
index 7da9d11..f61efa0 100644
--- a/apex/permission/service/java/com/android/role/persistence/RolesState.java
+++ b/apex/permission/service/java/com/android/role/persistence/RolesState.java
@@ -22,6 +22,7 @@
 import android.annotation.SystemApi.Client;
 
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -50,6 +51,13 @@
     @NonNull
     private final Map<String, Set<String>> mRoles;
 
+    /**
+     * Create a new instance of this class.
+     *
+     * @param version the version of the roles
+     * @param packagesHash the hash of all packages in the system
+     * @param roles the roles
+     */
     public RolesState(int version, @Nullable String packagesHash,
             @NonNull Map<String, Set<String>> roles) {
         mVersion = version;
@@ -57,17 +65,51 @@
         mRoles = roles;
     }
 
+    /**
+     * Get the version of the roles.
+     *
+     * @return the version of the roles
+     */
     public int getVersion() {
         return mVersion;
     }
 
+    /**
+     * Get the hash of all packages in the system.
+     *
+     * @return the hash of all packages in the system
+     */
     @Nullable
     public String getPackagesHash() {
         return mPackagesHash;
     }
 
+    /**
+     * Get the roles.
+     *
+     * @return the roles
+     */
     @NonNull
     public Map<String, Set<String>> getRoles() {
         return mRoles;
     }
+
+    @Override
+    public boolean equals(Object object) {
+        if (this == object) {
+            return true;
+        }
+        if (object == null || getClass() != object.getClass()) {
+            return false;
+        }
+        RolesState that = (RolesState) object;
+        return mVersion == that.mVersion
+                && Objects.equals(mPackagesHash, that.mPackagesHash)
+                && Objects.equals(mRoles, that.mRoles);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mVersion, mPackagesHash, mRoles);
+    }
 }
diff --git a/api/current.txt b/api/current.txt
index 2efb305..1e61eef 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -572,6 +572,7 @@
     field public static final int elevation = 16843840; // 0x1010440
     field public static final int ellipsize = 16842923; // 0x10100ab
     field public static final int ems = 16843096; // 0x1010158
+    field public static final int enableGwpAsan = 16844310; // 0x1010616
     field public static final int enableVrMode = 16844069; // 0x1010525
     field public static final int enabled = 16842766; // 0x101000e
     field public static final int end = 16843996; // 0x10104dc
@@ -620,7 +621,6 @@
     field public static final int fastScrollTextColor = 16843609; // 0x1010359
     field public static final int fastScrollThumbDrawable = 16843574; // 0x1010336
     field public static final int fastScrollTrackDrawable = 16843577; // 0x1010339
-    field public static final int featureId = 16844301; // 0x101060d
     field public static final int fillAfter = 16843197; // 0x10101bd
     field public static final int fillAlpha = 16843980; // 0x10104cc
     field public static final int fillBefore = 16843196; // 0x10101bc
@@ -4586,7 +4586,7 @@
 
   public final class AsyncNotedAppOp implements android.os.Parcelable {
     method public int describeContents();
-    method @Nullable public String getFeatureId();
+    method @Nullable public String getAttributionTag();
     method @NonNull public String getMessage();
     method @IntRange(from=0) public int getNotingUid();
     method @NonNull public String getOp();
@@ -6430,7 +6430,7 @@
   public final class SyncNotedAppOp implements android.os.Parcelable {
     ctor public SyncNotedAppOp(@IntRange(from=0L) int, @Nullable String);
     method public int describeContents();
-    method @Nullable public String getFeatureId();
+    method @Nullable public String getAttributionTag();
     method @NonNull public String getOp();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.SyncNotedAppOp> CREATOR;
@@ -6856,9 +6856,9 @@
     method @Nullable public String getAlwaysOnVpnPackage(@NonNull android.content.ComponentName);
     method @NonNull @WorkerThread public android.os.Bundle getApplicationRestrictions(@Nullable android.content.ComponentName, String);
     method @Deprecated @Nullable public String getApplicationRestrictionsManagingPackage(@NonNull android.content.ComponentName);
-    method public boolean getAutoTime(@NonNull android.content.ComponentName);
+    method public boolean getAutoTimeEnabled(@NonNull android.content.ComponentName);
     method @Deprecated public boolean getAutoTimeRequired();
-    method public boolean getAutoTimeZone(@NonNull android.content.ComponentName);
+    method public boolean getAutoTimeZoneEnabled(@NonNull android.content.ComponentName);
     method @NonNull public java.util.List<android.os.UserHandle> getBindDeviceAdminTargetUsers(@NonNull android.content.ComponentName);
     method public boolean getBluetoothContactSharingDisabled(@NonNull android.content.ComponentName);
     method public boolean getCameraDisabled(@Nullable android.content.ComponentName);
@@ -6982,9 +6982,9 @@
     method public boolean setApplicationHidden(@NonNull android.content.ComponentName, String, boolean);
     method @WorkerThread public void setApplicationRestrictions(@Nullable android.content.ComponentName, String, android.os.Bundle);
     method @Deprecated public void setApplicationRestrictionsManagingPackage(@NonNull android.content.ComponentName, @Nullable String) throws android.content.pm.PackageManager.NameNotFoundException;
-    method public void setAutoTime(@NonNull android.content.ComponentName, boolean);
+    method public void setAutoTimeEnabled(@NonNull android.content.ComponentName, boolean);
     method @Deprecated public void setAutoTimeRequired(@NonNull android.content.ComponentName, boolean);
-    method public void setAutoTimeZone(@NonNull android.content.ComponentName, boolean);
+    method public void setAutoTimeZoneEnabled(@NonNull android.content.ComponentName, boolean);
     method public void setBackupServiceEnabled(@NonNull android.content.ComponentName, boolean);
     method public void setBluetoothContactSharingDisabled(@NonNull android.content.ComponentName, boolean);
     method public void setCameraDisabled(@NonNull android.content.ComponentName, boolean);
@@ -9696,7 +9696,7 @@
     method public abstract int delete(@NonNull android.net.Uri, @Nullable String, @Nullable String[]);
     method public int delete(@NonNull android.net.Uri, @Nullable android.os.Bundle);
     method public void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]);
-    method @Nullable public final String getCallingFeatureId();
+    method @Nullable public final String getCallingAttributionTag();
     method @Nullable public final String getCallingPackage();
     method @Nullable public final String getCallingPackageUnchecked();
     method @Nullable public final android.content.Context getContext();
@@ -10030,11 +10030,11 @@
     method @CheckResult(suggest="#enforceUriPermission(Uri,int,int,String)") public abstract int checkUriPermission(android.net.Uri, int, int, int);
     method @CheckResult(suggest="#enforceUriPermission(Uri,String,String,int,int,int,String)") public abstract int checkUriPermission(@Nullable android.net.Uri, @Nullable String, @Nullable String, int, int, int);
     method @Deprecated public abstract void clearWallpaper() throws java.io.IOException;
+    method @NonNull public android.content.Context createAttributionContext(@Nullable String);
     method public abstract android.content.Context createConfigurationContext(@NonNull android.content.res.Configuration);
     method public abstract android.content.Context createContextForSplit(String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.Context createDeviceProtectedStorageContext();
     method public abstract android.content.Context createDisplayContext(@NonNull android.view.Display);
-    method @NonNull public android.content.Context createFeatureContext(@Nullable String);
     method public abstract android.content.Context createPackageContext(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public android.content.Context createWindowContext(int, @Nullable android.os.Bundle);
     method public abstract String[] databaseList();
@@ -10052,6 +10052,7 @@
     method public abstract android.content.Context getApplicationContext();
     method public abstract android.content.pm.ApplicationInfo getApplicationInfo();
     method public abstract android.content.res.AssetManager getAssets();
+    method @Nullable public String getAttributionTag();
     method public abstract java.io.File getCacheDir();
     method public abstract ClassLoader getClassLoader();
     method public abstract java.io.File getCodeCacheDir();
@@ -10068,7 +10069,6 @@
     method @Nullable public abstract java.io.File getExternalFilesDir(@Nullable String);
     method public abstract java.io.File[] getExternalFilesDirs(String);
     method @Deprecated public abstract java.io.File[] getExternalMediaDirs();
-    method @Nullable public String getFeatureId();
     method public abstract java.io.File getFileStreamPath(String);
     method public abstract java.io.File getFilesDir();
     method public java.util.concurrent.Executor getMainExecutor();
@@ -10806,6 +10806,7 @@
     field public static final String EXTRA_TEMPLATE = "android.intent.extra.TEMPLATE";
     field public static final String EXTRA_TEXT = "android.intent.extra.TEXT";
     field public static final String EXTRA_TIME = "android.intent.extra.TIME";
+    field public static final String EXTRA_TIMEZONE = "time-zone";
     field public static final String EXTRA_TITLE = "android.intent.extra.TITLE";
     field public static final String EXTRA_UID = "android.intent.extra.UID";
     field public static final String EXTRA_USER = "android.intent.extra.USER";
@@ -11438,6 +11439,7 @@
     method public int describeContents();
     method public void dump(android.util.Printer, String);
     method public static CharSequence getCategoryTitle(android.content.Context, int);
+    method @Nullable public Boolean isGwpAsanEnabled();
     method public boolean isProfileableByShell();
     method public boolean isResourceOverlay();
     method public boolean isVirtualPreload();
@@ -11571,6 +11573,7 @@
     method @NonNull public CharSequence getProfileSwitchingLabel(@NonNull android.os.UserHandle);
     method @NonNull public java.util.List<android.os.UserHandle> getTargetUserProfiles();
     method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_PROFILES, "android.permission.INTERACT_ACROSS_USERS"}) public void startActivity(@NonNull android.content.Intent, @NonNull android.os.UserHandle, @Nullable android.app.Activity);
+    method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_PROFILES, "android.permission.INTERACT_ACROSS_USERS"}) public void startActivity(@NonNull android.content.Intent, @NonNull android.os.UserHandle, @Nullable android.app.Activity, @Nullable android.os.Bundle);
     method public void startMainActivity(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
     field public static final String ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED = "android.content.pm.action.CAN_INTERACT_ACROSS_PROFILES_CHANGED";
   }
@@ -26448,14 +26451,14 @@
 
   public static interface MediaParser.OutputConsumer {
     method public void onSampleCompleted(int, long, int, int, int, @Nullable android.media.MediaCodec.CryptoInfo);
-    method public void onSampleData(int, @NonNull android.media.MediaParser.InputReader) throws java.io.IOException;
-    method public void onSeekMap(@NonNull android.media.MediaParser.SeekMap);
-    method public void onTrackData(int, @NonNull android.media.MediaParser.TrackData);
-    method public void onTracksFound(int);
+    method public void onSampleDataFound(int, @NonNull android.media.MediaParser.InputReader) throws java.io.IOException;
+    method public void onSeekMapFound(@NonNull android.media.MediaParser.SeekMap);
+    method public void onTrackCountFound(int);
+    method public void onTrackDataFound(int, @NonNull android.media.MediaParser.TrackData);
   }
 
   public static final class MediaParser.SeekMap {
-    method public long getDurationUs();
+    method public long getDurationMicros();
     method @NonNull public android.util.Pair<android.media.MediaParser.SeekPoint,android.media.MediaParser.SeekPoint> getSeekPoints(long);
     method public boolean isSeekable();
     field public static final int UNKNOWN_DURATION = -2147483648; // 0x80000000
@@ -26464,7 +26467,7 @@
   public static final class MediaParser.SeekPoint {
     field @NonNull public static final android.media.MediaParser.SeekPoint START;
     field public final long position;
-    field public final long timeUs;
+    field public final long timeMicros;
   }
 
   public static interface MediaParser.SeekableInputReader extends android.media.MediaParser.InputReader {
@@ -40714,6 +40717,7 @@
     field public static final String PARENTAL_CONTROL_LAST_UPDATE = "parental_control_last_update";
     field public static final String PARENTAL_CONTROL_REDIRECT_URL = "parental_control_redirect_url";
     field public static final String RTT_CALLING_MODE = "rtt_calling_mode";
+    field public static final String SECURE_FRP_MODE = "secure_frp_mode";
     field public static final String SELECTED_INPUT_METHOD_SUBTYPE = "selected_input_method_subtype";
     field public static final String SETTINGS_CLASSNAME = "settings_classname";
     field public static final String SKIP_FIRST_USE_HINTS = "skip_first_use_hints";
@@ -43012,12 +43016,12 @@
   }
 
   public static final class Dataset.Builder {
-    ctor public Dataset.Builder(@NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation);
     ctor public Dataset.Builder(@NonNull android.widget.RemoteViews);
     ctor public Dataset.Builder();
     method @NonNull public android.service.autofill.Dataset build();
     method @NonNull public android.service.autofill.Dataset.Builder setAuthentication(@Nullable android.content.IntentSender);
     method @NonNull public android.service.autofill.Dataset.Builder setId(@Nullable String);
+    method @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation);
     method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue);
     method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @NonNull android.widget.RemoteViews);
     method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern);
@@ -45973,9 +45977,6 @@
     field public static final int MISSED = 5; // 0x5
     field public static final int OTHER = 9; // 0x9
     field public static final String REASON_EMERGENCY_CALL_PLACED = "REASON_EMERGENCY_CALL_PLACED";
-    field public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
-    field public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED";
-    field public static final String REASON_WIFI_ON_BUT_WFC_OFF = "REASON_WIFI_ON_BUT_WFC_OFF";
     field public static final int REJECTED = 6; // 0x6
     field public static final int REMOTE = 3; // 0x3
     field public static final int RESTRICTED = 8; // 0x8
@@ -46302,7 +46303,6 @@
     field public static final int DURATION_SHORT = 1; // 0x1
     field public static final int DURATION_VERY_SHORT = 0; // 0x0
     field public static final String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
-    field public static final String EXTRA_CALL_CREATED_TIME_MILLIS = "android.telecom.extra.CALL_CREATED_TIME_MILLIS";
     field public static final String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE";
     field public static final String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
     field public static final String EXTRA_CALL_DURATION = "android.telecom.extra.CALL_DURATION";
@@ -48044,13 +48044,13 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.os.ParcelUuid createSubscriptionGroup(@NonNull java.util.List<java.lang.Integer>);
     method @Deprecated public static android.telephony.SubscriptionManager from(android.content.Context);
     method public java.util.List<android.telephony.SubscriptionInfo> getAccessibleSubscriptionInfoList();
-    method @Nullable public java.util.List<android.telephony.SubscriptionInfo> getActiveAndHiddenSubscriptionInfoList();
     method public static int getActiveDataSubscriptionId();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfo(int);
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getActiveSubscriptionInfoCount();
     method public int getActiveSubscriptionInfoCountMax();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int);
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telephony.SubscriptionInfo> getActiveSubscriptionInfoList();
+    method @NonNull public java.util.List<android.telephony.SubscriptionInfo> getCompleteActiveSubscriptionInfoList();
     method public static int getDefaultDataSubscriptionId();
     method public static int getDefaultSmsSubscriptionId();
     method public static int getDefaultSubscriptionId();
@@ -48213,6 +48213,7 @@
     method public boolean isEmergencyNumber(@NonNull String);
     method public boolean isHearingAidCompatibilitySupported();
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isManualNetworkSelectionAllowed();
+    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isModemEnabledForSlot(int);
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int isMultiSimSupported();
     method public boolean isNetworkRoaming();
     method public boolean isRttSupported();
@@ -48326,7 +48327,6 @@
     field public static final int NETWORK_TYPE_UNKNOWN = 0; // 0x0
     field public static final int PHONE_TYPE_CDMA = 2; // 0x2
     field public static final int PHONE_TYPE_GSM = 1; // 0x1
-    field public static final int PHONE_TYPE_IMS = 5; // 0x5
     field public static final int PHONE_TYPE_NONE = 0; // 0x0
     field public static final int PHONE_TYPE_SIP = 3; // 0x3
     field public static final int SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION = 2; // 0x2
@@ -55582,10 +55582,12 @@
   }
 
   public interface WindowInsetsController {
+    method public void addOnControllableInsetsChangedListener(@NonNull android.view.WindowInsetsController.OnControllableInsetsChangedListener);
     method @NonNull public android.os.CancellationSignal controlWindowInsetsAnimation(int, long, @Nullable android.view.animation.Interpolator, @NonNull android.view.WindowInsetsAnimationControlListener);
     method public int getSystemBarsAppearance();
     method public int getSystemBarsBehavior();
     method public void hide(int);
+    method public void removeOnControllableInsetsChangedListener(@NonNull android.view.WindowInsetsController.OnControllableInsetsChangedListener);
     method public void setSystemBarsAppearance(int, int);
     method public void setSystemBarsBehavior(int);
     method public void show(int);
@@ -55596,6 +55598,10 @@
     field public static final int BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2; // 0x2
   }
 
+  public static interface WindowInsetsController.OnControllableInsetsChangedListener {
+    method public void onControllableInsetsChanged(@NonNull android.view.WindowInsetsController, int);
+  }
+
   public interface WindowManager extends android.view.ViewManager {
     method @NonNull public default android.view.WindowMetrics getCurrentWindowMetrics();
     method @Deprecated public android.view.Display getDefaultDisplay();
@@ -56735,6 +56741,7 @@
     method public final void notifySessionResumed();
     method public final void notifyViewAppeared(@NonNull android.view.ViewStructure);
     method public final void notifyViewDisappeared(@NonNull android.view.autofill.AutofillId);
+    method public final void notifyViewInsetsChanged(@NonNull android.graphics.Insets);
     method public final void notifyViewTextChanged(@NonNull android.view.autofill.AutofillId, @Nullable CharSequence);
     method public final void notifyViewsDisappeared(@NonNull android.view.autofill.AutofillId, @NonNull long[]);
     method public final void setContentCaptureContext(@Nullable android.view.contentcapture.ContentCaptureContext);
@@ -82227,3 +82234,4 @@
   }
 
 }
+
diff --git a/api/lint-baseline.txt b/api/lint-baseline.txt
index 569e838..83c78fe 100644
--- a/api/lint-baseline.txt
+++ b/api/lint-baseline.txt
@@ -15,6 +15,16 @@
     
 ArrayReturn: android.content.ContentProviderOperation#resolveExtrasBackReferences(android.content.ContentProviderResult[], int) parameter #0:
     
+ArrayReturn: android.location.GnssAntennaInfo.SphericalCorrections#SphericalCorrections(double[][], double[][]) parameter #0:
+    Method parameter should be Collection<> (or subclass) instead of raw array; was `double[][]`
+ArrayReturn: android.location.GnssAntennaInfo.SphericalCorrections#SphericalCorrections(double[][], double[][]) parameter #1:
+    Method parameter should be Collection<> (or subclass) instead of raw array; was `double[][]`
+ArrayReturn: android.location.GnssAntennaInfo.SphericalCorrections#getCorrectionUncertaintiesArray():
+    Method should return Collection<> (or subclass) instead of raw array; was `double[][]`
+ArrayReturn: android.location.GnssAntennaInfo.SphericalCorrections#getCorrectionsArray():
+    Method should return Collection<> (or subclass) instead of raw array; was `double[][]`
+ArrayReturn: android.service.autofill.FillResponse.Builder#setAuthentication(android.view.autofill.AutofillId[], android.content.IntentSender, android.widget.RemoteViews, android.service.autofill.InlinePresentation) parameter #0:
+    Method parameter should be Collection<AutofillId> (or subclass) instead of raw array; was `android.view.autofill.AutofillId[]`
 
 
 BroadcastBehavior: android.app.AlarmManager#ACTION_NEXT_ALARM_CLOCK_CHANGED:
@@ -453,8 +463,12 @@
     
 
 
+ExecutorRegistration: android.media.MediaRouter2#setOnGetControllerHintsListener(android.media.MediaRouter2.OnGetControllerHintsListener):
+    Registration methods should have overload that accepts delivery Executor: `setOnGetControllerHintsListener`
+
+
 GenericException: android.content.res.loader.ResourcesProvider#finalize():
-    Methods must not throw generic exceptions (`java.lang.Throwable`)
+    
 
 
 HiddenSuperclass: android.content.res.ColorStateList:
@@ -499,6 +513,30 @@
     
 
 
+IntentBuilderName: android.net.VpnManager#provisionVpnProfile(android.net.PlatformVpnProfile):
+    Methods creating an Intent should be named `create<Foo>Intent()`, was `provisionVpnProfile`
+
+
+KotlinOperator: android.media.AudioMetadata.Map#set(android.media.AudioMetadata.Key<T>, T):
+    Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: android.media.AudioMetadata.ReadMap#get(android.media.AudioMetadata.Key<T>):
+    Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+
+
+MethodNameUnits: android.media.MediaParser.SeekMap#getDurationMicros():
+    Returned time values are strongly encouraged to be in milliseconds unless you need the extra precision, was `getDurationMicros`
+
+
+MinMaxConstant: android.telephony.DataFailCause#MAX_ACCESS_PROBE:
+    If min/max could change in future, make them dynamic methods: android.telephony.DataFailCause#MAX_ACCESS_PROBE
+MinMaxConstant: android.telephony.DataFailCause#MAX_IPV4_CONNECTIONS:
+    If min/max could change in future, make them dynamic methods: android.telephony.DataFailCause#MAX_IPV4_CONNECTIONS
+MinMaxConstant: android.telephony.DataFailCause#MAX_IPV6_CONNECTIONS:
+    If min/max could change in future, make them dynamic methods: android.telephony.DataFailCause#MAX_IPV6_CONNECTIONS
+MinMaxConstant: android.telephony.DataFailCause#MAX_PPP_INACTIVITY_TIMER_EXPIRED:
+    If min/max could change in future, make them dynamic methods: android.telephony.DataFailCause#MAX_PPP_INACTIVITY_TIMER_EXPIRED
+
+
 MissingNullability: android.app.AsyncNotedAppOp#equals(Object) parameter #0:
     
 MissingNullability: android.app.AsyncNotedAppOp#writeToParcel(android.os.Parcel, int) parameter #0:
@@ -506,11 +544,11 @@
 MissingNullability: android.app.SyncNotedAppOp#equals(Object) parameter #0:
     
 MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#CHORASMIAN:
-    Missing nullability on field `CHORASMIAN` in class `class android.icu.lang.UCharacter.UnicodeBlock`
+    
 MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#CJK_UNIFIED_IDEOGRAPHS_EXTENSION_G:
-    Missing nullability on field `CJK_UNIFIED_IDEOGRAPHS_EXTENSION_G` in class `class android.icu.lang.UCharacter.UnicodeBlock`
+    
 MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#DIVES_AKURU:
-    Missing nullability on field `DIVES_AKURU` in class `class android.icu.lang.UCharacter.UnicodeBlock`
+    
 MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#EGYPTIAN_HIEROGLYPH_FORMAT_CONTROLS:
     
 MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#ELYMAIC:
@@ -556,14 +594,39 @@
 MissingNullability: android.icu.util.VersionInfo#UNICODE_12_1:
     
 MissingNullability: android.icu.util.VersionInfo#UNICODE_13_0:
-    Missing nullability on field `UNICODE_13_0` in class `class android.icu.util.VersionInfo`
+    
 MissingNullability: android.media.MediaMetadataRetriever#getFrameAtTime(long, int, android.media.MediaMetadataRetriever.BitmapParams):
     
 MissingNullability: android.media.MediaMetadataRetriever#getScaledFrameAtTime(long, int, int, int, android.media.MediaMetadataRetriever.BitmapParams):
-
     
 MissingNullability: java.time.chrono.JapaneseEra#REIWA:
-    Missing nullability on field `REIWA` in class `class java.time.chrono.JapaneseEra`
+    
+
+
+NotCloseable: android.media.MediaCodec.GraphicBlock:
+    Classes that release resources (finalize()) should implement AutoClosable and CloseGuard: class android.media.MediaCodec.GraphicBlock
+NotCloseable: android.media.MediaCodec.LinearBlock:
+    Classes that release resources (finalize()) should implement AutoClosable and CloseGuard: class android.media.MediaCodec.LinearBlock
+NotCloseable: android.media.MediaParser:
+    Classes that release resources (release()) should implement AutoClosable and CloseGuard: class android.media.MediaParser
+NotCloseable: android.media.MediaRouter2.RoutingController:
+    Classes that release resources (release()) should implement AutoClosable and CloseGuard: class android.media.MediaRouter2.RoutingController
+NotCloseable: android.util.CloseGuard:
+    Classes that release resources (close()) should implement AutoClosable and CloseGuard: class android.util.CloseGuard
+NotCloseable: android.view.SurfaceControlViewHost:
+    Classes that release resources (release()) should implement AutoClosable and CloseGuard: class android.view.SurfaceControlViewHost
+
+
+OnNameExpected: android.app.admin.DevicePolicyKeyguardService#dismiss():
+    If implemented by developer, should follow the on<Something> style; otherwise consider marking final
+OnNameExpected: android.service.controls.ControlsProviderService#createPublisherFor(java.util.List<java.lang.String>):
+    Methods implemented by developers should follow the on<Something> style, was `createPublisherFor`
+OnNameExpected: android.service.controls.ControlsProviderService#createPublisherForAllAvailable():
+    Methods implemented by developers should follow the on<Something> style, was `createPublisherForAllAvailable`
+OnNameExpected: android.service.controls.ControlsProviderService#createPublisherForSuggested():
+    If implemented by developer, should follow the on<Something> style; otherwise consider marking final
+OnNameExpected: android.service.controls.ControlsProviderService#performControlAction(String, android.service.controls.actions.ControlAction, java.util.function.Consumer<java.lang.Integer>):
+    Methods implemented by developers should follow the on<Something> style, was `performControlAction`
 
 
 RequiresPermission: android.accounts.AccountManager#getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler):
@@ -1189,11 +1252,13 @@
 SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(String, long, float, java.util.concurrent.Executor, android.location.LocationListener):
     
 SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(long, float, android.location.Criteria, java.util.concurrent.Executor, android.location.LocationListener):
+    
 
 
-
+StreamFiles: android.content.res.loader.DirectoryAssetsProvider#DirectoryAssetsProvider(java.io.File):
+    Methods accepting `File` should also accept `FileDescriptor` or streams: constructor android.content.res.loader.DirectoryAssetsProvider(java.io.File)
 StreamFiles: android.content.res.loader.DirectoryResourceLoader#DirectoryResourceLoader(java.io.File):
-    Methods accepting `File` should also accept `FileDescriptor` or streams: constructor android.content.res.loader.DirectoryResourceLoader(java.io.File)
+    
 
 
 Todo: android.hardware.camera2.params.StreamConfigurationMap:
diff --git a/api/removed.txt b/api/removed.txt
index 8537b21..077c915 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -1,4 +1,12 @@
 // Signature format: 2.0
+package android {
+
+  public static final class R.attr {
+    field public static final int featureId = 16844301; // 0x101060d
+  }
+
+}
+
 package android.app {
 
   public class ActivityManager {
@@ -69,11 +77,17 @@
 
 package android.content {
 
+  public abstract class ContentProvider implements android.content.ComponentCallbacks2 {
+    method @Deprecated @Nullable public final String getCallingFeatureId();
+  }
+
   public abstract class ContentResolver {
     method @Deprecated public void notifyChange(@NonNull Iterable<android.net.Uri>, @Nullable android.database.ContentObserver, int);
   }
 
   public abstract class Context {
+    method @Deprecated @NonNull public android.content.Context createFeatureContext(@Nullable String);
+    method @Deprecated @Nullable public String getFeatureId();
     method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int);
     method public abstract java.io.File getSharedPreferencesPath(String);
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index 979d6e6..a044888 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -79,6 +79,7 @@
     field public static final String DEVICE_POWER = "android.permission.DEVICE_POWER";
     field public static final String DISPATCH_PROVISIONING_MESSAGE = "android.permission.DISPATCH_PROVISIONING_MESSAGE";
     field public static final String ENTER_CAR_MODE_PRIORITIZED = "android.permission.ENTER_CAR_MODE_PRIORITIZED";
+    field public static final String EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS = "android.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS";
     field public static final String FORCE_BACK = "android.permission.FORCE_BACK";
     field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
     field public static final String GET_APP_OPS_STATS = "android.permission.GET_APP_OPS_STATS";
@@ -231,6 +232,7 @@
     field public static final String UPDATE_APP_OPS_STATS = "android.permission.UPDATE_APP_OPS_STATS";
     field public static final String UPDATE_LOCK = "android.permission.UPDATE_LOCK";
     field public static final String UPDATE_TIME_ZONE_RULES = "android.permission.UPDATE_TIME_ZONE_RULES";
+    field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS";
     field public static final String USER_ACTIVITY = "android.permission.USER_ACTIVITY";
     field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK";
     field public static final String WHITELIST_RESTRICTED_PERMISSIONS = "android.permission.WHITELIST_RESTRICTED_PERMISSIONS";
@@ -446,14 +448,37 @@
     field public static final int UID_STATE_TOP = 200; // 0xc8
   }
 
-  public static final class AppOpsManager.HistoricalFeatureOps implements android.os.Parcelable {
+  public static final class AppOpsManager.AttributedHistoricalOps implements android.os.Parcelable {
     method public int describeContents();
-    method @Nullable public String getFeatureId();
     method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String);
     method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int);
     method @IntRange(from=0) public int getOpCount();
+    method @Nullable public String getTag();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalFeatureOps> CREATOR;
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.AttributedHistoricalOps> CREATOR;
+  }
+
+  public static final class AppOpsManager.AttributedOpEntry implements android.os.Parcelable {
+    method public int describeContents();
+    method public long getLastAccessBackgroundTime(int);
+    method public long getLastAccessForegroundTime(int);
+    method public long getLastAccessTime(int);
+    method public long getLastAccessTime(int, int, int);
+    method public long getLastBackgroundDuration(int);
+    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastBackgroundProxyInfo(int);
+    method public long getLastDuration(int);
+    method public long getLastDuration(int, int, int);
+    method public long getLastForegroundDuration(int);
+    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastForegroundProxyInfo(int);
+    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int);
+    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int, int, int);
+    method public long getLastRejectBackgroundTime(int);
+    method public long getLastRejectForegroundTime(int);
+    method public long getLastRejectTime(int);
+    method public long getLastRejectTime(int, int, int);
+    method public boolean isRunning();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.AttributedOpEntry> CREATOR;
   }
 
   public static final class AppOpsManager.HistoricalOp implements android.os.Parcelable {
@@ -489,7 +514,7 @@
   public static final class AppOpsManager.HistoricalOpsRequest.Builder {
     ctor public AppOpsManager.HistoricalOpsRequest.Builder(long, long);
     method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest build();
-    method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFeatureId(@Nullable String);
+    method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setAttributionTag(@Nullable String);
     method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFlags(int);
     method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setOpNames(@Nullable java.util.List<java.lang.String>);
     method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setPackageName(@Nullable String);
@@ -498,9 +523,9 @@
 
   public static final class AppOpsManager.HistoricalPackageOps implements android.os.Parcelable {
     method public int describeContents();
-    method @IntRange(from=0) public int getFeatureCount();
-    method @Nullable public android.app.AppOpsManager.HistoricalFeatureOps getFeatureOps(@NonNull String);
-    method @NonNull public android.app.AppOpsManager.HistoricalFeatureOps getFeatureOpsAt(@IntRange(from=0) int);
+    method @Nullable public android.app.AppOpsManager.AttributedHistoricalOps getAttributedOps(@NonNull String);
+    method @NonNull public android.app.AppOpsManager.AttributedHistoricalOps getAttributedOpsAt(@IntRange(from=0) int);
+    method @IntRange(from=0) public int getAttributedOpsCount();
     method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String);
     method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int);
     method @IntRange(from=0) public int getOpCount();
@@ -521,8 +546,8 @@
 
   public static final class AppOpsManager.OpEntry implements android.os.Parcelable {
     method public int describeContents();
+    method @NonNull public java.util.Map<java.lang.String,android.app.AppOpsManager.AttributedOpEntry> getAttributedOpEntries();
     method @Deprecated public long getDuration();
-    method @NonNull public java.util.Map<java.lang.String,android.app.AppOpsManager.OpFeatureEntry> getFeatures();
     method public long getLastAccessBackgroundTime(int);
     method public long getLastAccessForegroundTime(int);
     method public long getLastAccessTime(int);
@@ -552,36 +577,13 @@
 
   public static final class AppOpsManager.OpEventProxyInfo implements android.os.Parcelable {
     method public int describeContents();
-    method @Nullable public String getFeatureId();
+    method @Nullable public String getAttributionTag();
     method @Nullable public String getPackageName();
     method @IntRange(from=0) public int getUid();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpEventProxyInfo> CREATOR;
   }
 
-  public static final class AppOpsManager.OpFeatureEntry implements android.os.Parcelable {
-    method public int describeContents();
-    method public long getLastAccessBackgroundTime(int);
-    method public long getLastAccessForegroundTime(int);
-    method public long getLastAccessTime(int);
-    method public long getLastAccessTime(int, int, int);
-    method public long getLastBackgroundDuration(int);
-    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastBackgroundProxyInfo(int);
-    method public long getLastDuration(int);
-    method public long getLastDuration(int, int, int);
-    method public long getLastForegroundDuration(int);
-    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastForegroundProxyInfo(int);
-    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int);
-    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int, int, int);
-    method public long getLastRejectBackgroundTime(int);
-    method public long getLastRejectForegroundTime(int);
-    method public long getLastRejectTime(int);
-    method public long getLastRejectTime(int, int, int);
-    method public boolean isRunning();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpFeatureEntry> CREATOR;
-  }
-
   public static final class AppOpsManager.PackageOps implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public java.util.List<android.app.AppOpsManager.OpEntry> getOps();
@@ -686,7 +688,7 @@
   public final class RuntimeAppOpAccessMessage implements android.os.Parcelable {
     ctor public RuntimeAppOpAccessMessage(@IntRange(from=0L) int, @IntRange(from=0L) int, @NonNull String, @Nullable String, @NonNull String, int);
     method public int describeContents();
-    method @Nullable public String getFeatureId();
+    method @Nullable public String getAttributionTag();
     method @NonNull public String getMessage();
     method @NonNull public String getOp();
     method @NonNull public String getPackageName();
@@ -1369,7 +1371,6 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void addRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean addRoleHolderFromController(@NonNull String, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void clearRoleHoldersAsUser(@NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
-    method @Nullable public String getDefaultSmsPackage(int);
     method @NonNull @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public java.util.List<java.lang.String> getHeldRolesFromController(@NonNull String);
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHolders(@NonNull String);
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle);
@@ -2214,7 +2215,7 @@
     field public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery";
     field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
     field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
-    field public static final int FLAGS_PERMISSION_RESERVED_PERMISSIONCONTROLLER = -268435456; // 0xf0000000
+    field public static final int FLAGS_PERMISSION_RESERVED_PERMISSION_CONTROLLER = -268435456; // 0xf0000000
     field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000
     field public static final int FLAG_PERMISSION_AUTO_REVOKED = 131072; // 0x20000
     field public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 32; // 0x20
@@ -2938,7 +2939,7 @@
 
   public final class LightsManager.LightsSession implements java.lang.AutoCloseable {
     method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void close();
-    method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void setLights(@NonNull android.hardware.lights.LightsRequest);
+    method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void requestLights(@NonNull android.hardware.lights.LightsRequest);
   }
 
   public final class LightsRequest {
@@ -5618,8 +5619,8 @@
     method public int getConstellation();
     method public int getGuardInterval();
     method public int getHierarchy();
-    method public int getHpCodeRate();
-    method public int getLpCodeRate();
+    method public int getHighPriorityCodeRate();
+    method public int getLowPriorityCodeRate();
     method public int getPlpGroupId();
     method public int getPlpId();
     method public int getPlpMode();
@@ -5647,20 +5648,20 @@
     field public static final int CODERATE_8_9 = 512; // 0x200
     field public static final int CODERATE_AUTO = 1; // 0x1
     field public static final int CODERATE_UNDEFINED = 0; // 0x0
+    field public static final int CONSTELLATION_16QAM = 4; // 0x4
+    field public static final int CONSTELLATION_256QAM = 16; // 0x10
+    field public static final int CONSTELLATION_64QAM = 8; // 0x8
     field public static final int CONSTELLATION_AUTO = 1; // 0x1
-    field public static final int CONSTELLATION_CONSTELLATION_16QAM = 4; // 0x4
-    field public static final int CONSTELLATION_CONSTELLATION_256QAM = 16; // 0x10
-    field public static final int CONSTELLATION_CONSTELLATION_64QAM = 8; // 0x8
-    field public static final int CONSTELLATION_CONSTELLATION_QPSK = 2; // 0x2
+    field public static final int CONSTELLATION_QPSK = 2; // 0x2
     field public static final int CONSTELLATION_UNDEFINED = 0; // 0x0
+    field public static final int GUARD_INTERVAL_19_128 = 64; // 0x40
+    field public static final int GUARD_INTERVAL_19_256 = 128; // 0x80
+    field public static final int GUARD_INTERVAL_1_128 = 32; // 0x20
+    field public static final int GUARD_INTERVAL_1_16 = 4; // 0x4
+    field public static final int GUARD_INTERVAL_1_32 = 2; // 0x2
+    field public static final int GUARD_INTERVAL_1_4 = 16; // 0x10
+    field public static final int GUARD_INTERVAL_1_8 = 8; // 0x8
     field public static final int GUARD_INTERVAL_AUTO = 1; // 0x1
-    field public static final int GUARD_INTERVAL_INTERVAL_19_128 = 64; // 0x40
-    field public static final int GUARD_INTERVAL_INTERVAL_19_256 = 128; // 0x80
-    field public static final int GUARD_INTERVAL_INTERVAL_1_128 = 32; // 0x20
-    field public static final int GUARD_INTERVAL_INTERVAL_1_16 = 4; // 0x4
-    field public static final int GUARD_INTERVAL_INTERVAL_1_32 = 2; // 0x2
-    field public static final int GUARD_INTERVAL_INTERVAL_1_4 = 16; // 0x10
-    field public static final int GUARD_INTERVAL_INTERVAL_1_8 = 8; // 0x8
     field public static final int GUARD_INTERVAL_UNDEFINED = 0; // 0x0
     field public static final int HIERARCHY_1_INDEPTH = 64; // 0x40
     field public static final int HIERARCHY_1_NATIVE = 4; // 0x4
@@ -5695,8 +5696,8 @@
     method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setGuardInterval(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setHierarchy(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setHighPriority(boolean);
-    method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setHpCodeRate(int);
-    method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setLpCodeRate(int);
+    method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setHighPriorityCodeRate(int);
+    method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setLowPriorityCodeRate(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setMiso(boolean);
     method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setPlpGroupId(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setPlpId(int);
@@ -7156,7 +7157,7 @@
     method @NonNull public java.util.List<android.net.MacAddress> getBlockedClientList();
     method public int getChannel();
     method public int getMaxNumberOfClients();
-    method public int getShutdownTimeoutMillis();
+    method public long getShutdownTimeoutMillis();
     method public boolean isAutoShutdownEnabled();
     method public boolean isClientControlByUserEnabled();
     method @Nullable public android.net.wifi.WifiConfiguration toWifiConfiguration();
@@ -7170,16 +7171,17 @@
     ctor public SoftApConfiguration.Builder();
     ctor public SoftApConfiguration.Builder(@NonNull android.net.wifi.SoftApConfiguration);
     method @NonNull public android.net.wifi.SoftApConfiguration build();
-    method @NonNull public android.net.wifi.SoftApConfiguration.Builder enableClientControlByUser(boolean);
+    method @NonNull public android.net.wifi.SoftApConfiguration.Builder setAllowedClientList(@NonNull java.util.List<android.net.MacAddress>);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setAutoShutdownEnabled(boolean);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBand(int);
+    method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBlockedClientList(@NonNull java.util.List<android.net.MacAddress>);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBssid(@Nullable android.net.MacAddress);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setChannel(int, int);
-    method @NonNull public android.net.wifi.SoftApConfiguration.Builder setClientList(@NonNull java.util.List<android.net.MacAddress>, @NonNull java.util.List<android.net.MacAddress>);
+    method @NonNull public android.net.wifi.SoftApConfiguration.Builder setClientControlByUserEnabled(boolean);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setHiddenSsid(boolean);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setMaxNumberOfClients(@IntRange(from=0) int);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setPassphrase(@Nullable String, int);
-    method @NonNull public android.net.wifi.SoftApConfiguration.Builder setShutdownTimeoutMillis(@IntRange(from=0) int);
+    method @NonNull public android.net.wifi.SoftApConfiguration.Builder setShutdownTimeoutMillis(@IntRange(from=0) long);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setSsid(@Nullable String);
   }
 
@@ -7240,7 +7242,6 @@
     field @Deprecated public int numScorerOverride;
     field @Deprecated public int numScorerOverrideAndSwitchedNetwork;
     field @Deprecated public boolean requirePmf;
-    field @Deprecated @Nullable public String saePasswordId;
     field @Deprecated public boolean shared;
     field @Deprecated public boolean useExternalScores;
   }
@@ -7338,7 +7339,7 @@
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,android.net.wifi.hotspot2.PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(@NonNull java.util.Set<android.net.wifi.hotspot2.OsuProvider>);
     method @NonNull @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE}) public java.util.Map<android.net.wifi.WifiNetworkSuggestion,java.util.List<android.net.wifi.ScanResult>> getMatchingScanResults(@NonNull java.util.List<android.net.wifi.WifiNetworkSuggestion>, @Nullable java.util.List<android.net.wifi.ScanResult>);
     method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
-    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.SoftApConfiguration getSoftApConfiguration();
+    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public android.net.wifi.SoftApConfiguration getSoftApConfiguration();
     method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void getWifiActivityEnergyInfoAsync(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiActivityEnergyInfoListener);
     method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.WifiConfiguration getWifiApConfiguration();
     method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public int getWifiApState();
@@ -8223,13 +8224,15 @@
     field public static final String ACTION_UPDATE_CARRIER_PROVISIONING_URLS = "android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS";
     field public static final String ACTION_UPDATE_CONVERSATION_ACTIONS = "android.intent.action.UPDATE_CONVERSATION_ACTIONS";
     field public static final String ACTION_UPDATE_CT_LOGS = "android.intent.action.UPDATE_CT_LOGS";
-    field public static final String ACTION_UPDATE_EMERGENCY_NUMBER_DB = "android.os.action.UPDATE_EMERGENCY_NUMBER_DB";
+    field @RequiresPermission("android.permission.UPDATE_CONFIG") public static final String ACTION_UPDATE_EMERGENCY_NUMBER_DB = "android.os.action.UPDATE_EMERGENCY_NUMBER_DB";
     field public static final String ACTION_UPDATE_INTENT_FIREWALL = "android.intent.action.UPDATE_INTENT_FIREWALL";
     field public static final String ACTION_UPDATE_LANG_ID = "android.intent.action.UPDATE_LANG_ID";
     field public static final String ACTION_UPDATE_NETWORK_WATCHLIST = "android.intent.action.UPDATE_NETWORK_WATCHLIST";
     field public static final String ACTION_UPDATE_PINS = "android.intent.action.UPDATE_PINS";
     field public static final String ACTION_UPDATE_SMART_SELECTION = "android.intent.action.UPDATE_SMART_SELECTION";
     field public static final String ACTION_UPDATE_SMS_SHORT_CODES = "android.intent.action.UPDATE_SMS_SHORT_CODES";
+    field public static final String EXTRA_REQUIRED_HASH = "android.os.extra.REQUIRED_HASH";
+    field public static final String EXTRA_VERSION = "android.os.extra.VERSION";
   }
 
   public class Environment {
@@ -8939,12 +8942,12 @@
     method @BinderThread public abstract void onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.util.List<java.lang.String>>>);
     method @BinderThread public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @BinderThread public void onStageAndApplyRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
-    method @BinderThread public void onUpdateUserSensitivePermissionFlags();
+    method @BinderThread public void onUpdateUserSensitivePermissionFlags(int, @NonNull Runnable);
     field public static final String SERVICE_INTERFACE = "android.permission.PermissionControllerService";
   }
 
   public final class PermissionManager {
-    method @IntRange(from=0) @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, "android.permission.UPGRADE_RUNTIME_PERMISSIONS"}) public int getRuntimePermissionsVersion();
+    method @IntRange(from=0) @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public int getRuntimePermissionsVersion();
     method @NonNull public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions();
     method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToEnabledCarrierApps(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToEnabledImsServices(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
@@ -8952,7 +8955,7 @@
     method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToLuiApp(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void revokeDefaultPermissionsFromDisabledTelephonyDataServices(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void revokeDefaultPermissionsFromLuiApps(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
-    method @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, "android.permission.UPGRADE_RUNTIME_PERMISSIONS"}) public void setRuntimePermissionsVersion(@IntRange(from=0) int);
+    method @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public void setRuntimePermissionsVersion(@IntRange(from=0) int);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void startOneTimePermissionSession(@NonNull String, long, int, int);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void stopOneTimePermissionSession(@NonNull String);
   }
@@ -9066,18 +9069,6 @@
 
 package android.provider {
 
-  public class BlockedNumberContract {
-    field public static final String METHOD_NOTIFY_EMERGENCY_CONTACT = "notify_emergency_contact";
-    field public static final String METHOD_SHOULD_SYSTEM_BLOCK_NUMBER = "should_system_block_number";
-    field public static final String RES_BLOCK_STATUS = "block_status";
-    field public static final int STATUS_BLOCKED_IN_LIST = 1; // 0x1
-    field public static final int STATUS_BLOCKED_NOT_IN_CONTACTS = 5; // 0x5
-    field public static final int STATUS_BLOCKED_PAYPHONE = 4; // 0x4
-    field public static final int STATUS_BLOCKED_RESTRICTED = 2; // 0x2
-    field public static final int STATUS_BLOCKED_UNKNOWN_NUMBER = 3; // 0x3
-    field public static final int STATUS_NOT_BLOCKED = 0; // 0x0
-  }
-
   @Deprecated public static final class ContactsContract.MetadataSync implements android.provider.BaseColumns android.provider.ContactsContract.MetadataSyncColumns {
     field @Deprecated public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_metadata";
     field @Deprecated public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact_metadata";
@@ -9389,7 +9380,6 @@
     field public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = "lock_screen_allow_private_notifications";
     field public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications";
     field public static final String ODI_CAPTIONS_ENABLED = "odi_captions_enabled";
-    field public static final String SECURE_FRP_MODE = "secure_frp_mode";
     field public static final String THEME_CUSTOMIZATION_OVERLAY_PACKAGES = "theme_customization_overlay_packages";
     field public static final String USER_SETUP_COMPLETE = "user_setup_complete";
     field public static final int USER_SETUP_PERSONALIZATION_COMPLETE = 10; // 0xa
@@ -9435,20 +9425,12 @@
 
   public static final class Telephony.Carriers implements android.provider.BaseColumns {
     field public static final String APN_SET_ID = "apn_set_id";
-    field @Deprecated public static final String BEARER_BITMASK = "bearer_bitmask";
     field public static final int CARRIER_EDITED = 4; // 0x4
-    field @NonNull public static final android.net.Uri DPC_URI;
     field public static final String EDITED_STATUS = "edited";
-    field public static final int INVALID_APN_ID = -1; // 0xffffffff
     field public static final String MAX_CONNECTIONS = "max_conns";
     field public static final String MODEM_PERSIST = "modem_cognitive";
     field public static final String MTU = "mtu";
     field public static final int NO_APN_SET_ID = 0; // 0x0
-    field public static final String PROFILE_ID = "profile_id";
-    field public static final String SKIP_464XLAT = "skip_464xlat";
-    field public static final int SKIP_464XLAT_DEFAULT = -1; // 0xffffffff
-    field public static final int SKIP_464XLAT_DISABLE = 0; // 0x0
-    field public static final int SKIP_464XLAT_ENABLE = 1; // 0x1
     field public static final String TIME_LIMIT_FOR_MAX_CONNECTIONS = "max_conns_time";
     field public static final int UNEDITED = 0; // 0x0
     field public static final int USER_DELETED = 2; // 0x2
@@ -9813,7 +9795,7 @@
 
   public static final class Dataset.Builder {
     ctor public Dataset.Builder(@NonNull android.service.autofill.InlinePresentation);
-    method @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation);
+    method @NonNull public android.service.autofill.Dataset.Builder setFieldInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation);
   }
 
   public abstract class InlineSuggestionRenderService extends android.app.Service {
@@ -9974,7 +9956,7 @@
   }
 
   public static final class DataLoaderService.FileSystemConnector {
-    method public void writeData(@NonNull String, long, long, @NonNull android.os.ParcelFileDescriptor) throws java.io.IOException;
+    method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void writeData(@NonNull String, long, long, @NonNull android.os.ParcelFileDescriptor) throws java.io.IOException;
   }
 
 }
@@ -10652,14 +10634,7 @@
   }
 
   public final class PhoneAccount implements android.os.Parcelable {
-    field public static final int CAPABILITY_EMERGENCY_CALLS_ONLY = 128; // 0x80
-    field public static final int CAPABILITY_EMERGENCY_PREFERRED = 8192; // 0x2000
-    field public static final int CAPABILITY_EMERGENCY_VIDEO_CALLING = 512; // 0x200
     field public static final int CAPABILITY_MULTI_USER = 32; // 0x20
-    field public static final String EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE = "android.telecom.extra.ALWAYS_USE_VOIP_AUDIO_MODE";
-    field public static final String EXTRA_PLAY_CALL_RECORDING_TONE = "android.telecom.extra.PLAY_CALL_RECORDING_TONE";
-    field public static final String EXTRA_SORT_ORDER = "android.telecom.extra.SORT_ORDER";
-    field public static final String EXTRA_SUPPORTS_VIDEO_CALLING_FALLBACK = "android.telecom.extra.SUPPORTS_VIDEO_CALLING_FALLBACK";
   }
 
   public static class PhoneAccount.Builder {
@@ -10745,20 +10720,13 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall();
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(@Nullable android.telecom.PhoneAccountHandle);
-    field public static final String ACTION_CURRENT_TTY_MODE_CHANGED = "android.telecom.action.CURRENT_TTY_MODE_CHANGED";
-    field public static final String ACTION_TTY_PREFERRED_MODE_CHANGED = "android.telecom.action.TTY_PREFERRED_MODE_CHANGED";
     field public static final int CALL_SOURCE_EMERGENCY_DIALPAD = 1; // 0x1
     field public static final int CALL_SOURCE_EMERGENCY_SHORTCUT = 2; // 0x2
     field public static final int CALL_SOURCE_UNSPECIFIED = 0; // 0x0
     field public static final String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT";
-    field public static final String EXTRA_CALL_SOURCE = "android.telecom.extra.CALL_SOURCE";
-    field public static final String EXTRA_CALL_TECHNOLOGY_TYPE = "android.telecom.extra.CALL_TECHNOLOGY_TYPE";
     field public static final String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT";
     field public static final String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE";
-    field public static final String EXTRA_CURRENT_TTY_MODE = "android.telecom.extra.CURRENT_TTY_MODE";
     field public static final String EXTRA_IS_USER_INTENT_EMERGENCY_CALL = "android.telecom.extra.IS_USER_INTENT_EMERGENCY_CALL";
-    field public static final String EXTRA_TTY_PREFERRED_MODE = "android.telecom.extra.TTY_PREFERRED_MODE";
-    field public static final String EXTRA_UNKNOWN_CALL_HANDLE = "android.telecom.extra.UNKNOWN_CALL_HANDLE";
     field public static final int TTY_MODE_FULL = 1; // 0x1
     field public static final int TTY_MODE_HCO = 2; // 0x2
     field public static final int TTY_MODE_OFF = 0; // 0x0
@@ -11206,19 +11174,6 @@
     field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000
   }
 
-  public final class PinResult implements android.os.Parcelable {
-    ctor public PinResult(int, int);
-    method public int describeContents();
-    method public int getAttemptsRemaining();
-    method @NonNull public static android.telephony.PinResult getDefaultFailedResult();
-    method public int getType();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PinResult> CREATOR;
-    field public static final int PIN_RESULT_TYPE_FAILURE = 2; // 0x2
-    field public static final int PIN_RESULT_TYPE_INCORRECT = 1; // 0x1
-    field public static final int PIN_RESULT_TYPE_SUCCESS = 0; // 0x0
-  }
-
   public final class PreciseCallState implements android.os.Parcelable {
     ctor public PreciseCallState(int, int, int, int, int);
     method public int describeContents();
@@ -11349,7 +11304,6 @@
   public class ServiceState implements android.os.Parcelable {
     method public void fillInNotifierBundle(@NonNull android.os.Bundle);
     method public int getDataNetworkType();
-    method public int getDataRegistrationState();
     method @Nullable public android.telephony.NetworkRegistrationInfo getNetworkRegistrationInfo(int, int);
     method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForDomain(int);
     method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForTransportType(int);
@@ -11583,7 +11537,6 @@
   }
 
   public class TelephonyManager {
-    method public int addDevicePolicyOverrideApn(@NonNull android.content.Context, @NonNull android.telephony.data.ApnSetting);
     method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void call(String, String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int changeIccLockPassword(@NonNull String, @NonNull String);
     method public int checkCarrierPrivilegesForPackage(String);
@@ -11616,11 +11569,9 @@
     method @Deprecated public boolean getDataEnabled();
     method @Deprecated public boolean getDataEnabled(int);
     method @Nullable public static android.content.ComponentName getDefaultRespondViaMessageApplication(@NonNull android.content.Context, boolean);
-    method @NonNull public static String getDefaultSimCountryIso();
-    method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context);
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDeviceSoftwareVersion(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
-    method public int getEmergencyNumberDbVersion();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String[] getIsimImpu();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
@@ -11650,7 +11601,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApnMetered(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataAllowedInVoiceCall();
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataConnectionEnabled();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataConnectionAllowed();
     method public boolean isDataConnectivityPossible();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
@@ -11658,7 +11609,6 @@
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isInEmergencySmsMode();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled();
-    method public boolean isModemEnabledForSlot(int);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
@@ -11668,7 +11618,6 @@
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isVideoCallingEnabled();
     method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean matchesCurrentSimOperator(@NonNull String, int, @Nullable String);
-    method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting);
     method public boolean needsOtaServiceProvisioning();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOtaEmergencyNumberDbInstalled();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyUserActivity();
@@ -11711,10 +11660,8 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoiceActivationState(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void shutdownAllRadios();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean supplyPin(String);
-    method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult supplyPinReportPinResult(@NonNull String);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int[] supplyPinReportResult(String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean supplyPuk(String, String);
-    method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult supplyPukReportPinResult(@NonNull String, @NonNull String);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int[] supplyPukReportResult(String, String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean switchSlots(int[]);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void toggleRadioOnOff();
@@ -11807,7 +11754,6 @@
     field public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = 65536L; // 0x10000L
     field public static final long NETWORK_TYPE_BITMASK_UMTS = 4L; // 0x4L
     field public static final long NETWORK_TYPE_BITMASK_UNKNOWN = 0L; // 0x0L
-    field public static final int PHONE_TYPE_THIRD_PARTY = 4; // 0x4
     field public static final int RADIO_POWER_OFF = 0; // 0x0
     field public static final int RADIO_POWER_ON = 1; // 0x1
     field public static final int RADIO_POWER_UNAVAILABLE = 2; // 0x2
@@ -11887,23 +11833,6 @@
 
 package android.telephony.data {
 
-  public class ApnSetting implements android.os.Parcelable {
-    method @NonNull public static String getApnTypesStringFromBitmask(int);
-    field public static final String TYPE_ALL_STRING = "*";
-    field public static final String TYPE_CBS_STRING = "cbs";
-    field public static final String TYPE_DEFAULT_STRING = "default";
-    field public static final String TYPE_DUN_STRING = "dun";
-    field public static final String TYPE_EMERGENCY_STRING = "emergency";
-    field public static final String TYPE_FOTA_STRING = "fota";
-    field public static final String TYPE_HIPRI_STRING = "hipri";
-    field public static final String TYPE_IA_STRING = "ia";
-    field public static final String TYPE_IMS_STRING = "ims";
-    field public static final String TYPE_MCX_STRING = "mcx";
-    field public static final String TYPE_MMS_STRING = "mms";
-    field public static final String TYPE_SUPL_STRING = "supl";
-    field public static final String TYPE_XCAP_STRING = "xcap";
-  }
-
   public final class DataCallResponse implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public java.util.List<android.net.LinkAddress> getAddresses();
@@ -13262,6 +13191,7 @@
     method public long getEventTime();
     method @Nullable public android.view.autofill.AutofillId getId();
     method @Nullable public java.util.List<android.view.autofill.AutofillId> getIds();
+    method @Nullable public android.graphics.Insets getInsets();
     method @Nullable public CharSequence getText();
     method public int getType();
     method @Nullable public android.view.contentcapture.ViewNode getViewNode();
@@ -13272,6 +13202,7 @@
     field public static final int TYPE_SESSION_RESUMED = 7; // 0x7
     field public static final int TYPE_VIEW_APPEARED = 1; // 0x1
     field public static final int TYPE_VIEW_DISAPPEARED = 2; // 0x2
+    field public static final int TYPE_VIEW_INSETS_CHANGED = 9; // 0x9
     field public static final int TYPE_VIEW_TEXT_CHANGED = 3; // 0x3
     field public static final int TYPE_VIEW_TREE_APPEARED = 5; // 0x5
     field public static final int TYPE_VIEW_TREE_APPEARING = 4; // 0x4
diff --git a/api/test-current.txt b/api/test-current.txt
index 9d284b5..1403b69 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -21,6 +21,7 @@
     field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
     field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
     field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
+    field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS";
     field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG";
     field @Deprecated public static final String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE";
     field public static final String WRITE_OBB = "android.permission.WRITE_OBB";
@@ -82,6 +83,7 @@
     method public static void resumeAppSwitches() throws android.os.RemoteException;
     method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int);
     method @RequiresPermission("android.permission.MANAGE_USERS") public boolean switchUser(@NonNull android.os.UserHandle);
+    method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String);
     field public static final int PROCESS_CAPABILITY_ALL = 7; // 0x7
     field public static final int PROCESS_CAPABILITY_ALL_EXPLICIT = 1; // 0x1
     field public static final int PROCESS_CAPABILITY_ALL_IMPLICIT = 6; // 0x6
@@ -262,14 +264,37 @@
     field public static final int UID_STATE_TOP = 200; // 0xc8
   }
 
-  public static final class AppOpsManager.HistoricalFeatureOps implements android.os.Parcelable {
+  public static final class AppOpsManager.AttributedHistoricalOps implements android.os.Parcelable {
     method public int describeContents();
-    method @Nullable public String getFeatureId();
     method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String);
     method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int);
     method @IntRange(from=0) public int getOpCount();
+    method @Nullable public String getTag();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalFeatureOps> CREATOR;
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.AttributedHistoricalOps> CREATOR;
+  }
+
+  public static final class AppOpsManager.AttributedOpEntry implements android.os.Parcelable {
+    method public int describeContents();
+    method public long getLastAccessBackgroundTime(int);
+    method public long getLastAccessForegroundTime(int);
+    method public long getLastAccessTime(int);
+    method public long getLastAccessTime(int, int, int);
+    method public long getLastBackgroundDuration(int);
+    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastBackgroundProxyInfo(int);
+    method public long getLastDuration(int);
+    method public long getLastDuration(int, int, int);
+    method public long getLastForegroundDuration(int);
+    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastForegroundProxyInfo(int);
+    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int);
+    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int, int, int);
+    method public long getLastRejectBackgroundTime(int);
+    method public long getLastRejectForegroundTime(int);
+    method public long getLastRejectTime(int);
+    method public long getLastRejectTime(int, int, int);
+    method public boolean isRunning();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.AttributedOpEntry> CREATOR;
   }
 
   public static final class AppOpsManager.HistoricalOp implements android.os.Parcelable {
@@ -310,7 +335,7 @@
   public static final class AppOpsManager.HistoricalOpsRequest.Builder {
     ctor public AppOpsManager.HistoricalOpsRequest.Builder(long, long);
     method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest build();
-    method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFeatureId(@Nullable String);
+    method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setAttributionTag(@Nullable String);
     method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFlags(int);
     method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setOpNames(@Nullable java.util.List<java.lang.String>);
     method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setPackageName(@Nullable String);
@@ -319,9 +344,9 @@
 
   public static final class AppOpsManager.HistoricalPackageOps implements android.os.Parcelable {
     method public int describeContents();
-    method @IntRange(from=0) public int getFeatureCount();
-    method @Nullable public android.app.AppOpsManager.HistoricalFeatureOps getFeatureOps(@NonNull String);
-    method @NonNull public android.app.AppOpsManager.HistoricalFeatureOps getFeatureOpsAt(@IntRange(from=0) int);
+    method @Nullable public android.app.AppOpsManager.AttributedHistoricalOps getAttributedOps(@NonNull String);
+    method @NonNull public android.app.AppOpsManager.AttributedHistoricalOps getAttributedOpsAt(@IntRange(from=0) int);
+    method @IntRange(from=0) public int getAttributedOpsCount();
     method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String);
     method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int);
     method @IntRange(from=0) public int getOpCount();
@@ -342,8 +367,8 @@
 
   public static final class AppOpsManager.OpEntry implements android.os.Parcelable {
     method public int describeContents();
+    method @NonNull public java.util.Map<java.lang.String,android.app.AppOpsManager.AttributedOpEntry> getAttributedOpEntries();
     method @Deprecated public long getDuration();
-    method @NonNull public java.util.Map<java.lang.String,android.app.AppOpsManager.OpFeatureEntry> getFeatures();
     method public long getLastAccessBackgroundTime(int);
     method public long getLastAccessForegroundTime(int);
     method public long getLastAccessTime(int);
@@ -373,36 +398,13 @@
 
   public static final class AppOpsManager.OpEventProxyInfo implements android.os.Parcelable {
     method public int describeContents();
-    method @Nullable public String getFeatureId();
+    method @Nullable public String getAttributionTag();
     method @Nullable public String getPackageName();
     method @IntRange(from=0) public int getUid();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpEventProxyInfo> CREATOR;
   }
 
-  public static final class AppOpsManager.OpFeatureEntry implements android.os.Parcelable {
-    method public int describeContents();
-    method public long getLastAccessBackgroundTime(int);
-    method public long getLastAccessForegroundTime(int);
-    method public long getLastAccessTime(int);
-    method public long getLastAccessTime(int, int, int);
-    method public long getLastBackgroundDuration(int);
-    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastBackgroundProxyInfo(int);
-    method public long getLastDuration(int);
-    method public long getLastDuration(int, int, int);
-    method public long getLastForegroundDuration(int);
-    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastForegroundProxyInfo(int);
-    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int);
-    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int, int, int);
-    method public long getLastRejectBackgroundTime(int);
-    method public long getLastRejectForegroundTime(int);
-    method public long getLastRejectTime(int);
-    method public long getLastRejectTime(int, int, int);
-    method public boolean isRunning();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpFeatureEntry> CREATOR;
-  }
-
   public static final class AppOpsManager.PackageOps implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public java.util.List<android.app.AppOpsManager.OpEntry> getOps();
@@ -427,6 +429,7 @@
     method public boolean isBlockableSystem();
     method public boolean isImportanceLockedByCriticalDeviceFunction();
     method public boolean isImportanceLockedByOEM();
+    method public void lockFields(int);
     method public void setBlockableSystem(boolean);
     method public void setDeleted(boolean);
     method public void setFgServiceShown(boolean);
@@ -434,6 +437,7 @@
     method public void setImportanceLockedByOEM(boolean);
     method public void setImportantConversation(boolean);
     method public void setOriginalImportance(int);
+    field public static final int USER_LOCKED_SOUND = 32; // 0x20
   }
 
   public final class NotificationChannelGroup implements android.os.Parcelable {
@@ -462,7 +466,7 @@
   public final class RuntimeAppOpAccessMessage implements android.os.Parcelable {
     ctor public RuntimeAppOpAccessMessage(@IntRange(from=0L) int, @IntRange(from=0L) int, @NonNull String, @Nullable String, @NonNull String, int);
     method public int describeContents();
-    method @Nullable public String getFeatureId();
+    method @Nullable public String getAttributionTag();
     method @NonNull public String getMessage();
     method @NonNull public String getOp();
     method @NonNull public String getPackageName();
@@ -1278,7 +1282,7 @@
 
   public final class LightsManager.LightsSession implements java.lang.AutoCloseable {
     method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void close();
-    method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void setLights(@NonNull android.hardware.lights.LightsRequest);
+    method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void requestLights(@NonNull android.hardware.lights.LightsRequest);
   }
 
   public final class LightsRequest {
@@ -2811,9 +2815,9 @@
   }
 
   public final class PermissionManager {
-    method @IntRange(from=0) @RequiresPermission(anyOf={"android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY", "android.permission.UPGRADE_RUNTIME_PERMISSIONS"}) public int getRuntimePermissionsVersion();
+    method @IntRange(from=0) @RequiresPermission(anyOf={"android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY", android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public int getRuntimePermissionsVersion();
     method @NonNull public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions();
-    method @RequiresPermission(anyOf={"android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY", "android.permission.UPGRADE_RUNTIME_PERMISSIONS"}) public void setRuntimePermissionsVersion(@IntRange(from=0) int);
+    method @RequiresPermission(anyOf={"android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY", android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public void setRuntimePermissionsVersion(@IntRange(from=0) int);
   }
 
   public static final class PermissionManager.SplitPermissionInfo {
@@ -3131,7 +3135,7 @@
 
   public static final class Dataset.Builder {
     ctor public Dataset.Builder(@NonNull android.service.autofill.InlinePresentation);
-    method @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation);
+    method @NonNull public android.service.autofill.Dataset.Builder setFieldInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation);
   }
 
   public final class DateTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
@@ -3742,7 +3746,7 @@
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
     method @Nullable public static android.content.ComponentName getDefaultRespondViaMessageApplication(@NonNull android.content.Context, boolean);
     method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context);
-    method public int getEmergencyNumberDbVersion();
+    method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getEmergencyNumberDbVersion();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag();
     method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion();
     method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting);
@@ -4967,7 +4971,7 @@
     method public void resetRtlProperties();
     method public boolean restoreFocusInCluster(int);
     method public boolean restoreFocusNotInCluster();
-    method public void setAutofilled(boolean);
+    method public void setAutofilled(boolean, boolean);
     method public final void setFocusedInCluster();
     method public void setIsRootNamespace(boolean);
     method public final void setShowingLayoutBounds(boolean);
@@ -5081,6 +5085,7 @@
     method public long getEventTime();
     method @Nullable public android.view.autofill.AutofillId getId();
     method @Nullable public java.util.List<android.view.autofill.AutofillId> getIds();
+    method @Nullable public android.graphics.Insets getInsets();
     method @Nullable public CharSequence getText();
     method public int getType();
     method @Nullable public android.view.contentcapture.ViewNode getViewNode();
@@ -5091,6 +5096,7 @@
     field public static final int TYPE_SESSION_RESUMED = 7; // 0x7
     field public static final int TYPE_VIEW_APPEARED = 1; // 0x1
     field public static final int TYPE_VIEW_DISAPPEARED = 2; // 0x2
+    field public static final int TYPE_VIEW_INSETS_CHANGED = 9; // 0x9
     field public static final int TYPE_VIEW_TEXT_CHANGED = 3; // 0x3
     field public static final int TYPE_VIEW_TREE_APPEARED = 5; // 0x5
     field public static final int TYPE_VIEW_TREE_APPEARING = 4; // 0x4
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index f84e4b5..8f5e49d 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -113,7 +113,7 @@
 Status Idmap2Service::createIdmap(const std::string& target_apk_path,
                                   const std::string& overlay_apk_path, int32_t fulfilled_policies,
                                   bool enforce_overlayable, int32_t user_id ATTRIBUTE_UNUSED,
-                                  std::unique_ptr<std::string>* _aidl_return) {
+                                  aidl::nullable<std::string>* _aidl_return) {
   assert(_aidl_return);
   SYSTRACE << "Idmap2Service::createIdmap " << target_apk_path << " " << overlay_apk_path;
   _aidl_return->reset(nullptr);
@@ -155,7 +155,7 @@
     return error("failed to write to idmap path " + idmap_path);
   }
 
-  *_aidl_return = std::make_unique<std::string>(idmap_path);
+  *_aidl_return = aidl::make_nullable<std::string>(idmap_path);
   return ok();
 }
 
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h
index 94d2af4..b6f5136 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.h
+++ b/cmds/idmap2/idmap2d/Idmap2Service.h
@@ -19,9 +19,7 @@
 
 #include <android-base/unique_fd.h>
 #include <binder/BinderService.h>
-
-#include <memory>
-#include <string>
+#include <binder/Nullable.h>
 
 #include "android/os/BnIdmap2.h"
 
@@ -46,7 +44,7 @@
   binder::Status createIdmap(const std::string& target_apk_path,
                              const std::string& overlay_apk_path, int32_t fulfilled_policies,
                              bool enforce_overlayable, int32_t user_id,
-                             std::unique_ptr<std::string>* _aidl_return) override;
+                             aidl::nullable<std::string>* _aidl_return) override;
 };
 
 }  // namespace android::os
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index bd6ca47..33bfe51 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -113,8 +113,8 @@
         "libbase",
         "libcutils",
         "libprotoutil",
-        "libstatslog",
         "libstatsmetadata",
+        "libstatslog_statsd",
         "libsysutils",
         "libutils",
     ],
@@ -171,6 +171,37 @@
     ],
 }
 
+genrule {
+    name: "statslog_statsd.h",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_statsd.h --module statsd --namespace android,os,statsd,util",
+    out: [
+        "statslog_statsd.h",
+    ],
+}
+
+genrule {
+    name: "statslog_statsd.cpp",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_statsd.cpp --module statsd --namespace android,os,statsd,util --importHeader statslog_statsd.h",
+    out: [
+        "statslog_statsd.cpp",
+    ],
+}
+
+cc_library_static {
+    name: "libstatslog_statsd",
+    generated_sources: ["statslog_statsd.cpp"],
+    generated_headers: ["statslog_statsd.h"],
+    export_generated_headers: ["statslog_statsd.h"],
+    apex_available: [
+        "com.android.os.statsd",
+        "test_com.android.os.statsd",
+    ],
+    shared_libs: [
+        "libstatssocket",
+    ]
+}
 
 // =========
 // statsd
@@ -300,6 +331,7 @@
     static_libs: [
         "libgmock",
         "libplatformprotos",
+        "libstatslog", //TODO(b/150976524): remove this when the tests no longer hardcode atoms.
         "libstatssocket_private",
     ],
 
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 649c004..69b9fc7 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -34,7 +34,7 @@
 #include "state/StateManager.h"
 #include "stats_log_util.h"
 #include "stats_util.h"
-#include "statslog.h"
+#include "statslog_statsd.h"
 #include "storage/StorageManager.h"
 
 using namespace android;
@@ -287,17 +287,17 @@
         int64_t firstId = trainInfo->experimentIds.at(0);
         auto& ids = trainInfo->experimentIds;
         switch (trainInfo->status) {
-            case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALL_SUCCESS:
+            case android::os::statsd::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALL_SUCCESS:
                 if (find(ids.begin(), ids.end(), firstId + 1) == ids.end()) {
                     ids.push_back(firstId + 1);
                 }
                 break;
-            case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_INITIATED:
+            case android::os::statsd::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_INITIATED:
                 if (find(ids.begin(), ids.end(), firstId + 2) == ids.end()) {
                     ids.push_back(firstId + 2);
                 }
                 break;
-            case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_SUCCESS:
+            case android::os::statsd::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_SUCCESS:
                 if (find(ids.begin(), ids.end(), firstId + 3) == ids.end()) {
                     ids.push_back(firstId + 3);
                 }
@@ -366,13 +366,13 @@
     int64_t firstId = trainInfoOnDisk.experimentIds[0];
     auto& ids = trainInfoOnDisk.experimentIds;
     switch (rollbackTypeIn) {
-        case android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE:
+      case android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE:
             if (find(ids.begin(), ids.end(), firstId + 4) == ids.end()) {
                 ids.push_back(firstId + 4);
             }
             StorageManager::writeTrainInfo(trainInfoOnDisk);
             break;
-        case android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS:
+      case android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS:
             if (find(ids.begin(), ids.end(), firstId + 5) == ids.end()) {
                 ids.push_back(firstId + 5);
             }
@@ -405,13 +405,13 @@
 
     // Hard-coded logic to update train info on disk and fill in any information
     // this log event may be missing.
-    if (event->GetTagId() == android::util::BINARY_PUSH_STATE_CHANGED) {
+    if (event->GetTagId() == android::os::statsd::util::BINARY_PUSH_STATE_CHANGED) {
         onBinaryPushStateChangedEventLocked(event);
     }
 
     // Hard-coded logic to update experiment ids on disk for certain rollback
     // types and fill the rollback atom with experiment ids
-    if (event->GetTagId() == android::util::WATCHDOG_ROLLBACK_OCCURRED) {
+    if (event->GetTagId() == android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED) {
         onWatchdogRollbackOccurredLocked(event);
     }
 
@@ -429,7 +429,7 @@
 
     // Hard-coded logic to update the isolated uid's in the uid-map.
     // The field numbers need to be currently updated by hand with atoms.proto
-    if (event->GetTagId() == android::util::ISOLATED_UID_CHANGED) {
+    if (event->GetTagId() == android::os::statsd::util::ISOLATED_UID_CHANGED) {
         onIsolatedUidChangedEventLocked(*event);
     }
 
@@ -446,7 +446,7 @@
     }
 
 
-    if (event->GetTagId() != android::util::ISOLATED_UID_CHANGED) {
+    if (event->GetTagId() != android::os::statsd::util::ISOLATED_UID_CHANGED) {
         // Map the isolated uid to host uid if necessary.
         mapIsolatedUidToHostUidIfNecessaryLocked(event);
     }
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index cd10457..07579bb 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -32,7 +32,7 @@
 #include <frameworks/base/cmds/statsd/src/statsd_config.pb.h>
 #include <frameworks/base/cmds/statsd/src/uid_data.pb.h>
 #include <private/android_filesystem_config.h>
-#include <statslog.h>
+#include <statslog_statsd.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/system_properties.h>
@@ -767,7 +767,8 @@
     }
     if (good) {
         dprintf(out, "Logging AppBreadcrumbReported(%d, %d, %d) to statslog.\n", uid, label, state);
-        android::util::stats_write(android::util::APP_BREADCRUMB_REPORTED, uid, label, state);
+        android::os::statsd::util::stats_write(
+                android::os::statsd::util::APP_BREADCRUMB_REPORTED, uid, label, state);
     } else {
         print_cmd_help(out);
         return UNKNOWN_ERROR;
@@ -1188,7 +1189,7 @@
 Status StatsService::sendAppBreadcrumbAtom(int32_t label, int32_t state) {
     // Permission check not necessary as it's meant for applications to write to
     // statsd.
-    android::util::stats_write(util::APP_BREADCRUMB_REPORTED,
+    android::os::statsd::util::stats_write(android::os::statsd::util::APP_BREADCRUMB_REPORTED,
                                (int32_t) AIBinder_getCallingUid(), label,
                                state);
     return Status::ok();
diff --git a/cmds/statsd/src/anomaly/AlarmTracker.cpp b/cmds/statsd/src/anomaly/AlarmTracker.cpp
index 019a9f7..5722f92 100644
--- a/cmds/statsd/src/anomaly/AlarmTracker.cpp
+++ b/cmds/statsd/src/anomaly/AlarmTracker.cpp
@@ -23,7 +23,6 @@
 #include "stats_util.h"
 #include "storage/StorageManager.h"
 
-#include <statslog.h>
 #include <time.h>
 
 namespace android {
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index 7ace44e..a21abbf 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -25,7 +25,7 @@
 #include "subscriber/SubscriberReporter.h"
 
 #include <inttypes.h>
-#include <statslog.h>
+#include <statslog_statsd.h>
 #include <time.h>
 
 namespace android {
@@ -235,8 +235,8 @@
     StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.id());
 
     // TODO(b/110564268): This should also take in the const MetricDimensionKey& key?
-    android::util::stats_write(android::util::ANOMALY_DETECTED, mConfigKey.GetUid(),
-                               mConfigKey.GetId(), mAlert.id());
+    util::stats_write(util::ANOMALY_DETECTED, mConfigKey.GetUid(),
+                      mConfigKey.GetId(), mAlert.id());
 }
 
 void AnomalyTracker::detectAndDeclareAnomaly(const int64_t& timestampNs,
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 80f9fea..bd5bdc6 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -122,10 +122,10 @@
         SettingChanged setting_changed = 41 [(module) = "framework"];
         ActivityForegroundStateChanged activity_foreground_state_changed =
                 42 [(module) = "framework"];
-        IsolatedUidChanged isolated_uid_changed = 43 [(module) = "framework"];
+        IsolatedUidChanged isolated_uid_changed = 43 [(module) = "framework", (module) = "statsd"];
         PacketWakeupOccurred packet_wakeup_occurred = 44 [(module) = "framework"];
         WallClockTimeShifted wall_clock_time_shifted = 45 [(module) = "framework"];
-        AnomalyDetected anomaly_detected = 46;
+        AnomalyDetected anomaly_detected = 46 [(module) = "statsd"];
         AppBreadcrumbReported app_breadcrumb_reported =
                 47 [(allow_from_any_uid) = true, (module) = "statsd"];
         AppStartOccurred app_start_occurred = 48 [(module) = "framework"];
@@ -138,7 +138,7 @@
         AppStartMemoryStateCaptured app_start_memory_state_captured = 55 [(module) = "framework"];
         ShutdownSequenceReported shutdown_sequence_reported = 56 [(module) = "framework"];
         BootSequenceReported boot_sequence_reported = 57;
-        DaveyOccurred davey_occurred = 58 [(allow_from_any_uid) = true];
+        DaveyOccurred davey_occurred = 58 [(allow_from_any_uid) = true, (module) = "statsd"];
         OverlayStateChanged overlay_state_changed = 59 [(module) = "framework"];
         ForegroundServiceStateChanged foreground_service_state_changed
                 = 60 [(module) = "framework"];
@@ -166,7 +166,7 @@
         LowMemReported low_mem_reported = 81 [(module) = "framework"];
         GenericAtom generic_atom = 82;
         KeyValuePairsAtom key_value_pairs_atom =
-                83 [(allow_from_any_uid) = true, (module) = "framework"];
+                83 [(allow_from_any_uid) = true, (module) = "framework", (module) = "statsd"];
         VibratorStateChanged vibrator_state_changed = 84 [(module) = "framework"];
         DeferredJobStatsReported deferred_job_stats_reported = 85 [(module) = "framework"];
         ThermalThrottlingStateChanged thermal_throttling = 86 [deprecated=true];
@@ -242,7 +242,8 @@
         AdbConnectionChanged adb_connection_changed = 144 [(module) = "framework"];
         SpeechDspStatReported speech_dsp_stat_reported = 145;
         UsbContaminantReported usb_contaminant_reported = 146 [(module) = "framework"];
-        WatchdogRollbackOccurred watchdog_rollback_occurred = 147 [(module) = "framework"];
+        WatchdogRollbackOccurred watchdog_rollback_occurred =
+                147 [(module) = "framework", (module) = "statsd"];
         BiometricSystemHealthIssueDetected biometric_system_health_issue_detected =
                 148 [(module) = "framework"];
         BubbleUIChanged bubble_ui_changed = 149 [(module) = "sysui"];
@@ -401,7 +402,7 @@
     }
 
     // Pulled events will start at field 10000.
-    // Next: 10076
+    // Next: 10080
     oneof pulled {
         WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"];
         WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"];
@@ -412,7 +413,8 @@
         SubsystemSleepState subsystem_sleep_state = 10005;
         CpuTimePerFreq cpu_time_per_freq = 10008 [(module) = "framework"];
         CpuTimePerUid cpu_time_per_uid = 10009 [(module) = "framework"];
-        CpuTimePerUidFreq cpu_time_per_uid_freq = 10010 [(module) = "framework"];
+        CpuTimePerUidFreq cpu_time_per_uid_freq =
+                10010 [(module) = "framework", (module) = "statsd"];
         WifiActivityInfo wifi_activity_info = 10011 [(module) = "framework"];
         ModemActivityInfo modem_activity_info = 10012 [(module) = "framework"];
         BluetoothActivityInfo bluetooth_activity_info = 10007 [(module) = "framework"];
@@ -425,9 +427,9 @@
         RemainingBatteryCapacity remaining_battery_capacity = 10019 [(module) = "framework"];
         FullBatteryCapacity full_battery_capacity = 10020 [(module) = "framework"];
         Temperature temperature = 10021 [(module) = "framework"];
-        BinderCalls binder_calls = 10022 [(module) = "framework"];
+        BinderCalls binder_calls = 10022 [(module) = "framework", (module) = "statsd"];
         BinderCallsExceptions binder_calls_exceptions = 10023 [(module) = "framework"];
-        LooperStats looper_stats = 10024 [(module) = "framework"];
+        LooperStats looper_stats = 10024 [(module) = "framework", (module) = "statsd"];
         DiskStats disk_stats = 10025 [(module) = "framework"];
         DirectoryUsage directory_usage = 10026 [(module) = "framework"];
         AppSize app_size = 10027 [(module) = "framework"];
@@ -455,7 +457,7 @@
         NumFacesEnrolled num_faces_enrolled = 10048 [(module) = "framework"];
         RoleHolder role_holder = 10049 [(module) = "framework"];
         DangerousPermissionState dangerous_permission_state = 10050 [(module) = "framework"];
-        TrainInfo train_info = 10051;
+        TrainInfo train_info = 10051 [(module) = "statsd"];
         TimeZoneDataInfo time_zone_data_info = 10052 [(module) = "framework"];
         ExternalStorageInfo external_storage_info = 10053 [(module) = "framework"];
         GpuStatsGlobalInfo gpu_stats_global_info = 10054;
@@ -483,7 +485,11 @@
         PackageNotificationChannelGroupPreferences package_notification_channel_group_preferences =
                 10073 [(module) = "framework"];
         GnssStats gnss_stats = 10074 [(module) = "framework"];
-        AppFeaturesOps app_features_ops = 10075 [(module) = "framework"];
+        AttributedAppOps attributed_app_ops = 10075 [(module) = "framework"];
+        VoiceCallSession voice_call_session = 10076 [(module) = "telephony"];
+        VoiceCallRatUsage voice_call_rat_usage = 10077 [(module) = "telephony"];
+        SimSlotState sim_slot_state = 10078 [(module) = "telephony"];
+        SupportedRadioAccessFamily supported_radio_access_family = 10079 [(module) = "telephony"];
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -4475,8 +4481,7 @@
  * after an OTA.
  *
  * Logged from:
- *  - system/core/fs_mgr/libsnapshot/snapshot.cpp
- *  - system/core/fs_mgr/libsnapshot/snapshotctl.cpp
+ *  - system/update_engine/cleanup_previous_update_action.cc
  */
 message SnapshotMergeReported {
     // Keep in sync with
@@ -7598,18 +7603,19 @@
 }
 
 /**
- * Historical app ops data per package and features.
+ * Historical app ops data per package and attribution tag.
  */
-message AppFeaturesOps {
+message AttributedAppOps {
     // Uid of the package requesting the op
     optional int32 uid = 1 [(is_uid) = true];
 
     // Name of the package performing the op
     optional string package_name = 2;
 
-    // feature id; provided by developer when accessing related API, limited at 50 chars by API.
-    // Features must be provided through manifest using <feature> tag available in R and above.
-    optional string feature_id = 3;
+    // tag; provided by developer when accessing related API, limited at 50 chars by API.
+    // Attributions must be provided through manifest using <attribution> tag available in R and
+    // above.
+    optional string tag = 3;
 
     // operation id; maps to the OPSTR_* constants in AppOpsManager.java
     optional string op = 4;
@@ -8471,9 +8477,11 @@
     // operation string id per OPSTR_ constants in AppOpsManager.java
     optional string op = 3;
 
-    // feature id; provided by developer when accessing related API, limited at 50 chars by API.
-    // Features must be provided through manifest using <feature> tag available in R and above.
-    optional string feature_id = 4;
+    // attribution_tag; provided by developer when accessing related API, limited at 50 chars by
+    // API.
+    // Attributions must be provided through manifest using <attribution> tag available in R and
+    // above.
+    optional string attribution_tag = 4;
 
     // message related to app op access, limited to 600 chars by API
     optional string message = 5;
@@ -8670,6 +8678,154 @@
 }
 
 /**
+ * Pulls information for a single voice call.
+ *
+ * Each pull creates multiple atoms, one for each call. The sequence is randomized when pulled.
+ *
+ * Pulled from:
+ *   frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/PersistPullers.java
+ */
+message VoiceCallSession {
+    // Bearer (IMS or CS) when the call started.
+    optional android.telephony.CallBearerEnum bearer_at_start = 1;
+
+    // Bearer (IMS or CS) when the call ended.
+    // The bearer may change during the call, e.g. due to SRVCC.
+    optional android.telephony.CallBearerEnum bearer_at_end = 2;
+
+    // Direction of the call (incoming or outgoing).
+    optional android.telephony.CallDirectionEnum direction = 3;
+
+    // Time spent setting up the call.
+    optional android.telephony.CallSetupDurationEnum setup_duration = 4;
+
+    // Whether the call ended before the setup was completed.
+    optional bool setup_failed = 5;
+
+    // IMS reason code or CS disconnect cause.
+    // For IMS, see: frameworks/base/telephony/java/android/telephony/ims/ImsReasonInfo.java
+    // For CS, see: frameworks/base/telephony/java/android/telephony/DisconnectCause.java
+    optional int32 disconnect_reason_code = 6;
+
+    // IMS extra code or CS precise disconnect cause.
+    // For IMS, this code is vendor-specific
+    // For CS, see: frameworks/base/telephony/java/android/telephony/PreciseDisconnectCause.java
+    optional int32 disconnect_extra_code = 7;
+
+    // IMS extra message or CS vendor cause.
+    optional string disconnect_extra_message = 8;
+
+    // Radio access technology (RAT) used when call started.
+    optional android.telephony.NetworkTypeEnum rat_at_start = 9;
+
+    // Radio access technology (RAT) used when call terminated.
+    optional android.telephony.NetworkTypeEnum rat_at_end = 10;
+
+    // Number of times RAT changed during the call.
+    optional int64 rat_switch_count = 11;
+
+    // A bitmask of all codecs used during the call.
+    // See: frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
+    optional int64 codec_bitmask = 12;
+
+    // Number of other calls going on during call setup, for the same SIM slot.
+    optional int32 concurrent_call_count_at_start = 13;
+
+    // Number of other calls going on during call termination, for the same SIM slot.
+    optional int32 concurrent_call_count_at_end = 14;
+
+    // Index of the SIM is used, 0 for single-SIM devices.
+    optional int32 sim_slot_index = 15;
+
+    // Whether the device was in multi-SIM mode (with multiple active SIM profiles).
+    optional bool is_multi_sim = 16;
+
+    // Whether the call was made with an eSIM profile.
+    optional bool is_esim = 17;
+
+    // Carrier ID of the SIM card.
+    // See https://source.android.com/devices/tech/config/carrierid.
+    optional int32 carrier_id = 18;
+
+    // Whether an SRVCC has been completed successfully.
+    // SRVCC (CS fallback) should be recorded in the IMS call since there will be no more SRVCC
+    // events once the call is switched to CS.
+    optional bool srvcc_completed = 19;
+
+    // Number of SRVCC failures.
+    optional int64 srvcc_failure_count = 20;
+
+    // Number of SRVCC cancellations.
+    optional int64 srvcc_cancellation_count = 21;
+
+    // Whether the Real-Time Text (RTT) was ever used in the call.
+    optional bool rtt_enabled = 22;
+
+    // Whether this was an emergency call.
+    optional bool is_emergency = 23;
+
+    // Whether the call was performed while roaming.
+    optional bool is_roaming = 24;
+}
+
+/**
+ * Pulls voice call radio access technology (RAT) usage.
+ *
+ * Each pull creates multiple atoms, one for each carrier/RAT, the order of which is irrelevant to
+ * time. The atom will be skipped if not enough data is available.
+ *
+ * Pulled from:
+ *   frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/PersistPullers.java
+ */
+message VoiceCallRatUsage {
+    // Carrier ID (https://source.android.com/devices/tech/config/carrierid).
+    optional int32 carrier_id = 1;
+
+    // Radio access technology.
+    optional android.telephony.NetworkTypeEnum rat = 2;
+
+    // Total duration that voice calls spent on this carrier and RAT.
+    optional int64 total_duration_seconds = 3;
+
+    // Total number of calls using this carrier and RAT.
+    // A call is counted once even if it used the RAT multiple times.
+    optional int64 call_count = 4;
+}
+
+/**
+ * Pulls the number of active SIM slots and SIMs/eSIM profiles.
+ *
+ * Pulled from:
+ *   frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/NonPersistPullers.java
+ */
+message SimSlotState {
+    // Number of active SIM slots (both physical and eSIM profiles) in the device.
+    optional int32 active_slot_count = 1;
+
+    // Number of SIM cards (both physical and active eSIM profiles).
+    // This number is always equal to or less than the number of active SIM slots.
+    optional int32 sim_count = 2;
+
+    // Number of active eSIM profiles.
+    // This number is always equal to or less than the number of SIMs.
+    optional int32 esim_count = 3;
+}
+
+/**
+ * Pulls supported cellular radio access technologies.
+ *
+ * This atom reports the capabilities of the device, rather than the network it has access to.
+ *
+ * Pulled from:
+ *   frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/NonPersistPullers.java
+ */
+message SupportedRadioAccessFamily {
+    // A bitmask of supported radio technologies.
+    // See android.telephony.TelephonyManager.NetworkTypeBitMask.
+    optional int64 network_type_bitmask = 1;
+}
+
+/**
  * Logs gnss stats from location service provider
  *
  * Pulled from:
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 0115ba2..8b6a5a1 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -32,7 +32,7 @@
 #include "../statscompanion_util.h"
 #include "StatsCallbackPuller.h"
 #include "TrainInfoPuller.h"
-#include "statslog.h"
+#include "statslog_statsd.h"
 
 using std::shared_ptr;
 using std::vector;
@@ -47,7 +47,7 @@
 StatsPullerManager::StatsPullerManager()
     : kAllPullAtomInfo({
               // TrainInfo.
-              {{.atomTag = android::util::TRAIN_INFO}, new TrainInfoPuller()},
+              {{.atomTag = util::TRAIN_INFO}, new TrainInfoPuller()},
       }),
       mNextPullTimeNs(NO_ALARM_UPDATE) {
 }
diff --git a/cmds/statsd/src/external/TrainInfoPuller.cpp b/cmds/statsd/src/external/TrainInfoPuller.cpp
index a7d8d4e..3837f4a 100644
--- a/cmds/statsd/src/external/TrainInfoPuller.cpp
+++ b/cmds/statsd/src/external/TrainInfoPuller.cpp
@@ -22,7 +22,7 @@
 #include "TrainInfoPuller.h"
 #include "logd/LogEvent.h"
 #include "stats_log_util.h"
-#include "statslog.h"
+#include "statslog_statsd.h"
 #include "storage/StorageManager.h"
 
 using std::make_shared;
@@ -33,7 +33,7 @@
 namespace statsd {
 
 TrainInfoPuller::TrainInfoPuller() :
-    StatsPuller(android::util::TRAIN_INFO) {
+    StatsPuller(util::TRAIN_INFO) {
 }
 
 bool TrainInfoPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 3054b6d..2bd13d7 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -20,7 +20,7 @@
 
 #include <android/util/ProtoOutputStream.h>
 #include "../stats_log_util.h"
-#include "statslog.h"
+#include "statslog_statsd.h"
 #include "storage/StorageManager.h"
 
 namespace android {
@@ -113,9 +113,9 @@
 const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_TIME = 2;
 
 const std::map<int, std::pair<size_t, size_t>> StatsdStats::kAtomDimensionKeySizeLimitMap = {
-        {android::util::BINDER_CALLS, {6000, 10000}},
-        {android::util::LOOPER_STATS, {1500, 2500}},
-        {android::util::CPU_TIME_PER_UID_FREQ, {6000, 10000}},
+        {util::BINDER_CALLS, {6000, 10000}},
+        {util::LOOPER_STATS, {1500, 2500}},
+        {util::CPU_TIME_PER_UID_FREQ, {6000, 10000}},
 };
 
 StatsdStats::StatsdStats() {
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 974e203c..258f84d 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -18,7 +18,7 @@
 #include "logd/LogEvent.h"
 
 #include "stats_log_util.h"
-#include "statslog.h"
+#include "statslog_statsd.h"
 
 #include <android/binder_ibinder.h>
 #include <android-base/stringprintf.h>
@@ -100,7 +100,7 @@
                    const std::map<int32_t, float>& float_map) {
     mLogdTimestampNs = wallClockTimestampNs;
     mElapsedTimestampNs = elapsedTimestampNs;
-    mTagId = android::util::KEY_VALUE_PAIRS_ATOM;
+    mTagId = util::KEY_VALUE_PAIRS_ATOM;
     mLogUid = uid;
 
     int pos[] = {1, 1, 1};
@@ -153,7 +153,7 @@
                    const std::vector<uint8_t>& experimentIds, int32_t userId) {
     mLogdTimestampNs = getWallClockNs();
     mElapsedTimestampNs = getElapsedRealtimeNs();
-    mTagId = android::util::BINARY_PUSH_STATE_CHANGED;
+    mTagId = util::BINARY_PUSH_STATE_CHANGED;
     mLogUid = AIBinder_getCallingUid();
     mLogPid = AIBinder_getCallingPid();
 
@@ -172,7 +172,7 @@
                    const InstallTrainInfo& trainInfo) {
     mLogdTimestampNs = wallClockTimestampNs;
     mElapsedTimestampNs = elapsedTimestampNs;
-    mTagId = android::util::TRAIN_INFO;
+    mTagId = util::TRAIN_INFO;
 
     mValues.push_back(
             FieldValue(Field(mTagId, getSimpleField(1)), Value(trainInfo.trainVersionCode)));
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 3940aa8e..b68eeb8 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -328,4 +328,3 @@
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
-
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 536700f..6f54ea7 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -31,7 +31,7 @@
 #include "state/StateManager.h"
 #include "stats_log_util.h"
 #include "stats_util.h"
-#include "statslog.h"
+#include "statslog_statsd.h"
 
 using android::util::FIELD_COUNT_REPEATED;
 using android::util::FIELD_TYPE_INT32;
@@ -291,7 +291,7 @@
 }
 
 bool MetricsManager::eventSanityCheck(const LogEvent& event) {
-    if (event.GetTagId() == android::util::APP_BREADCRUMB_REPORTED) {
+    if (event.GetTagId() == util::APP_BREADCRUMB_REPORTED) {
         // Check that app breadcrumb reported fields are valid.
         status_t err = NO_ERROR;
 
@@ -318,7 +318,7 @@
             VLOG("APP_BREADCRUMB_REPORTED does not have valid state %ld", appHookState);
             return false;
         }
-    } else if (event.GetTagId() == android::util::DAVEY_OCCURRED) {
+    } else if (event.GetTagId() == util::DAVEY_OCCURRED) {
         // Daveys can be logged from any app since they are logged in libs/hwui/JankTracker.cpp.
         // Check that the davey duration is reasonable. Max length check is for privacy.
         status_t err = NO_ERROR;
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index 4e3c506..02fe7b1 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -139,7 +139,7 @@
     // record is deleted.
     void appendUidMap(const int64_t& timestamp, const ConfigKey& key, std::set<string>* str_set,
                       bool includeVersionStrings, bool includeInstaller,
-                      util::ProtoOutputStream* proto);
+                      ProtoOutputStream* proto);
 
     // Forces the output to be cleared. We still generate a snapshot based on the current state.
     // This results in extra data uploaded but helps us reconstruct the uid mapping on the server
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
index 4b78e30..7febb35 100644
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -137,7 +137,9 @@
                                             simplePredicate, trackerNameIndexMap);
     EXPECT_FALSE(conditionTracker.isSliced());
 
-    LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+    // This event is not accessed in this test besides dimensions which is why this is okay.
+    // This is technically an invalid LogEvent because we do not call parseBuffer.
+    LogEvent event(/*uid=*/0, /*pid=*/0);
 
     vector<MatchingState> matcherState;
     matcherState.push_back(MatchingState::kNotMatched);
@@ -222,7 +224,9 @@
                                             trackerNameIndexMap);
     EXPECT_FALSE(conditionTracker.isSliced());
 
-    LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+    // This event is not accessed in this test besides dimensions which is why this is okay.
+    // This is technically an invalid LogEvent because we do not call parseBuffer.
+    LogEvent event(/*uid=*/0, /*pid=*/0);
 
     // one matched start
     vector<MatchingState> matcherState;
@@ -296,8 +300,8 @@
 
         std::vector<int> uids = {111, 222, 333};
 
-        LogEvent event(/*uid=*/-1, /*pid=*/-1);
-        makeWakeLockEvent(&event, /*atomId=*/1, /*timestamp=*/0, uids, "wl1", /*acquire=*/1);
+        LogEvent event1(/*uid=*/0, /*pid=*/0);
+        makeWakeLockEvent(&event1, /*atomId=*/1, /*timestamp=*/0, uids, "wl1", /*acquire=*/1);
 
         // one matched start
         vector<MatchingState> matcherState;
@@ -308,7 +312,7 @@
         vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
         vector<bool> changedCache(1, false);
 
-        conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
+        conditionTracker.evaluateCondition(event1, matcherState, allPredicates, conditionCache,
                                            changedCache);
 
         if (position == Position::FIRST || position == Position::LAST) {
@@ -333,7 +337,7 @@
         EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
 
         // another wake lock acquired by this uid
-        LogEvent event2(/*uid=*/-1, /*pid=*/-1);
+        LogEvent event2(/*uid=*/0, /*pid=*/0);
         makeWakeLockEvent(&event2, /*atomId=*/1, /*timestamp=*/0, uids, "wl2", /*acquire=*/1);
         matcherState.clear();
         matcherState.push_back(MatchingState::kMatched);
@@ -353,7 +357,7 @@
 
 
         // wake lock 1 release
-        LogEvent event3(/*uid=*/-1, /*pid=*/-1);
+        LogEvent event3(/*uid=*/0, /*pid=*/0);
         makeWakeLockEvent(&event3, /*atomId=*/1, /*timestamp=*/0, uids, "wl1", /*acquire=*/0);
         matcherState.clear();
         matcherState.push_back(MatchingState::kNotMatched);
@@ -372,7 +376,7 @@
         EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
         EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
 
-        LogEvent event4(/*uid=*/-1, /*pid=*/-1);
+        LogEvent event4(/*uid=*/0, /*pid=*/0);
         makeWakeLockEvent(&event4, /*atomId=*/1, /*timestamp=*/0, uids, "wl2", /*acquire=*/0);
         matcherState.clear();
         matcherState.push_back(MatchingState::kNotMatched);
@@ -399,246 +403,235 @@
 
 }
 
-//TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
-//    std::vector<sp<ConditionTracker>> allConditions;
-//
-//    SimplePredicate simplePredicate = getWakeLockHeldCondition(
-//            true /*nesting*/, true /*default to false*/, false /*slice output by uid*/,
-//            Position::ANY /* position */);
-//    string conditionName = "WL_HELD";
-//
-//    unordered_map<int64_t, int> trackerNameIndexMap;
-//    trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0;
-//    trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
-//    trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
-//
-//    SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
-//                                            0 /*condition tracker index*/, simplePredicate,
-//                                            trackerNameIndexMap);
-//
-//    EXPECT_FALSE(conditionTracker.isSliced());
-//
-//    std::vector<int> uid_list1 = {111, 1111, 11111};
-//    string uid1_wl1 = "wl1_1";
-//    std::vector<int> uid_list2 = {222, 2222, 22222};
-//    string uid2_wl1 = "wl2_1";
-//
-//    LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
-//    makeWakeLockEvent(&event, uid_list1, uid1_wl1, 1);
-//
-//    // one matched start for uid1
-//    vector<MatchingState> matcherState;
-//    matcherState.push_back(MatchingState::kMatched);
-//    matcherState.push_back(MatchingState::kNotMatched);
-//    matcherState.push_back(MatchingState::kNotMatched);
-//    vector<sp<ConditionTracker>> allPredicates;
-//    vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
-//    vector<bool> changedCache(1, false);
-//
-//    conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
-//                                       changedCache);
-//
-//    EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
-//    EXPECT_TRUE(changedCache[0]);
-//
-//    // Now test query
-//    ConditionKey queryKey;
-//    conditionCache[0] = ConditionState::kNotEvaluated;
-//
-//    conditionTracker.isConditionMet(queryKey, allPredicates,
-//                                    true,
-//                                    conditionCache);
-//    EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
-//
-//    // another wake lock acquired by this uid
-//    LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
-//    makeWakeLockEvent(&event2, uid_list2, uid2_wl1, 1);
-//    matcherState.clear();
-//    matcherState.push_back(MatchingState::kMatched);
-//    matcherState.push_back(MatchingState::kNotMatched);
-//    conditionCache[0] = ConditionState::kNotEvaluated;
-//    changedCache[0] = false;
-//    conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
-//                                       changedCache);
-//    EXPECT_FALSE(changedCache[0]);
-//
-//    // uid1 wake lock 1 release
-//    LogEvent event3(1 /*tagId*/, 0 /*timestamp*/);
-//    makeWakeLockEvent(&event3, uid_list1, uid1_wl1, 0);  // now release it.
-//    matcherState.clear();
-//    matcherState.push_back(MatchingState::kNotMatched);
-//    matcherState.push_back(MatchingState::kMatched);
-//    conditionCache[0] = ConditionState::kNotEvaluated;
-//    changedCache[0] = false;
-//    conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
-//                                       changedCache);
-//    // nothing changes, because uid2 is still holding wl.
-//    EXPECT_FALSE(changedCache[0]);
-//
-//    LogEvent event4(1 /*tagId*/, 0 /*timestamp*/);
-//    makeWakeLockEvent(&event4, uid_list2, uid2_wl1, 0);  // now release it.
-//    matcherState.clear();
-//    matcherState.push_back(MatchingState::kNotMatched);
-//    matcherState.push_back(MatchingState::kMatched);
-//    conditionCache[0] = ConditionState::kNotEvaluated;
-//    changedCache[0] = false;
-//    conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache,
-//                                       changedCache);
-//    EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
-//    EXPECT_TRUE(changedCache[0]);
-//
-//    // query again
-//    conditionCache[0] = ConditionState::kNotEvaluated;
-//    conditionTracker.isConditionMet(queryKey, allPredicates,
-//                                    true,
-//                                    conditionCache);
-//    EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
-//}
-//
-//TEST(SimpleConditionTrackerTest, TestStopAll) {
-//    std::vector<sp<ConditionTracker>> allConditions;
-//    for (Position position :
-//            { Position::FIRST, Position::LAST }) {
-//        SimplePredicate simplePredicate = getWakeLockHeldCondition(
-//                true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
-//                position);
-//        string conditionName = "WL_HELD_BY_UID3";
-//
-//        unordered_map<int64_t, int> trackerNameIndexMap;
-//        trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0;
-//        trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
-//        trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
-//
-//        SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
-//                                                0 /*condition tracker index*/, simplePredicate,
-//                                                trackerNameIndexMap);
-//
-//        std::vector<int> uid_list1 = {111, 1111, 11111};
-//        std::vector<int> uid_list2 = {222, 2222, 22222};
-//
-//        LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
-//        makeWakeLockEvent(&event, uid_list1, "wl1", 1);
-//
-//        // one matched start
-//        vector<MatchingState> matcherState;
-//        matcherState.push_back(MatchingState::kMatched);
-//        matcherState.push_back(MatchingState::kNotMatched);
-//        matcherState.push_back(MatchingState::kNotMatched);
-//        vector<sp<ConditionTracker>> allPredicates;
-//        vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
-//        vector<bool> changedCache(1, false);
-//
-//        conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
-//                                           changedCache);
-//        if (position == Position::FIRST ||
-//            position == Position::LAST) {
-//            EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
-//        } else {
-//            EXPECT_EQ(uid_list1.size(), conditionTracker.mSlicedConditionState.size());
-//        }
-//        EXPECT_TRUE(changedCache[0]);
-//        {
-//            if (position == Position::FIRST ||
-//                position == Position::LAST) {
-//                EXPECT_EQ(1UL, conditionTracker.getChangedToTrueDimensions(allConditions)->size());
-//                EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
-//            } else {
-//                EXPECT_EQ(uid_list1.size(), conditionTracker.getChangedToTrueDimensions(allConditions)->size());
-//                EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
-//            }
-//        }
-//
-//        // Now test query
-//        const auto queryKey = getWakeLockQueryKey(position, uid_list1, conditionName);
-//        conditionCache[0] = ConditionState::kNotEvaluated;
-//
-//        conditionTracker.isConditionMet(queryKey, allPredicates,
-//                                        false,
-//                                        conditionCache);
-//        EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
-//
-//        // another wake lock acquired by uid2
-//        LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
-//        makeWakeLockEvent(&event2, uid_list2, "wl2", 1);
-//        matcherState.clear();
-//        matcherState.push_back(MatchingState::kMatched);
-//        matcherState.push_back(MatchingState::kNotMatched);
-//        matcherState.push_back(MatchingState::kNotMatched);
-//        conditionCache[0] = ConditionState::kNotEvaluated;
-//        changedCache[0] = false;
-//        conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
-//                                           changedCache);
-//        if (position == Position::FIRST ||
-//            position == Position::LAST) {
-//            EXPECT_EQ(2UL, conditionTracker.mSlicedConditionState.size());
-//        } else {
-//            EXPECT_EQ(uid_list1.size() + uid_list2.size(),
-//                      conditionTracker.mSlicedConditionState.size());
-//        }
-//        EXPECT_TRUE(changedCache[0]);
-//        {
-//            if (position == Position::FIRST ||
-//                position == Position::LAST) {
-//                EXPECT_EQ(1UL, conditionTracker.getChangedToTrueDimensions(allConditions)->size());
-//                EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
-//            } else {
-//                EXPECT_EQ(uid_list2.size(), conditionTracker.getChangedToTrueDimensions(allConditions)->size());
-//                EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
-//            }
-//        }
-//
-//
-//        // TEST QUERY
-//        const auto queryKey2 = getWakeLockQueryKey(position, uid_list2, conditionName);
-//        conditionCache[0] = ConditionState::kNotEvaluated;
-//        conditionTracker.isConditionMet(queryKey, allPredicates,
-//                                        false,
-//                                        conditionCache);
-//
-//        EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
-//
-//
-//        // stop all event
-//        LogEvent event3(2 /*tagId*/, 0 /*timestamp*/);
-//        matcherState.clear();
-//        matcherState.push_back(MatchingState::kNotMatched);
-//        matcherState.push_back(MatchingState::kNotMatched);
-//        matcherState.push_back(MatchingState::kMatched);
-//
-//        conditionCache[0] = ConditionState::kNotEvaluated;
-//        changedCache[0] = false;
-//        conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
-//                                           changedCache);
-//        EXPECT_TRUE(changedCache[0]);
-//        EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
-//        {
-//            if (position == Position::FIRST || position == Position::LAST) {
-//                EXPECT_EQ(2UL, conditionTracker.getChangedToFalseDimensions(allConditions)->size());
-//                EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
-//            } else {
-//                EXPECT_EQ(uid_list1.size() + uid_list2.size(),
-//                          conditionTracker.getChangedToFalseDimensions(allConditions)->size());
-//                EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
-//            }
-//        }
-//
-//        // TEST QUERY
-//        const auto queryKey3 = getWakeLockQueryKey(position, uid_list1, conditionName);
-//        conditionCache[0] = ConditionState::kNotEvaluated;
-//        conditionTracker.isConditionMet(queryKey, allPredicates,
-//                                        false,
-//                                        conditionCache);
-//        EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
-//
-//        // TEST QUERY
-//        const auto queryKey4 = getWakeLockQueryKey(position, uid_list2, conditionName);
-//        conditionCache[0] = ConditionState::kNotEvaluated;
-//        conditionTracker.isConditionMet(queryKey, allPredicates,
-//                                        false,
-//                                        conditionCache);
-//        EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
-//    }
-//}
+TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
+    std::vector<sp<ConditionTracker>> allConditions;
+
+    SimplePredicate simplePredicate =
+            getWakeLockHeldCondition(true /*nesting*/, true /*default to false*/,
+                                     false /*slice output by uid*/, Position::ANY /* position */);
+    string conditionName = "WL_HELD";
+
+    unordered_map<int64_t, int> trackerNameIndexMap;
+    trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0;
+    trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
+    trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
+
+    SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
+                                            0 /*condition tracker index*/, simplePredicate,
+                                            trackerNameIndexMap);
+
+    EXPECT_FALSE(conditionTracker.isSliced());
+
+    std::vector<int> uids1 = {111, 1111, 11111};
+    string uid1_wl1 = "wl1_1";
+    std::vector<int> uids2 = {222, 2222, 22222};
+    string uid2_wl1 = "wl2_1";
+
+    LogEvent event1(/*uid=*/0, /*pid=*/0);
+    makeWakeLockEvent(&event1, /*atomId=*/1, /*timestamp=*/0, uids1, uid1_wl1, /*acquire=*/1);
+
+    // one matched start for uid1
+    vector<MatchingState> matcherState;
+    matcherState.push_back(MatchingState::kMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    vector<sp<ConditionTracker>> allPredicates;
+    vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+    vector<bool> changedCache(1, false);
+
+    conditionTracker.evaluateCondition(event1, matcherState, allPredicates, conditionCache,
+                                       changedCache);
+
+    EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+    EXPECT_TRUE(changedCache[0]);
+
+    // Now test query
+    ConditionKey queryKey;
+    conditionCache[0] = ConditionState::kNotEvaluated;
+
+    conditionTracker.isConditionMet(queryKey, allPredicates, true, conditionCache);
+    EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+
+    // another wake lock acquired by this uid
+    LogEvent event2(/*uid=*/0, /*pid=*/0);
+    makeWakeLockEvent(&event2, /*atomId=*/1, /*timestamp=*/0, uids2, uid2_wl1, /*acquire=*/1);
+
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+    conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
+                                       changedCache);
+    EXPECT_FALSE(changedCache[0]);
+
+    // uid1 wake lock 1 release
+    LogEvent event3(/*uid=*/0, /*pid=*/0);
+    makeWakeLockEvent(&event3, /*atomId=*/1, /*timestamp=*/0, uids1, uid1_wl1,
+                      /*release=*/0);  // now release it.
+
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+    conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
+                                       changedCache);
+    // nothing changes, because uid2 is still holding wl.
+    EXPECT_FALSE(changedCache[0]);
+
+    LogEvent event4(/*uid=*/0, /*pid=*/0);
+    makeWakeLockEvent(&event4, /*atomId=*/1, /*timestamp=*/0, uids2, uid2_wl1,
+                      /*acquire=*/0);  // now release it.
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+    conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache,
+                                       changedCache);
+    EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
+    EXPECT_TRUE(changedCache[0]);
+
+    // query again
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    conditionTracker.isConditionMet(queryKey, allPredicates, true, conditionCache);
+    EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+}
+
+TEST(SimpleConditionTrackerTest, TestStopAll) {
+    std::vector<sp<ConditionTracker>> allConditions;
+    for (Position position : {Position::FIRST, Position::LAST}) {
+        SimplePredicate simplePredicate =
+                getWakeLockHeldCondition(true /*nesting*/, true /*default to false*/,
+                                         true /*output slice by uid*/, position);
+        string conditionName = "WL_HELD_BY_UID3";
+
+        unordered_map<int64_t, int> trackerNameIndexMap;
+        trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0;
+        trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
+        trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
+
+        SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
+                                                0 /*condition tracker index*/, simplePredicate,
+                                                trackerNameIndexMap);
+
+        std::vector<int> uids1 = {111, 1111, 11111};
+        std::vector<int> uids2 = {222, 2222, 22222};
+
+        LogEvent event1(/*uid=*/0, /*pid=*/0);
+        makeWakeLockEvent(&event1, /*atomId=*/1, /*timestamp=*/0, uids1, "wl1", /*acquire=*/1);
+
+        // one matched start
+        vector<MatchingState> matcherState;
+        matcherState.push_back(MatchingState::kMatched);
+        matcherState.push_back(MatchingState::kNotMatched);
+        matcherState.push_back(MatchingState::kNotMatched);
+        vector<sp<ConditionTracker>> allPredicates;
+        vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+        vector<bool> changedCache(1, false);
+
+        conditionTracker.evaluateCondition(event1, matcherState, allPredicates, conditionCache,
+                                           changedCache);
+        if (position == Position::FIRST || position == Position::LAST) {
+            EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+        } else {
+            EXPECT_EQ(uids1.size(), conditionTracker.mSlicedConditionState.size());
+        }
+        EXPECT_TRUE(changedCache[0]);
+        {
+            if (position == Position::FIRST || position == Position::LAST) {
+                EXPECT_EQ(1UL, conditionTracker.getChangedToTrueDimensions(allConditions)->size());
+                EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
+            } else {
+                EXPECT_EQ(uids1.size(),
+                          conditionTracker.getChangedToTrueDimensions(allConditions)->size());
+                EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
+            }
+        }
+
+        // Now test query
+        const auto queryKey = getWakeLockQueryKey(position, uids1, conditionName);
+        conditionCache[0] = ConditionState::kNotEvaluated;
+
+        conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache);
+        EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+
+        // another wake lock acquired by uid2
+        LogEvent event2(/*uid=*/0, /*pid=*/0);
+        makeWakeLockEvent(&event2, /*atomId=*/1, /*timestamp=*/0, uids2, "wl2", /*acquire=*/1);
+
+        matcherState.clear();
+        matcherState.push_back(MatchingState::kMatched);
+        matcherState.push_back(MatchingState::kNotMatched);
+        matcherState.push_back(MatchingState::kNotMatched);
+        conditionCache[0] = ConditionState::kNotEvaluated;
+        changedCache[0] = false;
+        conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
+                                           changedCache);
+        if (position == Position::FIRST || position == Position::LAST) {
+            EXPECT_EQ(2UL, conditionTracker.mSlicedConditionState.size());
+        } else {
+            EXPECT_EQ(uids1.size() + uids2.size(), conditionTracker.mSlicedConditionState.size());
+        }
+        EXPECT_TRUE(changedCache[0]);
+        {
+            if (position == Position::FIRST || position == Position::LAST) {
+                EXPECT_EQ(1UL, conditionTracker.getChangedToTrueDimensions(allConditions)->size());
+                EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
+            } else {
+                EXPECT_EQ(uids2.size(),
+                          conditionTracker.getChangedToTrueDimensions(allConditions)->size());
+                EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
+            }
+        }
+
+        // TEST QUERY
+        const auto queryKey2 = getWakeLockQueryKey(position, uids2, conditionName);
+        conditionCache[0] = ConditionState::kNotEvaluated;
+        conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache);
+
+        EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+
+        // stop all event
+        LogEvent event3(/*uid=*/0, /*pid=*/0);
+        makeWakeLockEvent(&event3, /*atomId=*/1, /*timestamp=*/0, uids2, "wl2", /*acquire=*/1);
+
+        matcherState.clear();
+        matcherState.push_back(MatchingState::kNotMatched);
+        matcherState.push_back(MatchingState::kNotMatched);
+        matcherState.push_back(MatchingState::kMatched);
+
+        conditionCache[0] = ConditionState::kNotEvaluated;
+        changedCache[0] = false;
+        conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
+                                           changedCache);
+        EXPECT_TRUE(changedCache[0]);
+        EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
+        {
+            if (position == Position::FIRST || position == Position::LAST) {
+                EXPECT_EQ(2UL, conditionTracker.getChangedToFalseDimensions(allConditions)->size());
+                EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
+            } else {
+                EXPECT_EQ(uids1.size() + uids2.size(),
+                          conditionTracker.getChangedToFalseDimensions(allConditions)->size());
+                EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
+            }
+        }
+
+        // TEST QUERY
+        const auto queryKey3 = getWakeLockQueryKey(position, uids1, conditionName);
+        conditionCache[0] = ConditionState::kNotEvaluated;
+        conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache);
+        EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+
+        // TEST QUERY
+        const auto queryKey4 = getWakeLockQueryKey(position, uids2, conditionName);
+        conditionCache[0] = ConditionState::kNotEvaluated;
+        conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache);
+        EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+    }
+}
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp b/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp
index 1eaaf08..e0eebef 100644
--- a/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp
@@ -53,185 +53,192 @@
 
 }  // namespace
 
-// TODO(b/149590301): Update these tests to use new socket schema.
-//TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket) {
-//    const int num_buckets = 1;
-//    const int threshold = 3;
-//    auto config = CreateStatsdConfig(num_buckets, threshold);
-//    const uint64_t alert_id = config.alert(0).id();
-//    const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
-//
-//    int64_t bucketStartTimeNs = 10000000000;
-//    int64_t bucketSizeNs =
-//        TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
-//
-//    ConfigKey cfgKey;
-//    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-//    EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
-//
-//    sp<AnomalyTracker> anomalyTracker =
-//        processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
-//
-//    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
-//    std::vector<AttributionNodeInternal> attributions2 = {
-//        CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1")};
-//    std::vector<AttributionNodeInternal> attributions3 = {
-//        CreateAttribution(111, "App1"), CreateAttribution(333, "App3")};
-//    std::vector<AttributionNodeInternal> attributions4 = {
-//        CreateAttribution(222, "GMSCoreModule1"), CreateAttribution(333, "App3")};
-//    std::vector<AttributionNodeInternal> attributions5 = {
-//        CreateAttribution(222, "GMSCoreModule1") };
-//
-//    FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
-//                           Value((int32_t)111));
-//    HashableDimensionKey whatKey1({fieldValue1});
-//    MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
-//
-//    FieldValue fieldValue2(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
-//                           Value((int32_t)222));
-//    HashableDimensionKey whatKey2({fieldValue2});
-//    MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY);
-//
-//    auto event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 2);
-//    processor->OnLogEvent(event.get());
-//    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-//
-//    event = CreateAcquireWakelockEvent(attributions4, "wl2", bucketStartTimeNs + 2);
-//    processor->OnLogEvent(event.get());
-//    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
-//
-//    event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + 3);
-//    processor->OnLogEvent(event.get());
-//    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-//
-//    event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + 3);
-//    processor->OnLogEvent(event.get());
-//    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
-//
-//    event = CreateAcquireWakelockEvent(attributions3, "wl1", bucketStartTimeNs + 4);
-//    processor->OnLogEvent(event.get());
-//    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-//
-//    event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + 4);
-//    processor->OnLogEvent(event.get());
-//    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
-//
-//    // Fired alarm and refractory period end timestamp updated.
-//    event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 5);
-//    processor->OnLogEvent(event.get());
-//    EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1,
-//              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-//
-//    event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 100);
-//    processor->OnLogEvent(event.get());
-//    EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1,
-//              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-//
-//    event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - 1);
-//    processor->OnLogEvent(event.get());
-//    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1,
-//              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-//
-//    event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1);
-//    processor->OnLogEvent(event.get());
-//    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1,
-//              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-//
-//    event = CreateAcquireWakelockEvent(attributions4, "wl2", bucketStartTimeNs + bucketSizeNs + 1);
-//    processor->OnLogEvent(event.get());
-//    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
-//
-//    event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + bucketSizeNs + 2);
-//    processor->OnLogEvent(event.get());
-//    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
-//
-//    event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + bucketSizeNs + 3);
-//    processor->OnLogEvent(event.get());
-//    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
-//
-//    event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + bucketSizeNs + 4);
-//    processor->OnLogEvent(event.get());
-//    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 4) / NS_PER_SEC + 1,
-//              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
-//}
-//
-//TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets) {
-//    const int num_buckets = 3;
-//    const int threshold = 3;
-//    auto config = CreateStatsdConfig(num_buckets, threshold);
-//    const uint64_t alert_id = config.alert(0).id();
-//    const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
-//
-//    int64_t bucketStartTimeNs = 10000000000;
-//    int64_t bucketSizeNs =
-//        TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
-//
-//    ConfigKey cfgKey;
-//    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-//    EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
-//
-//    sp<AnomalyTracker> anomalyTracker =
-//        processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
-//
-//    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
-//    std::vector<AttributionNodeInternal> attributions2 = {
-//        CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1")};
-//    std::vector<AttributionNodeInternal> attributions3 = {
-//        CreateAttribution(111, "App1"), CreateAttribution(333, "App3")};
-//    std::vector<AttributionNodeInternal> attributions4 = {
-//        CreateAttribution(222, "GMSCoreModule1"), CreateAttribution(333, "App3")};
-//    std::vector<AttributionNodeInternal> attributions5 = {
-//        CreateAttribution(222, "GMSCoreModule1") };
-//
-//    FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
-//                           Value((int32_t)111));
-//    HashableDimensionKey whatKey1({fieldValue1});
-//    MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
-//
-//    FieldValue fieldValue2(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
-//                           Value((int32_t)222));
-//    HashableDimensionKey whatKey2({fieldValue2});
-//    MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY);
-//
-//    auto event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 2);
-//    processor->OnLogEvent(event.get());
-//    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-//
-//    event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + 3);
-//    processor->OnLogEvent(event.get());
-//    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-//
-//    // Fired alarm and refractory period end timestamp updated.
-//    event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 4);
-//    processor->OnLogEvent(event.get());
-//    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-//
-//    event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1);
-//    processor->OnLogEvent(event.get());
-//    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
-//              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-//
-//    event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + bucketSizeNs + 2);
-//    processor->OnLogEvent(event.get());
-//    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
-//              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-//
-//    event = CreateAcquireWakelockEvent(
-//        attributions2, "wl1", bucketStartTimeNs + 3 * bucketSizeNs + 1);
-//    processor->OnLogEvent(event.get());
-//    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
-//              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-//
-//    event = CreateAcquireWakelockEvent(
-//        attributions2, "wl1", bucketStartTimeNs + 3 * bucketSizeNs + 2);
-//    processor->OnLogEvent(event.get());
-//    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 3 * bucketSizeNs + 2) / NS_PER_SEC + 1,
-//              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-//}
+TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket) {
+    const int num_buckets = 1;
+    const int threshold = 3;
+    auto config = CreateStatsdConfig(num_buckets, threshold);
+    const uint64_t alert_id = config.alert(0).id();
+    const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
+
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
+
+    sp<AnomalyTracker> anomalyTracker =
+            processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
+
+    std::vector<int> attributionUids1 = {111};
+    std::vector<string> attributionTags1 = {"App1"};
+    std::vector<int> attributionUids2 = {111, 222};
+    std::vector<string> attributionTags2 = {"App1", "GMSCoreModule1"};
+    std::vector<int> attributionUids3 = {111, 333};
+    std::vector<string> attributionTags3 = {"App1", "App3"};
+    std::vector<int> attributionUids4 = {222, 333};
+    std::vector<string> attributionTags4 = {"GMSCoreModule1", "App3"};
+    std::vector<int> attributionUids5 = {222};
+    std::vector<string> attributionTags5 = {"GMSCoreModule1"};
+
+    FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
+                           Value((int32_t)111));
+    HashableDimensionKey whatKey1({fieldValue1});
+    MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
+
+    FieldValue fieldValue2(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
+                           Value((int32_t)222));
+    HashableDimensionKey whatKey2({fieldValue2});
+    MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY);
+
+    auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
+                                            attributionTags1, "wl1");
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids4, attributionTags4,
+                                       "wl2");
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+
+    event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3, attributionUids2, attributionTags2,
+                                       "wl1");
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3, attributionUids5, attributionTags5,
+                                       "wl2");
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+
+    event = CreateAcquireWakelockEvent(bucketStartTimeNs + 4, attributionUids3, attributionTags3,
+                                       "wl1");
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    event = CreateAcquireWakelockEvent(bucketStartTimeNs + 4, attributionUids5, attributionTags5,
+                                       "wl2");
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+
+    // Fired alarm and refractory period end timestamp updated.
+    event = CreateAcquireWakelockEvent(bucketStartTimeNs + 5, attributionUids1, attributionTags1,
+                                       "wl1");
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    event = CreateAcquireWakelockEvent(bucketStartTimeNs + 100, attributionUids1, attributionTags1,
+                                       "wl1");
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids1,
+                                       attributionTags1, "wl1");
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, attributionUids1,
+                                       attributionTags1, "wl1");
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, attributionUids4,
+                                       attributionTags4, "wl2");
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+
+    event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2, attributionUids5,
+                                       attributionTags5, "wl2");
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+
+    event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 3, attributionUids5,
+                                       attributionTags5, "wl2");
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+
+    event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 4, attributionUids5,
+                                       attributionTags5, "wl2");
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 4) / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+}
+
+TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets) {
+    const int num_buckets = 3;
+    const int threshold = 3;
+    auto config = CreateStatsdConfig(num_buckets, threshold);
+    const uint64_t alert_id = config.alert(0).id();
+    const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
+
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
+
+    sp<AnomalyTracker> anomalyTracker =
+            processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
+
+    std::vector<int> attributionUids1 = {111};
+    std::vector<string> attributionTags1 = {"App1"};
+    std::vector<int> attributionUids2 = {111, 222};
+    std::vector<string> attributionTags2 = {"App1", "GMSCoreModule1"};
+
+    FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
+                           Value((int32_t)111));
+    HashableDimensionKey whatKey1({fieldValue1});
+    MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
+
+    auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
+                                            attributionTags1, "wl1");
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3, attributionUids2, attributionTags2,
+                                       "wl1");
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    // Fired alarm and refractory period end timestamp updated.
+    event = CreateAcquireWakelockEvent(bucketStartTimeNs + 4, attributionUids1, attributionTags1,
+                                       "wl1");
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, attributionUids1,
+                                       attributionTags1, "wl1");
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2, attributionUids2,
+                                       attributionTags2, "wl1");
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 1, attributionUids2,
+                                       attributionTags2, "wl1");
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 2, attributionUids2,
+                                       attributionTags2, "wl1");
+    processor->OnLogEvent(event.get());
+    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 3 * bucketSizeNs + 2) / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+}
 
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
diff --git a/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp b/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp
index 03a209a..fe45b55 100644
--- a/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp
@@ -69,414 +69,432 @@
     return config;
 }
 
-}  // namespace
+std::vector<int> attributionUids1 = {111, 222};
+std::vector<string> attributionTags1 = {"App1", "GMSCoreModule1"};
 
-std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
-                                                      CreateAttribution(222, "GMSCoreModule1")};
+std::vector<int> attributionUids2 = {111, 222};
+std::vector<string> attributionTags2 = {"App2", "GMSCoreModule1"};
 
-std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(111, "App2"),
-                                                      CreateAttribution(222, "GMSCoreModule1")};
+std::vector<int> attributionUids3 = {222};
+std::vector<string> attributionTags3 = {"GMSCoreModule1"};
 
-std::vector<AttributionNodeInternal> attributions3 = {CreateAttribution(222, "GMSCoreModule1")};
-
-MetricDimensionKey dimensionKey(
-    HashableDimensionKey({FieldValue(Field(android::util::WAKELOCK_STATE_CHANGED,
-                                           (int32_t)0x02010101), Value((int32_t)111))}),
-    DEFAULT_DIMENSION_KEY);
+MetricDimensionKey dimensionKey1(
+        HashableDimensionKey({FieldValue(Field(android::util::WAKELOCK_STATE_CHANGED,
+                                               (int32_t)0x02010101),
+                                         Value((int32_t)111))}),
+        DEFAULT_DIMENSION_KEY);
 
 MetricDimensionKey dimensionKey2(
     HashableDimensionKey({FieldValue(Field(android::util::WAKELOCK_STATE_CHANGED,
                                            (int32_t)0x02010101), Value((int32_t)222))}),
     DEFAULT_DIMENSION_KEY);
 
-// TODO(b/149590301): Update these tests to use new socket schema.
-//TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket) {
-//    const int num_buckets = 1;
-//    const uint64_t threshold_ns = NS_PER_SEC;
-//    auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true);
-//    const uint64_t alert_id = config.alert(0).id();
-//    const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
-//
-//    int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
-//    int64_t bucketSizeNs =
-//        TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
-//
-//    ConfigKey cfgKey;
-//    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-//    EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
-//
-//    sp<AnomalyTracker> anomalyTracker =
-//        processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
-//
-//    auto screen_on_event = CreateScreenStateChangedEvent(
-//            android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 1);
-//    auto screen_off_event = CreateScreenStateChangedEvent(
-//            android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 10);
-//    processor->OnLogEvent(screen_on_event.get());
-//    processor->OnLogEvent(screen_off_event.get());
-//
-//    // Acquire wakelock wl1.
-//    auto acquire_event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 11);
-//    processor->OnLogEvent(acquire_event.get());
-//    EXPECT_EQ((bucketStartTimeNs + 11 + threshold_ns) / NS_PER_SEC + 1,
-//              anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
-//
-//    // Release wakelock wl1. No anomaly detected. Alarm cancelled at the "release" event.
-//    auto release_event = CreateReleaseWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 101);
-//    processor->OnLogEvent(release_event.get());
-//    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
-//
-//    // Acquire wakelock wl1 within bucket #0.
-//    acquire_event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + 110);
-//    processor->OnLogEvent(acquire_event.get());
-//    EXPECT_EQ((bucketStartTimeNs + 110 + threshold_ns - 90) / NS_PER_SEC + 1,
-//              anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
-//
-//    // Release wakelock wl1. One anomaly detected.
-//    release_event = CreateReleaseWakelockEvent(
-//            attributions2, "wl1", bucketStartTimeNs + NS_PER_SEC + 109);
-//    processor->OnLogEvent(release_event.get());
-//    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1,
-//              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
-//
-//    // Acquire wakelock wl1.
-//    acquire_event = CreateAcquireWakelockEvent(
-//        attributions1, "wl1", bucketStartTimeNs + NS_PER_SEC + 112);
-//    processor->OnLogEvent(acquire_event.get());
-//    // Wakelock has been hold longer than the threshold in bucket #0. The alarm is set at the
-//    // end of the refractory period.
-//    const int64_t alarmFiredTimestampSec0 = anomalyTracker->getAlarmTimestampSec(dimensionKey);
-//    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1,
-//              (uint32_t)alarmFiredTimestampSec0);
-//
-//    // Anomaly alarm fired.
-//    auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan(
-//            static_cast<uint32_t>(alarmFiredTimestampSec0));
-//    EXPECT_EQ(1u, alarmSet.size());
-//    processor->onAnomalyAlarmFired(alarmFiredTimestampSec0 * NS_PER_SEC, alarmSet);
-//    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//    EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0,
-//              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
-//
-//    // Release wakelock wl1.
-//    release_event = CreateReleaseWakelockEvent(
-//            attributions1, "wl1", alarmFiredTimestampSec0 * NS_PER_SEC + NS_PER_SEC + 1);
-//    processor->OnLogEvent(release_event.get());
-//    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//    // Within refractory period. No more anomaly detected.
-//    EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0,
-//              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
-//
-//    // Acquire wakelock wl1.
-//    acquire_event = CreateAcquireWakelockEvent(
-//        attributions2, "wl1", bucketStartTimeNs + bucketSizeNs -  5 * NS_PER_SEC - 11);
-//    processor->OnLogEvent(acquire_event.get());
-//    const int64_t alarmFiredTimestampSec1 = anomalyTracker->getAlarmTimestampSec(dimensionKey);
-//    EXPECT_EQ((bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC) / NS_PER_SEC,
-//              (uint64_t)alarmFiredTimestampSec1);
-//
-//    // Release wakelock wl1.
-//    release_event = CreateReleaseWakelockEvent(
-//        attributions2, "wl1", bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10);
-//    processor->OnLogEvent(release_event.get());
-//    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//    EXPECT_EQ(refractory_period_sec +
-//                    (bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10) / NS_PER_SEC + 1,
-//              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
-//
-//    alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan(
-//                static_cast<uint32_t>(alarmFiredTimestampSec1));
-//    EXPECT_EQ(0u, alarmSet.size());
-//
-//    // Acquire wakelock wl1 near the end of bucket #0.
-//    acquire_event = CreateAcquireWakelockEvent(
-//            attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - 2);
-//    processor->OnLogEvent(acquire_event.get());
-//    EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC,
-//               anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//
-//    // Release the event at early bucket #1.
-//    release_event = CreateReleaseWakelockEvent(
-//            attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + NS_PER_SEC - 1);
-//    processor->OnLogEvent(release_event.get());
-//    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//    // Anomaly detected when stopping the alarm. The refractory period does not change.
-//    EXPECT_EQ(refractory_period_sec +
-//                    (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
-//              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
-//
-//    // Condition changes to false.
-//    screen_on_event = CreateScreenStateChangedEvent(
-//        android::view::DisplayStateEnum::DISPLAY_STATE_ON,
-//        bucketStartTimeNs + 2 * bucketSizeNs + 20);
-//    processor->OnLogEvent(screen_on_event.get());
-//    EXPECT_EQ(refractory_period_sec +
-//                    (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
-//              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
-//    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//
-//    acquire_event = CreateAcquireWakelockEvent(
-//        attributions2, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 30);
-//    processor->OnLogEvent(acquire_event.get());
-//    // The condition is false. Do not start the alarm.
-//    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//    EXPECT_EQ(refractory_period_sec +
-//                    (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
-//              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
-//
-//    // Condition turns true.
-//    screen_off_event = CreateScreenStateChangedEvent(
-//        android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
-//        bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC);
-//    processor->OnLogEvent(screen_off_event.get());
-//    EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC + threshold_ns) / NS_PER_SEC,
-//              anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//
-//    // Condition turns to false.
-//    screen_on_event = CreateScreenStateChangedEvent(
-//        android::view::DisplayStateEnum::DISPLAY_STATE_ON,
-//        bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1);
-//    processor->OnLogEvent(screen_on_event.get());
-//    // Condition turns to false. Cancelled the alarm.
-//    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//    //  Detected one anomaly.
-//    EXPECT_EQ(refractory_period_sec +
-//                    (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1) / NS_PER_SEC + 1,
-//              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
-//
-//    // Condition turns to true again.
-//    screen_off_event = CreateScreenStateChangedEvent(
-//        android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
-//        bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 2);
-//    processor->OnLogEvent(screen_off_event.get());
-//    EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 2 + 1,
-//              anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//
-//    release_event = CreateReleaseWakelockEvent(
-//        attributions2, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC);
-//    processor->OnLogEvent(release_event.get());
-//    EXPECT_EQ(refractory_period_sec +
-//                    (bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC) / NS_PER_SEC,
-//              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
-//    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//}
-//
-//TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets) {
-//    const int num_buckets = 3;
-//    const uint64_t threshold_ns = NS_PER_SEC;
-//    auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true);
-//    const uint64_t alert_id = config.alert(0).id();
-//    const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
-//
-//    int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
-//    int64_t bucketSizeNs =
-//        TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
-//
-//    ConfigKey cfgKey;
-//    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-//    EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
-//
-//    sp<AnomalyTracker> anomalyTracker =
-//        processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
-//
-//    auto screen_off_event = CreateScreenStateChangedEvent(
-//            android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 1);
-//    processor->OnLogEvent(screen_off_event.get());
-//
-//    // Acquire wakelock "wc1" in bucket #0.
-//    auto acquire_event = CreateAcquireWakelockEvent(
-//        attributions1, "wl1", bucketStartTimeNs + bucketSizeNs -  NS_PER_SEC / 2 - 1);
-//    processor->OnLogEvent(acquire_event.get());
-//    EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1,
-//              anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
-//
-//    // Release wakelock "wc1" in bucket #0.
-//    auto release_event = CreateReleaseWakelockEvent(
-//        attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - 1);
-//    processor->OnLogEvent(release_event.get());
-//    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
-//
-//    // Acquire wakelock "wc1" in bucket #1.
-//    acquire_event = CreateAcquireWakelockEvent(
-//        attributions2, "wl1", bucketStartTimeNs + bucketSizeNs + 1);
-//    processor->OnLogEvent(acquire_event.get());
-//    EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1,
-//              anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
-//
-//    release_event = CreateReleaseWakelockEvent(
-//        attributions2, "wl1", bucketStartTimeNs + bucketSizeNs + 100);
-//    processor->OnLogEvent(release_event.get());
-//    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
-//
-//    // Acquire wakelock "wc2" in bucket #2.
-//    acquire_event = CreateAcquireWakelockEvent(
-//        attributions3, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 1);
-//    processor->OnLogEvent(acquire_event.get());
-//    EXPECT_EQ((bucketStartTimeNs + 2 *  bucketSizeNs) / NS_PER_SEC + 2,
-//              anomalyTracker->getAlarmTimestampSec(dimensionKey2));
-//    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
-//
-//    // Release wakelock "wc2" in bucket #2.
-//    release_event = CreateReleaseWakelockEvent(
-//        attributions3, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC);
-//    processor->OnLogEvent(release_event.get());
-//    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2));
-//    EXPECT_EQ(refractory_period_sec +
-//                   (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC) / NS_PER_SEC,
-//              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
-//
-//    // Acquire wakelock "wc1" in bucket #2.
-//    acquire_event = CreateAcquireWakelockEvent(
-//        attributions2, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC);
-//    processor->OnLogEvent(acquire_event.get());
-//    EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 1,
-//              anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
-//
-//    // Release wakelock "wc1" in bucket #2.
-//    release_event = CreateReleaseWakelockEvent(
-//        attributions2, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC);
-//    processor->OnLogEvent(release_event.get());
-//    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//    EXPECT_EQ(refractory_period_sec +
-//                   (int64_t)(bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC) / NS_PER_SEC + 1,
-//              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
-//
-//    acquire_event = CreateAcquireWakelockEvent(
-//        attributions3, "wl2", bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 4);
-//    processor->OnLogEvent(acquire_event.get());
-//    acquire_event = CreateAcquireWakelockEvent(
-//        attributions1, "wl1", bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 5);
-//    processor->OnLogEvent(acquire_event.get());
-//    EXPECT_EQ((bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1,
-//              anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//    EXPECT_EQ((bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1,
-//              anomalyTracker->getAlarmTimestampSec(dimensionKey2));
-//
-//    release_event = CreateReleaseWakelockEvent(
-//        attributions3, "wl2", bucketStartTimeNs + 6 * bucketSizeNs + 2);
-//    processor->OnLogEvent(release_event.get());
-//    release_event = CreateReleaseWakelockEvent(
-//        attributions1, "wl1", bucketStartTimeNs + 6 * bucketSizeNs + 6);
-//    processor->OnLogEvent(release_event.get());
-//    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2));
-//    // The buckets are not messed up across dimensions. Only one dimension has anomaly triggered.
-//    EXPECT_EQ(refractory_period_sec +
-//                   (int64_t)(bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1,
-//              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
-//}
-//
-//TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period) {
-//    const int num_buckets = 2;
-//    const uint64_t threshold_ns = 3 * NS_PER_SEC;
-//    auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, false);
-//    int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
-//    int64_t bucketSizeNs =
-//        TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
-//
-//    const uint64_t alert_id = config.alert(0).id();
-//    const uint32_t refractory_period_sec = 3 * bucketSizeNs / NS_PER_SEC;
-//    config.mutable_alert(0)->set_refractory_period_secs(refractory_period_sec);
-//
-//    ConfigKey cfgKey;
-//    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-//    EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
-//
-//    sp<AnomalyTracker> anomalyTracker =
-//        processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
-//
-//    auto screen_off_event = CreateScreenStateChangedEvent(
-//            android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 1);
-//    processor->OnLogEvent(screen_off_event.get());
-//
-//    // Acquire wakelock "wc1" in bucket #0.
-//    auto acquire_event = CreateAcquireWakelockEvent(
-//        attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - 100);
-//    processor->OnLogEvent(acquire_event.get());
-//    EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3,
-//              anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
-//
-//    // Acquire the wakelock "wc1" again.
-//    acquire_event = CreateAcquireWakelockEvent(
-//        attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 2 * NS_PER_SEC + 1);
-//    processor->OnLogEvent(acquire_event.get());
-//    // The alarm does not change.
-//    EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3,
-//              anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
-//
-//    // Anomaly alarm fired late.
-//    const int64_t firedAlarmTimestampNs = bucketStartTimeNs + 2 * bucketSizeNs - NS_PER_SEC;
-//    auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan(
-//            static_cast<uint32_t>(firedAlarmTimestampNs / NS_PER_SEC));
-//    EXPECT_EQ(1u, alarmSet.size());
-//    processor->onAnomalyAlarmFired(firedAlarmTimestampNs, alarmSet);
-//    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//    EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
-//              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
-//
-//    acquire_event = CreateAcquireWakelockEvent(
-//        attributions1, "wl1", bucketStartTimeNs + 2 * bucketSizeNs - 100);
-//    processor->OnLogEvent(acquire_event.get());
-//    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//    EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
-//              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
-//
-//    auto release_event = CreateReleaseWakelockEvent(
-//        attributions1, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 1);
-//    processor->OnLogEvent(release_event.get());
-//    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//    // Within the refractory period. No anomaly.
-//    EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
-//              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
-//
-//    // A new wakelock, but still within refractory period.
-//    acquire_event = CreateAcquireWakelockEvent(
-//        attributions1, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 10 * NS_PER_SEC);
-//    processor->OnLogEvent(acquire_event.get());
-//    EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
-//              anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//
-//    release_event = CreateReleaseWakelockEvent(
-//        attributions1, "wl1", bucketStartTimeNs + 3 * bucketSizeNs - NS_PER_SEC);
-//    // Still in the refractory period. No anomaly.
-//    processor->OnLogEvent(release_event.get());
-//    EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
-//              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
-//
-//    acquire_event = CreateAcquireWakelockEvent(
-//        attributions1, "wl1", bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 5);
-//    processor->OnLogEvent(acquire_event.get());
-//    EXPECT_EQ((bucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC,
-//              anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//
-//    release_event = CreateReleaseWakelockEvent(
-//        attributions1, "wl1", bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 4);
-//    processor->OnLogEvent(release_event.get());
-//    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//
-//    acquire_event = CreateAcquireWakelockEvent(
-//        attributions1, "wl1", bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 3);
-//    processor->OnLogEvent(acquire_event.get());
-//    EXPECT_EQ((bucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC,
-//              anomalyTracker->getAlarmTimestampSec(dimensionKey));
-//}
+}  // namespace
+
+TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket) {
+    const int num_buckets = 1;
+    const uint64_t threshold_ns = NS_PER_SEC;
+    auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true);
+    const uint64_t alert_id = config.alert(0).id();
+    const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
+
+    int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
+    int64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
+
+    sp<AnomalyTracker> anomalyTracker =
+            processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
+
+    auto screen_on_event = CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+    auto screen_off_event = CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 10, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
+    processor->OnLogEvent(screen_on_event.get());
+    processor->OnLogEvent(screen_off_event.get());
+
+    // Acquire wakelock wl1.
+    auto acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 11, attributionUids1,
+                                                    attributionTags1, "wl1");
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ((bucketStartTimeNs + 11 + threshold_ns) / NS_PER_SEC + 1,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    // Release wakelock wl1. No anomaly detected. Alarm cancelled at the "release" event.
+    auto release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 101, attributionUids1,
+                                                    attributionTags1, "wl1");
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    // Acquire wakelock wl1 within bucket #0.
+    acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 110, attributionUids2,
+                                               attributionTags2, "wl1");
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ((bucketStartTimeNs + 110 + threshold_ns - 90) / NS_PER_SEC + 1,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    // Release wakelock wl1. One anomaly detected.
+    release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + NS_PER_SEC + 109,
+                                               attributionUids2, attributionTags2, "wl1");
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    // Acquire wakelock wl1.
+    acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + NS_PER_SEC + 112,
+                                               attributionUids1, attributionTags1, "wl1");
+    processor->OnLogEvent(acquire_event.get());
+    // Wakelock has been hold longer than the threshold in bucket #0. The alarm is set at the
+    // end of the refractory period.
+    const int64_t alarmFiredTimestampSec0 = anomalyTracker->getAlarmTimestampSec(dimensionKey1);
+    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1,
+              (uint32_t)alarmFiredTimestampSec0);
+
+    // Anomaly alarm fired.
+    auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan(
+            static_cast<uint32_t>(alarmFiredTimestampSec0));
+    EXPECT_EQ(1u, alarmSet.size());
+    processor->onAnomalyAlarmFired(alarmFiredTimestampSec0 * NS_PER_SEC, alarmSet);
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+    EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    // Release wakelock wl1.
+    release_event =
+            CreateReleaseWakelockEvent(alarmFiredTimestampSec0 * NS_PER_SEC + NS_PER_SEC + 1,
+                                       attributionUids1, attributionTags1, "wl1");
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+    // Within refractory period. No more anomaly detected.
+    EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    // Acquire wakelock wl1.
+    acquire_event =
+            CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC - 11,
+                                       attributionUids2, attributionTags2, "wl1");
+    processor->OnLogEvent(acquire_event.get());
+    const int64_t alarmFiredTimestampSec1 = anomalyTracker->getAlarmTimestampSec(dimensionKey1);
+    EXPECT_EQ((bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC) / NS_PER_SEC,
+              (uint64_t)alarmFiredTimestampSec1);
+
+    // Release wakelock wl1.
+    release_event =
+            CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10,
+                                       attributionUids2, attributionTags2, "wl1");
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+    EXPECT_EQ(refractory_period_sec +
+                      (bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10) / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan(
+            static_cast<uint32_t>(alarmFiredTimestampSec1));
+    EXPECT_EQ(0u, alarmSet.size());
+
+    // Acquire wakelock wl1 near the end of bucket #0.
+    acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 2,
+                                               attributionUids1, attributionTags1, "wl1");
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+
+    // Release the event at early bucket #1.
+    release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs + NS_PER_SEC - 1,
+                                               attributionUids1, attributionTags1, "wl1");
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+    // Anomaly detected when stopping the alarm. The refractory period does not change.
+    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    // Condition changes to false.
+    screen_on_event =
+            CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 20,
+                                          android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+    processor->OnLogEvent(screen_on_event.get());
+    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+
+    acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 30,
+                                               attributionUids2, attributionTags2, "wl1");
+    processor->OnLogEvent(acquire_event.get());
+    // The condition is false. Do not start the alarm.
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+    EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    // Condition turns true.
+    screen_off_event =
+            CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC,
+                                          android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
+    processor->OnLogEvent(screen_off_event.get());
+    EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC + threshold_ns) / NS_PER_SEC,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+
+    // Condition turns to false.
+    screen_on_event =
+            CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1,
+                                          android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+    processor->OnLogEvent(screen_on_event.get());
+    // Condition turns to false. Cancelled the alarm.
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+    //  Detected one anomaly.
+    EXPECT_EQ(refractory_period_sec +
+                      (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1) / NS_PER_SEC + 1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    // Condition turns to true again.
+    screen_off_event =
+            CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 2,
+                                          android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
+    processor->OnLogEvent(screen_off_event.get());
+    EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 2 + 1,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+
+    release_event =
+            CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC,
+                                       attributionUids2, attributionTags2, "wl1");
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(refractory_period_sec +
+                      (bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC) / NS_PER_SEC,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+}
+
+TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets) {
+    const int num_buckets = 3;
+    const uint64_t threshold_ns = NS_PER_SEC;
+    auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true);
+    const uint64_t alert_id = config.alert(0).id();
+    const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
+
+    int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
+    int64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
+
+    sp<AnomalyTracker> anomalyTracker =
+            processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
+
+    auto screen_off_event = CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
+    processor->OnLogEvent(screen_off_event.get());
+
+    // Acquire wakelock "wc1" in bucket #0.
+    auto acquire_event =
+            CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - NS_PER_SEC / 2 - 1,
+                                       attributionUids1, attributionTags1, "wl1");
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    // Release wakelock "wc1" in bucket #0.
+    auto release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1,
+                                                    attributionUids1, attributionTags1, "wl1");
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    // Acquire wakelock "wc1" in bucket #1.
+    acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1,
+                                               attributionUids2, attributionTags2, "wl1");
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs + 100,
+                                               attributionUids2, attributionTags2, "wl1");
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    // Acquire wakelock "wc2" in bucket #2.
+    acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1,
+                                               attributionUids3, attributionTags3, "wl2");
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey2));
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+
+    // Release wakelock "wc2" in bucket #2.
+    release_event =
+            CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC,
+                                       attributionUids3, attributionTags3, "wl2");
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2));
+    EXPECT_EQ(refractory_period_sec +
+                      (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC) / NS_PER_SEC,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
+
+    // Acquire wakelock "wc1" in bucket #2.
+    acquire_event =
+            CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC,
+                                       attributionUids2, attributionTags2, "wl1");
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 1,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    // Release wakelock "wc1" in bucket #2.
+    release_event =
+            CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC,
+                                       attributionUids2, attributionTags2, "wl1");
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+    EXPECT_EQ(refractory_period_sec +
+                      (int64_t)(bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC) /
+                              NS_PER_SEC +
+                      1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    acquire_event =
+            CreateAcquireWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 4,
+                                       attributionUids3, attributionTags3, "wl2");
+    processor->OnLogEvent(acquire_event.get());
+    acquire_event =
+            CreateAcquireWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 5,
+                                       attributionUids1, attributionTags1, "wl1");
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ((bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+    EXPECT_EQ((bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey2));
+
+    release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs + 2,
+                                               attributionUids3, attributionTags3, "wl2");
+    processor->OnLogEvent(release_event.get());
+    release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs + 6,
+                                               attributionUids1, attributionTags1, "wl1");
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2));
+    // The buckets are not messed up across dimensions. Only one dimension has anomaly triggered.
+    EXPECT_EQ(refractory_period_sec + (int64_t)(bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC +
+                      1,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+}
+
+TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period) {
+    const int num_buckets = 2;
+    const uint64_t threshold_ns = 3 * NS_PER_SEC;
+    auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, false);
+    int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
+    int64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
+
+    const uint64_t alert_id = config.alert(0).id();
+    const uint32_t refractory_period_sec = 3 * bucketSizeNs / NS_PER_SEC;
+    config.mutable_alert(0)->set_refractory_period_secs(refractory_period_sec);
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
+
+    sp<AnomalyTracker> anomalyTracker =
+            processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
+
+    auto screen_off_event = CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
+    processor->OnLogEvent(screen_off_event.get());
+
+    // Acquire wakelock "wc1" in bucket #0.
+    auto acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 100,
+                                                    attributionUids1, attributionTags1, "wl1");
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    // Acquire the wakelock "wc1" again.
+    acquire_event =
+            CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2 * NS_PER_SEC + 1,
+                                       attributionUids1, attributionTags1, "wl1");
+    processor->OnLogEvent(acquire_event.get());
+    // The alarm does not change.
+    EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+    EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    // Anomaly alarm fired late.
+    const int64_t firedAlarmTimestampNs = bucketStartTimeNs + 2 * bucketSizeNs - NS_PER_SEC;
+    auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan(
+            static_cast<uint32_t>(firedAlarmTimestampNs / NS_PER_SEC));
+    EXPECT_EQ(1u, alarmSet.size());
+    processor->onAnomalyAlarmFired(firedAlarmTimestampNs, alarmSet);
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+    EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs - 100,
+                                               attributionUids1, attributionTags1, "wl1");
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+    EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    auto release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1,
+                                                    attributionUids1, attributionTags1, "wl1");
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+    // Within the refractory period. No anomaly.
+    EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    // A new wakelock, but still within refractory period.
+    acquire_event =
+            CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 10 * NS_PER_SEC,
+                                       attributionUids1, attributionTags1, "wl1");
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+
+    release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs - NS_PER_SEC,
+                                               attributionUids1, attributionTags1, "wl1");
+    // Still in the refractory period. No anomaly.
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
+              anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+    acquire_event =
+            CreateAcquireWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 5,
+                                       attributionUids1, attributionTags1, "wl1");
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ((bucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+
+    release_event =
+            CreateReleaseWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 4,
+                                       attributionUids1, attributionTags1, "wl1");
+    processor->OnLogEvent(release_event.get());
+    EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+
+    acquire_event =
+            CreateAcquireWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 3,
+                                       attributionUids1, attributionTags1, "wl1");
+    processor->OnLogEvent(acquire_event.get());
+    EXPECT_EQ((bucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC,
+              anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+}
 
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
index 6051174..9e743f7 100644
--- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
@@ -53,367 +53,318 @@
     return config;
 }
 
+// GMS core node is in the middle.
+std::vector<int> attributionUids1 = {111, 222, 333};
+std::vector<string> attributionTags1 = {"App1", "GMSCoreModule1", "App3"};
+
+// GMS core node is the last one.
+std::vector<int> attributionUids2 = {111, 333, 222};
+std::vector<string> attributionTags2 = {"App1", "App3", "GMSCoreModule1"};
+
+// GMS core node is the first one.
+std::vector<int> attributionUids3 = {222, 333};
+std::vector<string> attributionTags3 = {"GMSCoreModule1", "App3"};
+
+// Single GMS core node.
+std::vector<int> attributionUids4 = {222};
+std::vector<string> attributionTags4 = {"GMSCoreModule1"};
+
+// GMS core has another uid.
+std::vector<int> attributionUids5 = {111, 444, 333};
+std::vector<string> attributionTags5 = {"App1", "GMSCoreModule2", "App3"};
+
+// Multiple GMS core nodes.
+std::vector<int> attributionUids6 = {444, 222};
+std::vector<string> attributionTags6 = {"GMSCoreModule2", "GMSCoreModule1"};
+
+// No GMS core nodes
+std::vector<int> attributionUids7 = {111, 333};
+std::vector<string> attributionTags7 = {"App1", "App3"};
+
+std::vector<int> attributionUids8 = {111};
+std::vector<string> attributionTags8 = {"App1"};
+
+// GMS core node with isolated uid.
+const int isolatedUid = 666;
+std::vector<int> attributionUids9 = {isolatedUid};
+std::vector<string> attributionTags9 = {"GMSCoreModule3"};
+
+std::vector<int> attributionUids10 = {isolatedUid};
+std::vector<string> attributionTags10 = {"GMSCoreModule1"};
+
 }  // namespace
 
-// TODO(b/149590301): Update these tests to use new socket schema.
-//TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid) {
-//    auto config = CreateStatsdConfig(Position::FIRST);
-//    int64_t bucketStartTimeNs = 10000000000;
-//    int64_t bucketSizeNs =
-//        TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
-//
-//    ConfigKey cfgKey;
-//    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-//
-//    // Here it assumes that GMS core has two uids.
-//    processor->getUidMap()->updateMap(
-//            1, {222, 444, 111, 333}, {1, 1, 2, 2},
-//            {String16("v1"), String16("v1"), String16("v2"), String16("v2")},
-//            {String16("com.android.gmscore"), String16("com.android.gmscore"), String16("app1"),
-//             String16("APP3")},
-//            {String16(""), String16(""), String16(""), String16("")});
-//
-//    // GMS core node is in the middle.
-//    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
-//                                                          CreateAttribution(222, "GMSCoreModule1"),
-//                                                          CreateAttribution(333, "App3")};
-//
-//    // GMS core node is the last one.
-//    std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(111, "App1"),
-//                                                          CreateAttribution(333, "App3"),
-//                                                          CreateAttribution(222, "GMSCoreModule1")};
-//
-//    // GMS core node is the first one.
-//    std::vector<AttributionNodeInternal> attributions3 = {CreateAttribution(222, "GMSCoreModule1"),
-//                                                          CreateAttribution(333, "App3")};
-//
-//    // Single GMS core node.
-//    std::vector<AttributionNodeInternal> attributions4 = {CreateAttribution(222, "GMSCoreModule1")};
-//
-//    // GMS core has another uid.
-//    std::vector<AttributionNodeInternal> attributions5 = {CreateAttribution(111, "App1"),
-//                                                          CreateAttribution(444, "GMSCoreModule2"),
-//                                                          CreateAttribution(333, "App3")};
-//
-//    // Multiple GMS core nodes.
-//    std::vector<AttributionNodeInternal> attributions6 = {CreateAttribution(444, "GMSCoreModule2"),
-//                                                          CreateAttribution(222, "GMSCoreModule1")};
-//
-//    // No GMS core nodes.
-//    std::vector<AttributionNodeInternal> attributions7 = {CreateAttribution(111, "App1"),
-//                                                          CreateAttribution(333, "App3")};
-//    std::vector<AttributionNodeInternal> attributions8 = {CreateAttribution(111, "App1")};
-//
-//    // GMS core node with isolated uid.
-//    const int isolatedUid = 666;
-//    std::vector<AttributionNodeInternal> attributions9 = {
-//            CreateAttribution(isolatedUid, "GMSCoreModule3")};
-//
-//    std::vector<std::unique_ptr<LogEvent>> events;
-//    // Events 1~4 are in the 1st bucket.
-//    events.push_back(CreateAcquireWakelockEvent(
-//        attributions1, "wl1", bucketStartTimeNs + 2));
-//    events.push_back(CreateAcquireWakelockEvent(
-//        attributions2, "wl1", bucketStartTimeNs + 200));
-//    events.push_back(CreateAcquireWakelockEvent(
-//        attributions3, "wl1", bucketStartTimeNs + bucketSizeNs - 1));
-//    events.push_back(CreateAcquireWakelockEvent(
-//        attributions4, "wl1", bucketStartTimeNs + bucketSizeNs));
-//
-//    // Events 5~8 are in the 3rd bucket.
-//    events.push_back(CreateAcquireWakelockEvent(
-//        attributions5, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 1));
-//    events.push_back(CreateAcquireWakelockEvent(
-//        attributions6, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 100));
-//    events.push_back(CreateAcquireWakelockEvent(
-//        attributions7, "wl2", bucketStartTimeNs + 3 * bucketSizeNs - 2));
-//    events.push_back(CreateAcquireWakelockEvent(
-//        attributions8, "wl2", bucketStartTimeNs + 3 * bucketSizeNs));
-//    events.push_back(CreateAcquireWakelockEvent(
-//        attributions9, "wl2", bucketStartTimeNs + 3 * bucketSizeNs + 1));
-//    events.push_back(CreateAcquireWakelockEvent(
-//        attributions9, "wl2", bucketStartTimeNs + 3 * bucketSizeNs + 100));
-//    events.push_back(CreateIsolatedUidChangedEvent(
-//        isolatedUid, 222, true/* is_create*/, bucketStartTimeNs + 3 * bucketSizeNs - 1));
-//    events.push_back(CreateIsolatedUidChangedEvent(
-//        isolatedUid, 222, false/* is_create*/, bucketStartTimeNs + 3 * bucketSizeNs + 10));
-//
-//    sortLogEventsByTimestamp(&events);
-//
-//    for (const auto& event : events) {
-//        processor->OnLogEvent(event.get());
-//    }
-//    ConfigMetricsReportList reports;
-//    vector<uint8_t> buffer;
-//    processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true,
-//                            ADB_DUMP, FAST, &buffer);
-//    EXPECT_TRUE(buffer.size() > 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStringInReport(&reports);
-//    backfillStartEndTimestamp(&reports);
-//    EXPECT_EQ(reports.reports_size(), 1);
-//    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
-//
-//    StatsLogReport::CountMetricDataWrapper countMetrics;
-//    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
-//    EXPECT_EQ(countMetrics.data_size(), 4);
-//
-//    auto data = countMetrics.data(0);
-//    ValidateAttributionUidAndTagDimension(
-//        data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 111,
-//            "App1");
-//    EXPECT_EQ(data.bucket_info_size(), 2);
-//    EXPECT_EQ(data.bucket_info(0).count(), 2);
-//    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-//    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
-//    EXPECT_EQ(data.bucket_info(1).count(), 1);
-//    EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
-//    EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs);
-//
-//    data = countMetrics.data(1);
-//    ValidateAttributionUidAndTagDimension(
-//        data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 222,
-//            "GMSCoreModule1");
-//    EXPECT_EQ(data.bucket_info_size(), 2);
-//    EXPECT_EQ(data.bucket_info(0).count(), 1);
-//    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-//    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
-//    EXPECT_EQ(data.bucket_info(1).count(), 1);
-//    EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
-//    EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
-//
-//    data = countMetrics.data(2);
-//    ValidateAttributionUidAndTagDimension(
-//        data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 222,
-//            "GMSCoreModule3");
-//    EXPECT_EQ(data.bucket_info_size(), 1);
-//    EXPECT_EQ(data.bucket_info(0).count(), 1);
-//    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs);
-//    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 4 * bucketSizeNs);
-//
-//    data = countMetrics.data(3);
-//    ValidateAttributionUidAndTagDimension(
-//        data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 444,
-//            "GMSCoreModule2");
-//    EXPECT_EQ(data.bucket_info_size(), 1);
-//    EXPECT_EQ(data.bucket_info(0).count(), 1);
-//    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
-//    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs);
-//}
-//
-//TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain) {
-//    auto config = CreateStatsdConfig(Position::ALL);
-//    int64_t bucketStartTimeNs = 10000000000;
-//    int64_t bucketSizeNs =
-//        TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
-//
-//    ConfigKey cfgKey;
-//    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-//
-//    // Here it assumes that GMS core has two uids.
-//    processor->getUidMap()->updateMap(
-//            1, {222, 444, 111, 333}, {1, 1, 2, 2},
-//            {String16("v1"), String16("v1"), String16("v2"), String16("v2")},
-//            {String16("com.android.gmscore"), String16("com.android.gmscore"), String16("app1"),
-//             String16("APP3")},
-//            {String16(""), String16(""), String16(""), String16("")});
-//
-//    // GMS core node is in the middle.
-//    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
-//                                                          CreateAttribution(222, "GMSCoreModule1"),
-//                                                          CreateAttribution(333, "App3")};
-//
-//    // GMS core node is the last one.
-//    std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(111, "App1"),
-//                                                          CreateAttribution(333, "App3"),
-//                                                          CreateAttribution(222, "GMSCoreModule1")};
-//
-//    // GMS core node is the first one.
-//    std::vector<AttributionNodeInternal> attributions3 = {CreateAttribution(222, "GMSCoreModule1"),
-//                                                          CreateAttribution(333, "App3")};
-//
-//    // Single GMS core node.
-//    std::vector<AttributionNodeInternal> attributions4 = {CreateAttribution(222, "GMSCoreModule1")};
-//
-//    // GMS core has another uid.
-//    std::vector<AttributionNodeInternal> attributions5 = {CreateAttribution(111, "App1"),
-//                                                          CreateAttribution(444, "GMSCoreModule2"),
-//                                                          CreateAttribution(333, "App3")};
-//
-//    // Multiple GMS core nodes.
-//    std::vector<AttributionNodeInternal> attributions6 = {CreateAttribution(444, "GMSCoreModule2"),
-//                                                          CreateAttribution(222, "GMSCoreModule1")};
-//
-//    // No GMS core nodes.
-//    std::vector<AttributionNodeInternal> attributions7 = {CreateAttribution(111, "App1"),
-//                                                          CreateAttribution(333, "App3")};
-//    std::vector<AttributionNodeInternal> attributions8 = {CreateAttribution(111, "App1")};
-//
-//    // GMS core node with isolated uid.
-//    const int isolatedUid = 666;
-//    std::vector<AttributionNodeInternal> attributions9 = {
-//            CreateAttribution(isolatedUid, "GMSCoreModule1")};
-//
-//    std::vector<std::unique_ptr<LogEvent>> events;
-//    // Events 1~4 are in the 1st bucket.
-//    events.push_back(CreateAcquireWakelockEvent(
-//        attributions1, "wl1", bucketStartTimeNs + 2));
-//    events.push_back(CreateAcquireWakelockEvent(
-//        attributions2, "wl1", bucketStartTimeNs + 200));
-//    events.push_back(CreateAcquireWakelockEvent(
-//        attributions3, "wl1", bucketStartTimeNs + bucketSizeNs - 1));
-//    events.push_back(CreateAcquireWakelockEvent(
-//        attributions4, "wl1", bucketStartTimeNs + bucketSizeNs));
-//
-//    // Events 5~8 are in the 3rd bucket.
-//    events.push_back(CreateAcquireWakelockEvent(
-//        attributions5, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 1));
-//    events.push_back(CreateAcquireWakelockEvent(
-//        attributions6, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 100));
-//    events.push_back(CreateAcquireWakelockEvent(
-//        attributions7, "wl2", bucketStartTimeNs + 3 * bucketSizeNs - 2));
-//    events.push_back(CreateAcquireWakelockEvent(
-//        attributions8, "wl2", bucketStartTimeNs + 3 * bucketSizeNs));
-//    events.push_back(CreateAcquireWakelockEvent(
-//        attributions9, "wl2", bucketStartTimeNs + 3 * bucketSizeNs + 1));
-//    events.push_back(CreateAcquireWakelockEvent(
-//        attributions9, "wl2", bucketStartTimeNs + 3 * bucketSizeNs + 100));
-//    events.push_back(CreateIsolatedUidChangedEvent(
-//        isolatedUid, 222, true/* is_create*/, bucketStartTimeNs + 3 * bucketSizeNs - 1));
-//    events.push_back(CreateIsolatedUidChangedEvent(
-//        isolatedUid, 222, false/* is_create*/, bucketStartTimeNs + 3 * bucketSizeNs + 10));
-//
-//    sortLogEventsByTimestamp(&events);
-//
-//    for (const auto& event : events) {
-//        processor->OnLogEvent(event.get());
-//    }
-//    ConfigMetricsReportList reports;
-//    vector<uint8_t> buffer;
-//    processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true,
-//                            ADB_DUMP, FAST, &buffer);
-//    EXPECT_TRUE(buffer.size() > 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStringInReport(&reports);
-//    backfillStartEndTimestamp(&reports);
-//    EXPECT_EQ(reports.reports_size(), 1);
-//    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
-//
-//    StatsLogReport::CountMetricDataWrapper countMetrics;
-//    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
-//    EXPECT_EQ(countMetrics.data_size(), 6);
-//
-//    auto data = countMetrics.data(0);
-//    ValidateAttributionUidAndTagDimension(
-//        data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1");
-//    EXPECT_EQ(2, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
-//              data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
-//              data.bucket_info(0).end_bucket_elapsed_nanos());
-//    EXPECT_EQ(1, data.bucket_info(1).count());
-//    EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
-//              data.bucket_info(1).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + 4 * bucketSizeNs,
-//              data.bucket_info(1).end_bucket_elapsed_nanos());
-//
-//    data = countMetrics.data(1);
-//    ValidateUidDimension(
-//        data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 222);
-//    ValidateAttributionUidAndTagDimension(
-//        data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1");
-//    ValidateUidDimension(
-//        data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333);
-//    ValidateAttributionUidAndTagDimension(
-//        data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333, "App3");
-//    EXPECT_EQ(data.bucket_info_size(), 1);
-//    EXPECT_EQ(data.bucket_info(0).count(), 1);
-//    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-//    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
-//
-//    data = countMetrics.data(2);
-//    ValidateUidDimension(
-//        data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 444);
-//    ValidateAttributionUidAndTagDimension(
-//        data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 444, "GMSCoreModule2");
-//    ValidateUidDimension(
-//        data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222);
-//    ValidateAttributionUidAndTagDimension(
-//        data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1");
-//    EXPECT_EQ(data.bucket_info_size(), 1);
-//    EXPECT_EQ(data.bucket_info(0).count(), 1);
-//    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
-//              data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
-//              data.bucket_info(0).end_bucket_elapsed_nanos());
-//
-//    data = countMetrics.data(3);
-//    ValidateUidDimension(
-//        data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111);
-//    ValidateAttributionUidAndTagDimension(
-//        data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111, "App1");
-//    ValidateUidDimension(
-//        data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222);
-//    ValidateAttributionUidAndTagDimension(
-//        data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1");
-//    ValidateUidDimension(
-//        data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333);
-//    ValidateAttributionUidAndTagDimension(
-//        data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333, "App3");
-//    EXPECT_EQ(data.bucket_info_size(), 1);
-//    EXPECT_EQ(data.bucket_info(0).count(), 1);
-//    EXPECT_EQ(bucketStartTimeNs,
-//              data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
-//              data.bucket_info(0).end_bucket_elapsed_nanos());
-//
-//    data = countMetrics.data(4);
-//    ValidateUidDimension(
-//        data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111);
-//    ValidateAttributionUidAndTagDimension(
-//        data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111, "App1");
-//    ValidateUidDimension(
-//        data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333);
-//    ValidateAttributionUidAndTagDimension(
-//        data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333, "App3");
-//    ValidateUidDimension(
-//        data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 222);
-//    ValidateAttributionUidAndTagDimension(
-//        data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1");
-//    EXPECT_EQ(data.bucket_info_size(), 1);
-//    EXPECT_EQ(data.bucket_info(0).count(), 1);
-//    EXPECT_EQ(bucketStartTimeNs,
-//              data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
-//              data.bucket_info(0).end_bucket_elapsed_nanos());
-//
-//    data = countMetrics.data(5);
-//    ValidateUidDimension(
-//        data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111);
-//    ValidateAttributionUidAndTagDimension(
-//        data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111, "App1");
-//    ValidateUidDimension(
-//        data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 444);
-//    ValidateAttributionUidAndTagDimension(
-//        data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 444, "GMSCoreModule2");
-//    ValidateUidDimension(
-//        data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333);
-//    ValidateAttributionUidAndTagDimension(
-//        data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333, "App3");
-//    EXPECT_EQ(data.bucket_info_size(), 1);
-//    EXPECT_EQ(data.bucket_info(0).count(), 1);
-//    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
-//              data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
-//              data.bucket_info(0).end_bucket_elapsed_nanos());
-//}
+TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid) {
+    auto config = CreateStatsdConfig(Position::FIRST);
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+    // Here it assumes that GMS core has two uids.
+    processor->getUidMap()->updateMap(
+            1, {222, 444, 111, 333}, {1, 1, 2, 2},
+            {String16("v1"), String16("v1"), String16("v2"), String16("v2")},
+            {String16("com.android.gmscore"), String16("com.android.gmscore"), String16("app1"),
+             String16("APP3")},
+            {String16(""), String16(""), String16(""), String16("")});
+
+    std::vector<std::unique_ptr<LogEvent>> events;
+    // Events 1~4 are in the 1st bucket.
+    events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
+                                                attributionTags1, "wl1"));
+    events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 200, attributionUids2,
+                                                attributionTags2, "wl1"));
+    events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1,
+                                                attributionUids3, attributionTags3, "wl1"));
+    events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs, attributionUids4,
+                                                attributionTags4, "wl1"));
+
+    // Events 5~8 are in the 3rd bucket.
+    events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1,
+                                                attributionUids5, attributionTags5, "wl2"));
+    events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100,
+                                                attributionUids6, attributionTags6, "wl2"));
+    events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs - 2,
+                                                attributionUids7, attributionTags7, "wl2"));
+    events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs,
+                                                attributionUids8, attributionTags8, "wl2"));
+    events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 1,
+                                                attributionUids9, attributionTags9, "wl2"));
+    events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 100,
+                                                attributionUids9, attributionTags9, "wl2"));
+    events.push_back(CreateIsolatedUidChangedEvent(bucketStartTimeNs + 3 * bucketSizeNs - 1, 222,
+                                                   isolatedUid, true /*is_create*/));
+    events.push_back(CreateIsolatedUidChangedEvent(bucketStartTimeNs + 3 * bucketSizeNs + 10, 222,
+                                                   isolatedUid, false /*is_create*/));
+
+    sortLogEventsByTimestamp(&events);
+
+    for (const auto& event : events) {
+        processor->OnLogEvent(event.get());
+    }
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true, ADB_DUMP,
+                            FAST, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+    EXPECT_EQ(reports.reports_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+
+    StatsLogReport::CountMetricDataWrapper countMetrics;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+    EXPECT_EQ(countMetrics.data_size(), 4);
+
+    auto data = countMetrics.data(0);
+    ValidateAttributionUidAndTagDimension(data.dimensions_in_what(),
+                                          android::util::WAKELOCK_STATE_CHANGED, 111, "App1");
+    EXPECT_EQ(data.bucket_info_size(), 2);
+    EXPECT_EQ(data.bucket_info(0).count(), 2);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(data.bucket_info(1).count(), 1);
+    EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
+              bucketStartTimeNs + 2 * bucketSizeNs);
+    EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs);
+
+    data = countMetrics.data(1);
+    ValidateAttributionUidAndTagDimension(data.dimensions_in_what(),
+                                          android::util::WAKELOCK_STATE_CHANGED, 222,
+                                          "GMSCoreModule1");
+    EXPECT_EQ(data.bucket_info_size(), 2);
+    EXPECT_EQ(data.bucket_info(0).count(), 1);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(data.bucket_info(1).count(), 1);
+    EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+
+    data = countMetrics.data(2);
+    ValidateAttributionUidAndTagDimension(data.dimensions_in_what(),
+                                          android::util::WAKELOCK_STATE_CHANGED, 222,
+                                          "GMSCoreModule3");
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 1);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
+              bucketStartTimeNs + 3 * bucketSizeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 4 * bucketSizeNs);
+
+    data = countMetrics.data(3);
+    ValidateAttributionUidAndTagDimension(data.dimensions_in_what(),
+                                          android::util::WAKELOCK_STATE_CHANGED, 444,
+                                          "GMSCoreModule2");
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 1);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
+              bucketStartTimeNs + 2 * bucketSizeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs);
+}
+
+TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain) {
+    auto config = CreateStatsdConfig(Position::ALL);
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+    // Here it assumes that GMS core has two uids.
+    processor->getUidMap()->updateMap(
+            1, {222, 444, 111, 333}, {1, 1, 2, 2},
+            {String16("v1"), String16("v1"), String16("v2"), String16("v2")},
+            {String16("com.android.gmscore"), String16("com.android.gmscore"), String16("app1"),
+             String16("APP3")},
+            {String16(""), String16(""), String16(""), String16("")});
+
+    std::vector<std::unique_ptr<LogEvent>> events;
+    // Events 1~4 are in the 1st bucket.
+    events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
+                                                attributionTags1, "wl1"));
+    events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 200, attributionUids2,
+                                                attributionTags2, "wl1"));
+    events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1,
+                                                attributionUids3, attributionTags3, "wl1"));
+    events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs, attributionUids4,
+                                                attributionTags4, "wl1"));
+
+    // Events 5~8 are in the 3rd bucket.
+    events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1,
+                                                attributionUids5, attributionTags5, "wl2"));
+    events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100,
+                                                attributionUids6, attributionTags6, "wl2"));
+    events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs - 2,
+                                                attributionUids7, attributionTags7, "wl2"));
+    events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs,
+                                                attributionUids8, attributionTags8, "wl2"));
+    events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 1,
+                                                attributionUids10, attributionTags10, "wl2"));
+    events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 100,
+                                                attributionUids10, attributionTags10, "wl2"));
+    events.push_back(CreateIsolatedUidChangedEvent(bucketStartTimeNs + 3 * bucketSizeNs - 1, 222,
+                                                   isolatedUid, true /*is_create*/));
+    events.push_back(CreateIsolatedUidChangedEvent(bucketStartTimeNs + 3 * bucketSizeNs + 10, 222,
+                                                   isolatedUid, false /*is_create*/));
+
+    sortLogEventsByTimestamp(&events);
+
+    for (const auto& event : events) {
+        processor->OnLogEvent(event.get());
+    }
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true, ADB_DUMP,
+                            FAST, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+    EXPECT_EQ(reports.reports_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+
+    StatsLogReport::CountMetricDataWrapper countMetrics;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+    EXPECT_EQ(countMetrics.data_size(), 6);
+
+    auto data = countMetrics.data(0);
+    ValidateAttributionUidAndTagDimension(data.dimensions_in_what(),
+                                          android::util::WAKELOCK_STATE_CHANGED, 222,
+                                          "GMSCoreModule1");
+    EXPECT_EQ(2, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+    EXPECT_EQ(1, data.bucket_info(1).count());
+    EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
+              data.bucket_info(1).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(1);
+    ValidateUidDimension(data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 222);
+    ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0,
+                                          android::util::WAKELOCK_STATE_CHANGED, 222,
+                                          "GMSCoreModule1");
+    ValidateUidDimension(data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333);
+    ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1,
+                                          android::util::WAKELOCK_STATE_CHANGED, 333, "App3");
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 1);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
+
+    data = countMetrics.data(2);
+    ValidateUidDimension(data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 444);
+    ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0,
+                                          android::util::WAKELOCK_STATE_CHANGED, 444,
+                                          "GMSCoreModule2");
+    ValidateUidDimension(data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222);
+    ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1,
+                                          android::util::WAKELOCK_STATE_CHANGED, 222,
+                                          "GMSCoreModule1");
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 1);
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+              data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(3);
+    ValidateUidDimension(data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111);
+    ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0,
+                                          android::util::WAKELOCK_STATE_CHANGED, 111, "App1");
+    ValidateUidDimension(data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222);
+    ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1,
+                                          android::util::WAKELOCK_STATE_CHANGED, 222,
+                                          "GMSCoreModule1");
+    ValidateUidDimension(data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333);
+    ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 2,
+                                          android::util::WAKELOCK_STATE_CHANGED, 333, "App3");
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 1);
+    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(4);
+    ValidateUidDimension(data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111);
+    ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0,
+                                          android::util::WAKELOCK_STATE_CHANGED, 111, "App1");
+    ValidateUidDimension(data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333);
+    ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1,
+                                          android::util::WAKELOCK_STATE_CHANGED, 333, "App3");
+    ValidateUidDimension(data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 222);
+    ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 2,
+                                          android::util::WAKELOCK_STATE_CHANGED, 222,
+                                          "GMSCoreModule1");
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 1);
+    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(5);
+    ValidateUidDimension(data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111);
+    ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0,
+                                          android::util::WAKELOCK_STATE_CHANGED, 111, "App1");
+    ValidateUidDimension(data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 444);
+    ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1,
+                                          android::util::WAKELOCK_STATE_CHANGED, 444,
+                                          "GMSCoreModule2");
+    ValidateUidDimension(data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333);
+    ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 2,
+                                          android::util::WAKELOCK_STATE_CHANGED, 333, "App3");
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 1);
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+              data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+}
 
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
diff --git a/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp b/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp
index f8edee5..102bb1e 100644
--- a/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp
@@ -56,54 +56,54 @@
 
 }  // namespace
 
-// TODO(b/149590301): Update this test to use new socket schema.
-//TEST(ConfigTtlE2eTest, TestCountMetric) {
-//    const int num_buckets = 1;
-//    const int threshold = 3;
-//    auto config = CreateStatsdConfig(num_buckets, threshold);
-//    const uint64_t alert_id = config.alert(0).id();
-//    const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
-//
-//    int64_t bucketStartTimeNs = 10000000000;
-//    int64_t bucketSizeNs =
-//        TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
-//
-//    ConfigKey cfgKey;
-//    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-//
-//    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
-//
-//    FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
-//                           Value((int32_t)111));
-//    HashableDimensionKey whatKey1({fieldValue1});
-//    MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
-//
-//    FieldValue fieldValue2(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
-//                           Value((int32_t)222));
-//    HashableDimensionKey whatKey2({fieldValue2});
-//    MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY);
-//
-//    auto event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 2);
-//    processor->OnLogEvent(event.get());
-//
-//    event = CreateAcquireWakelockEvent(attributions1, "wl2", bucketStartTimeNs + bucketSizeNs + 2);
-//    processor->OnLogEvent(event.get());
-//
-//    event = CreateAcquireWakelockEvent(
-//        attributions1, "wl1", bucketStartTimeNs + 25 * bucketSizeNs + 2);
-//    processor->OnLogEvent(event.get());
-//
-//    EXPECT_EQ((int64_t)(bucketStartTimeNs + 25 * bucketSizeNs + 2 + 2 * 3600 * NS_PER_SEC),
-//              processor->mMetricsManagers.begin()->second->getTtlEndNs());
-//
-//    // Clear the data stored on disk as a result of the ttl.
-//    vector<uint8_t> buffer;
-//    processor->onDumpReport(cfgKey, bucketStartTimeNs + 25 * bucketSizeNs + 3, false, true,
-//                                ADB_DUMP, FAST, &buffer);
-//}
+TEST(ConfigTtlE2eTest, TestCountMetric) {
+    const int num_buckets = 1;
+    const int threshold = 3;
+    auto config = CreateStatsdConfig(num_buckets, threshold);
+    const uint64_t alert_id = config.alert(0).id();
+    const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
 
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+    std::vector<int> attributionUids1 = {111};
+    std::vector<string> attributionTags1 = {"App1"};
+
+    FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
+                           Value((int32_t)111));
+    HashableDimensionKey whatKey1({fieldValue1});
+    MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
+
+    FieldValue fieldValue2(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
+                           Value((int32_t)222));
+    HashableDimensionKey whatKey2({fieldValue2});
+    MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY);
+
+    auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
+                                            attributionTags1, "wl1");
+    processor->OnLogEvent(event.get());
+
+    event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2, attributionUids1,
+                                       attributionTags1, "wl2");
+    processor->OnLogEvent(event.get());
+
+    event = CreateAcquireWakelockEvent(bucketStartTimeNs + 25 * bucketSizeNs + 2, attributionUids1,
+                                       attributionTags1, "wl1");
+    processor->OnLogEvent(event.get());
+
+    EXPECT_EQ((int64_t)(bucketStartTimeNs + 25 * bucketSizeNs + 2 + 2 * 3600 * NS_PER_SEC),
+              processor->mMetricsManagers.begin()->second->getTtlEndNs());
+
+    // Clear the data stored on disk as a result of the ttl.
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 25 * bucketSizeNs + 3, false, true,
+                            ADB_DUMP, FAST, &buffer);
+}
 
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
diff --git a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
index a1f74a6..2cd7854 100644
--- a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
@@ -27,773 +27,775 @@
 
 #ifdef __ANDROID__
 
-// TODO(b/149590301): Update these tests to use new socket schema.
-///**
-// * Test a count metric that has one slice_by_state with no primary fields.
-// *
-// * Once the CountMetricProducer is initialized, it has one atom id in
-// * mSlicedStateAtoms and no entries in mStateGroupMap.
-//
-// * One StateTracker tracks the state atom, and it has one listener which is the
-// * CountMetricProducer that was initialized.
-// */
-//TEST(CountMetricE2eTest, TestSlicedState) {
-//    // Initialize config.
-//    StatsdConfig config;
-//    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
-//
-//    auto syncStartMatcher = CreateSyncStartAtomMatcher();
-//    *config.add_atom_matcher() = syncStartMatcher;
-//
-//    auto state = CreateScreenState();
-//    *config.add_state() = state;
-//
-//    // Create count metric that slices by screen state.
-//    int64_t metricId = 123456;
-//    auto countMetric = config.add_count_metric();
-//    countMetric->set_id(metricId);
-//    countMetric->set_what(syncStartMatcher.id());
-//    countMetric->set_bucket(TimeUnit::FIVE_MINUTES);
-//    countMetric->add_slice_by_state(state.id());
-//
-//    // Initialize StatsLogProcessor.
-//    const uint64_t bucketStartTimeNs = 10000000000;  // 0:10
-//    const uint64_t bucketSizeNs =
-//            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
-//    int uid = 12345;
-//    int64_t cfgId = 98765;
-//    ConfigKey cfgKey(uid, cfgId);
-//    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-//
-//    // Check that CountMetricProducer was initialized correctly.
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
-//    EXPECT_TRUE(metricsManager->isConfigValid());
-//    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
-//    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
-//    EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
-//    EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID);
-//    EXPECT_EQ(metricProducer->mStateGroupMap.size(), 0);
-//
-//    // Check that StateTrackers were initialized correctly.
-//    EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
-//    EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
-//
-//    /*
-//               bucket #1                      bucket #2
-//    |     1     2     3     4     5     6     7     8     9     10 (minutes)
-//    |-----------------------------|-----------------------------|--
-//            x                x         x    x        x      x       (syncStartEvents)
-//          |                                       |                 (ScreenIsOnEvent)
-//                   |     |                                          (ScreenIsOffEvent)
-//                                                        |           (ScreenUnknownEvent)
-//    */
-//    // Initialize log events - first bucket.
-//    int appUid = 123;
-//    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(appUid, "App1")};
-//    std::vector<std::unique_ptr<LogEvent>> events;
-//    events.push_back(
-//            CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
-//                                          bucketStartTimeNs + 50 * NS_PER_SEC));  // 1:00
-//    events.push_back(CreateSyncStartEvent(attributions1, "sync_name",
-//                                          bucketStartTimeNs + 75 * NS_PER_SEC));  // 1:25
-//    events.push_back(
-//            CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
-//                                          bucketStartTimeNs + 150 * NS_PER_SEC));  // 2:40
-//    events.push_back(
-//            CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
-//                                          bucketStartTimeNs + 200 * NS_PER_SEC));  // 3:30
-//    events.push_back(CreateSyncStartEvent(attributions1, "sync_name",
-//                                          bucketStartTimeNs + 250 * NS_PER_SEC));  // 4:20
-//
-//    // Initialize log events - second bucket.
-//    events.push_back(CreateSyncStartEvent(attributions1, "sync_name",
-//                                          bucketStartTimeNs + 350 * NS_PER_SEC));  // 6:00
-//    events.push_back(CreateSyncStartEvent(attributions1, "sync_name",
-//                                          bucketStartTimeNs + 400 * NS_PER_SEC));  // 6:50
-//    events.push_back(
-//            CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
-//                                          bucketStartTimeNs + 450 * NS_PER_SEC));  // 7:40
-//    events.push_back(CreateSyncStartEvent(attributions1, "sync_name",
-//                                          bucketStartTimeNs + 475 * NS_PER_SEC));  // 8:05
-//    events.push_back(
-//            CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN,
-//                                          bucketStartTimeNs + 500 * NS_PER_SEC));  // 8:30
-//    events.push_back(CreateSyncStartEvent(attributions1, "sync_name",
-//                                          bucketStartTimeNs + 520 * NS_PER_SEC));  // 8:50
-//
-//    // Send log events to StatsLogProcessor.
-//    for (auto& event : events) {
-//        processor->OnLogEvent(event.get());
-//    }
-//
-//    // Check dump report.
-//    vector<uint8_t> buffer;
-//    ConfigMetricsReportList reports;
-//    processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP,
-//                            FAST, &buffer);
-//    EXPECT_GT(buffer.size(), 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStringInReport(&reports);
-//    backfillStartEndTimestamp(&reports);
-//
-//    EXPECT_EQ(1, reports.reports_size());
-//    EXPECT_EQ(1, reports.reports(0).metrics_size());
-//    EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics());
-//    EXPECT_EQ(3, reports.reports(0).metrics(0).count_metrics().data_size());
-//
-//    // For each CountMetricData, check StateValue info is correct and buckets
-//    // have correct counts.
-//    auto data = reports.reports(0).metrics(0).count_metrics().data(0);
-//    EXPECT_EQ(1, data.slice_by_state_size());
-//    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
-//    EXPECT_TRUE(data.slice_by_state(0).has_value());
-//    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value());
-//    EXPECT_EQ(2, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(1, data.bucket_info(1).count());
-//
-//    data = reports.reports(0).metrics(0).count_metrics().data(1);
-//    EXPECT_EQ(1, data.slice_by_state_size());
-//    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
-//    EXPECT_TRUE(data.slice_by_state(0).has_value());
-//    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN, data.slice_by_state(0).value());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//
-//    data = reports.reports(0).metrics(0).count_metrics().data(2);
-//    EXPECT_EQ(1, data.slice_by_state_size());
-//    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
-//    EXPECT_TRUE(data.slice_by_state(0).has_value());
-//    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value());
-//    EXPECT_EQ(2, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(2, data.bucket_info(1).count());
-//}
-//
-///**
-// * Test a count metric that has one slice_by_state with a mapping and no
-// * primary fields.
-// *
-// * Once the CountMetricProducer is initialized, it has one atom id in
-// * mSlicedStateAtoms and has one entry per state value in mStateGroupMap.
-// *
-// * One StateTracker tracks the state atom, and it has one listener which is the
-// * CountMetricProducer that was initialized.
-// */
-//TEST(CountMetricE2eTest, TestSlicedStateWithMap) {
-//    // Initialize config.
-//    StatsdConfig config;
-//    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
-//
-//    auto syncStartMatcher = CreateSyncStartAtomMatcher();
-//    *config.add_atom_matcher() = syncStartMatcher;
-//
-//    auto state = CreateScreenStateWithOnOffMap();
-//    *config.add_state() = state;
-//
-//    // Create count metric that slices by screen state with on/off map.
-//    int64_t metricId = 123456;
-//    auto countMetric = config.add_count_metric();
-//    countMetric->set_id(metricId);
-//    countMetric->set_what(syncStartMatcher.id());
-//    countMetric->set_bucket(TimeUnit::FIVE_MINUTES);
-//    countMetric->add_slice_by_state(state.id());
-//
-//    // Initialize StatsLogProcessor.
-//    const uint64_t bucketStartTimeNs = 10000000000;  // 0:10
-//    const uint64_t bucketSizeNs =
-//            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
-//    int uid = 12345;
-//    int64_t cfgId = 98765;
-//    ConfigKey cfgKey(uid, cfgId);
-//    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-//
-//    // Check that StateTrackers were initialized correctly.
-//    EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
-//    EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
-//
-//    // Check that CountMetricProducer was initialized correctly.
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
-//    EXPECT_TRUE(metricsManager->isConfigValid());
-//    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
-//    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
-//    EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
-//    EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID);
-//    EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1);
-//
-//    StateMap map = state.map();
-//    for (auto group : map.group()) {
-//        for (auto value : group.value()) {
-//            EXPECT_EQ(metricProducer->mStateGroupMap[SCREEN_STATE_ATOM_ID][value],
-//                      group.group_id());
-//        }
-//    }
-//
-//    /*
-//               bucket #1                      bucket #2
-//    |     1     2     3     4     5     6     7     8     9     10 (minutes)
-//    |-----------------------------|-----------------------------|--
-//      x   x     x       x    x   x      x         x         x       (syncStartEvents)
-//     -----------------------------------------------------------SCREEN_OFF events
-//       |                                                            (ScreenStateUnknownEvent = 0)
-//             |                  |                                   (ScreenStateOffEvent = 1)
-//                          |                                         (ScreenStateDozeEvent = 3)
-//                                                |                   (ScreenStateDozeSuspendEvent = 4)
-//     -----------------------------------------------------------SCREEN_ON events
-//                   |                                       |        (ScreenStateOnEvent = 2)
-//                      |                                             (ScreenStateVrEvent = 5)
-//                                            |                       (ScreenStateOnSuspendEvent = 6)
-//    */
-//    // Initialize log events - first bucket.
-//    int appUid = 123;
-//    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(appUid, "App1")};
-//
-//    std::vector<std::unique_ptr<LogEvent>> events;
-//    events.push_back(CreateSyncStartEvent(attributions1, "sync_name",
-//                                          bucketStartTimeNs + 20 * NS_PER_SEC));  // 0:30
-//    events.push_back(
-//            CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN,
-//                                          bucketStartTimeNs + 30 * NS_PER_SEC));  // 0:40
-//    events.push_back(CreateSyncStartEvent(attributions1, "sync_name",
-//                                          bucketStartTimeNs + 60 * NS_PER_SEC));  // 1:10
-//    events.push_back(
-//            CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
-//                                          bucketStartTimeNs + 90 * NS_PER_SEC));  // 1:40
-//    events.push_back(CreateSyncStartEvent(attributions1, "sync_name",
-//                                          bucketStartTimeNs + 120 * NS_PER_SEC));  // 2:10
-//    events.push_back(
-//            CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
-//                                          bucketStartTimeNs + 150 * NS_PER_SEC));  // 2:40
-//    events.push_back(
-//            CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_VR,
-//                                          bucketStartTimeNs + 180 * NS_PER_SEC));  // 3:10
-//    events.push_back(CreateSyncStartEvent(attributions1, "sync_name",
-//                                          bucketStartTimeNs + 200 * NS_PER_SEC));  // 3:30
-//    events.push_back(
-//            CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE,
-//                                          bucketStartTimeNs + 210 * NS_PER_SEC));  // 3:40
-//    events.push_back(CreateSyncStartEvent(attributions1, "sync_name",
-//                                          bucketStartTimeNs + 250 * NS_PER_SEC));  // 4:20
-//    events.push_back(
-//            CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
-//                                          bucketStartTimeNs + 280 * NS_PER_SEC));  // 4:50
-//    events.push_back(CreateSyncStartEvent(attributions1, "sync_name",
-//                                          bucketStartTimeNs + 285 * NS_PER_SEC));  // 4:55
-//
-//    // Initialize log events - second bucket.
-//    events.push_back(CreateSyncStartEvent(attributions1, "sync_name",
-//                                          bucketStartTimeNs + 360 * NS_PER_SEC));  // 6:10
-//    events.push_back(
-//            CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND,
-//                                          bucketStartTimeNs + 390 * NS_PER_SEC));  // 6:40
-//    events.push_back(CreateScreenStateChangedEvent(
-//            android::view::DisplayStateEnum::DISPLAY_STATE_DOZE_SUSPEND,
-//            bucketStartTimeNs + 430 * NS_PER_SEC));  // 7:20
-//    events.push_back(CreateSyncStartEvent(attributions1, "sync_name",
-//                                          bucketStartTimeNs + 440 * NS_PER_SEC));  // 7:30
-//    events.push_back(
-//            CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
-//                                          bucketStartTimeNs + 540 * NS_PER_SEC));  // 9:10
-//    events.push_back(CreateSyncStartEvent(attributions1, "sync_name",
-//                                          bucketStartTimeNs + 570 * NS_PER_SEC));  // 9:40
-//
-//    // Send log events to StatsLogProcessor.
-//    for (auto& event : events) {
-//        processor->OnLogEvent(event.get());
-//    }
-//
-//    // Check dump report.
-//    vector<uint8_t> buffer;
-//    ConfigMetricsReportList reports;
-//    processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP,
-//                            FAST, &buffer);
-//    EXPECT_GT(buffer.size(), 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStringInReport(&reports);
-//    backfillStartEndTimestamp(&reports);
-//
-//    EXPECT_EQ(1, reports.reports_size());
-//    EXPECT_EQ(1, reports.reports(0).metrics_size());
-//    EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics());
-//    EXPECT_EQ(3, reports.reports(0).metrics(0).count_metrics().data_size());
-//
-//    // For each CountMetricData, check StateValue info is correct and buckets
-//    // have correct counts.
-//    auto data = reports.reports(0).metrics(0).count_metrics().data(0);
-//    EXPECT_EQ(1, data.slice_by_state_size());
-//    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
-//    EXPECT_TRUE(data.slice_by_state(0).has_value());
-//    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//
-//    data = reports.reports(0).metrics(0).count_metrics().data(1);
-//    EXPECT_EQ(1, data.slice_by_state_size());
-//    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
-//    EXPECT_TRUE(data.slice_by_state(0).has_group_id());
-//    EXPECT_EQ(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id());
-//    EXPECT_EQ(2, data.bucket_info_size());
-//    EXPECT_EQ(4, data.bucket_info(0).count());
-//    EXPECT_EQ(2, data.bucket_info(1).count());
-//
-//    data = reports.reports(0).metrics(0).count_metrics().data(2);
-//    EXPECT_EQ(1, data.slice_by_state_size());
-//    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
-//    EXPECT_TRUE(data.slice_by_state(0).has_group_id());
-//    EXPECT_EQ(StringToId("SCREEN_ON"), data.slice_by_state(0).group_id());
-//    EXPECT_EQ(2, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(1, data.bucket_info(1).count());
-//}
-//
-///**
-// * Test a count metric that has one slice_by_state with a primary field.
-//
-// * Once the CountMetricProducer is initialized, it should have one
-// * MetricStateLink stored. State querying using a non-empty primary key
-// * should also work as intended.
-// */
-//TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields) {
-//    // Initialize config.
-//    StatsdConfig config;
-//    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
-//
-//    auto appCrashMatcher =
-//            CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", android::util::APP_CRASH_OCCURRED);
-//    *config.add_atom_matcher() = appCrashMatcher;
-//
-//    auto state = CreateUidProcessState();
-//    *config.add_state() = state;
-//
-//    // Create count metric that slices by uid process state.
-//    int64_t metricId = 123456;
-//    auto countMetric = config.add_count_metric();
-//    countMetric->set_id(metricId);
-//    countMetric->set_what(appCrashMatcher.id());
-//    countMetric->set_bucket(TimeUnit::FIVE_MINUTES);
-//    countMetric->add_slice_by_state(state.id());
-//    MetricStateLink* stateLink = countMetric->add_state_link();
-//    stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
-//    auto fieldsInWhat = stateLink->mutable_fields_in_what();
-//    *fieldsInWhat = CreateDimensions(android::util::APP_CRASH_OCCURRED, {1 /* uid */});
-//    auto fieldsInState = stateLink->mutable_fields_in_state();
-//    *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
-//
-//    // Initialize StatsLogProcessor.
-//    const uint64_t bucketStartTimeNs = 10000000000;  // 0:10
-//    const uint64_t bucketSizeNs =
-//            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
-//    int uid = 12345;
-//    int64_t cfgId = 98765;
-//    ConfigKey cfgKey(uid, cfgId);
-//    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-//
-//    // Check that StateTrackers were initialized correctly.
-//    EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
-//    EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
-//
-//    // Check that CountMetricProducer was initialized correctly.
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
-//    EXPECT_TRUE(metricsManager->isConfigValid());
-//    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
-//    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
-//    EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
-//    EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), UID_PROCESS_STATE_ATOM_ID);
-//    EXPECT_EQ(metricProducer->mStateGroupMap.size(), 0);
-//    EXPECT_EQ(metricProducer->mMetric2StateLinks.size(), 1);
-//
-//    /*
-//    NOTE: "1" or "2" represents the uid associated with the state/app crash event
-//               bucket #1                      bucket #2
-//    |     1     2     3     4     5     6     7     8     9     10
-//    |-----------------------------|-----------------------------|--
-//      1   1     1       1    1   2      1         1          2      (AppCrashEvents)
-//     -----------------------------------------------------------PROCESS STATE events
-//             1                  2                                   (ProcessStateTopEvent = 1002)
-//                          1                 1                       (ProcessStateForegroundServiceEvent = 1003)
-//                                                2                   (ProcessStateImportantBackgroundEvent = 1006)
-//        1          1                                       1        (ProcessStateImportantForegroundEvent = 1005)
-//
-//    Based on the diagram above, an AppCrashEvent querying for process state value would return:
-//    - StateTracker::kStateUnknown
-//    - Important foreground
-//    - Top
-//    - Important foreground
-//    - Foreground service
-//    - Top (both the app crash and state still have matching uid = 2)
-//
-//    - Foreground service
-//    - Foreground service
-//    - Important background
-//    */
-//    // Initialize log events - first bucket.
-//    std::vector<std::unique_ptr<LogEvent>> events;
-//    events.push_back(
-//            CreateAppCrashOccurredEvent(1 /* uid */, bucketStartTimeNs + 20 * NS_PER_SEC));  // 0:30
-//    events.push_back(CreateUidProcessStateChangedEvent(
-//            1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
-//            bucketStartTimeNs + 30 * NS_PER_SEC));  // 0:40
-//    events.push_back(
-//            CreateAppCrashOccurredEvent(1 /* uid */, bucketStartTimeNs + 60 * NS_PER_SEC));  // 1:10
-//    events.push_back(CreateUidProcessStateChangedEvent(
-//            1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP,
-//            bucketStartTimeNs + 90 * NS_PER_SEC));  // 1:40
-//    events.push_back(CreateAppCrashOccurredEvent(1 /* uid */,
-//                                                 bucketStartTimeNs + 120 * NS_PER_SEC));  // 2:10
-//    events.push_back(CreateUidProcessStateChangedEvent(
-//            1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
-//            bucketStartTimeNs + 150 * NS_PER_SEC));  // 2:40
-//    events.push_back(CreateAppCrashOccurredEvent(1 /* uid */,
-//                                                 bucketStartTimeNs + 200 * NS_PER_SEC));  // 3:30
-//    events.push_back(CreateUidProcessStateChangedEvent(
-//            1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE,
-//            bucketStartTimeNs + 210 * NS_PER_SEC));  // 3:40
-//    events.push_back(CreateAppCrashOccurredEvent(1 /* uid */,
-//                                                 bucketStartTimeNs + 250 * NS_PER_SEC));  // 4:20
-//    events.push_back(CreateUidProcessStateChangedEvent(
-//            2 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP,
-//            bucketStartTimeNs + 280 * NS_PER_SEC));  // 4:50
-//    events.push_back(CreateAppCrashOccurredEvent(2 /* uid */,
-//                                                 bucketStartTimeNs + 285 * NS_PER_SEC));  // 4:55
-//
-//    // Initialize log events - second bucket.
-//    events.push_back(CreateAppCrashOccurredEvent(1 /* uid */,
-//                                                 bucketStartTimeNs + 360 * NS_PER_SEC));  // 6:10
-//    events.push_back(CreateUidProcessStateChangedEvent(
-//            1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE,
-//            bucketStartTimeNs + 390 * NS_PER_SEC));  // 6:40
-//    events.push_back(CreateUidProcessStateChangedEvent(
-//            2 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
-//            bucketStartTimeNs + 430 * NS_PER_SEC));  // 7:20
-//    events.push_back(CreateAppCrashOccurredEvent(1 /* uid */,
-//                                                 bucketStartTimeNs + 440 * NS_PER_SEC));  // 7:30
-//    events.push_back(CreateUidProcessStateChangedEvent(
-//            1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
-//            bucketStartTimeNs + 540 * NS_PER_SEC));  // 9:10
-//    events.push_back(CreateAppCrashOccurredEvent(2 /* uid */,
-//                                                 bucketStartTimeNs + 570 * NS_PER_SEC));  // 9:40
-//
-//    // Send log events to StatsLogProcessor.
-//    for (auto& event : events) {
-//        processor->OnLogEvent(event.get());
-//    }
-//
-//    // Check dump report.
-//    vector<uint8_t> buffer;
-//    ConfigMetricsReportList reports;
-//    processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP,
-//                            FAST, &buffer);
-//    EXPECT_GT(buffer.size(), 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStringInReport(&reports);
-//    backfillStartEndTimestamp(&reports);
-//
-//    EXPECT_EQ(1, reports.reports_size());
-//    EXPECT_EQ(1, reports.reports(0).metrics_size());
-//    EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics());
-//    EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size());
-//
-//    // For each CountMetricData, check StateValue info is correct and buckets
-//    // have correct counts.
-//    auto data = reports.reports(0).metrics(0).count_metrics().data(0);
-//    EXPECT_EQ(1, data.slice_by_state_size());
-//    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
-//    EXPECT_TRUE(data.slice_by_state(0).has_value());
-//    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, data.slice_by_state(0).value());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//
-//    data = reports.reports(0).metrics(0).count_metrics().data(1);
-//    EXPECT_EQ(1, data.slice_by_state_size());
-//    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
-//    EXPECT_TRUE(data.slice_by_state(0).has_value());
-//    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//
-//    data = reports.reports(0).metrics(0).count_metrics().data(2);
-//    EXPECT_EQ(1, data.slice_by_state_size());
-//    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
-//    EXPECT_TRUE(data.slice_by_state(0).has_value());
-//    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(0).value());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(2, data.bucket_info(0).count());
-//
-//    data = reports.reports(0).metrics(0).count_metrics().data(3);
-//    EXPECT_EQ(1, data.slice_by_state_size());
-//    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
-//    EXPECT_TRUE(data.slice_by_state(0).has_value());
-//    EXPECT_EQ(android::app::PROCESS_STATE_TOP, data.slice_by_state(0).value());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(2, data.bucket_info(0).count());
-//
-//    data = reports.reports(0).metrics(0).count_metrics().data(4);
-//    EXPECT_EQ(1, data.slice_by_state_size());
-//    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
-//    EXPECT_TRUE(data.slice_by_state(0).has_value());
-//    EXPECT_EQ(android::app::PROCESS_STATE_FOREGROUND_SERVICE, data.slice_by_state(0).value());
-//    EXPECT_EQ(2, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(2, data.bucket_info(1).count());
-//}
-//
-//TEST(CountMetricE2eTest, TestMultipleSlicedStates) {
-//    // Initialize config.
-//    StatsdConfig config;
-//    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
-//
-//    auto appCrashMatcher =
-//            CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", android::util::APP_CRASH_OCCURRED);
-//    *config.add_atom_matcher() = appCrashMatcher;
-//
-//    auto state1 = CreateScreenStateWithOnOffMap();
-//    *config.add_state() = state1;
-//    auto state2 = CreateUidProcessState();
-//    *config.add_state() = state2;
-//
-//    // Create count metric that slices by screen state with on/off map and
-//    // slices by uid process state.
-//    int64_t metricId = 123456;
-//    auto countMetric = config.add_count_metric();
-//    countMetric->set_id(metricId);
-//    countMetric->set_what(appCrashMatcher.id());
-//    countMetric->set_bucket(TimeUnit::FIVE_MINUTES);
-//    countMetric->add_slice_by_state(state1.id());
-//    countMetric->add_slice_by_state(state2.id());
-//    MetricStateLink* stateLink = countMetric->add_state_link();
-//    stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
-//    auto fieldsInWhat = stateLink->mutable_fields_in_what();
-//    *fieldsInWhat = CreateDimensions(android::util::APP_CRASH_OCCURRED, {1 /* uid */});
-//    auto fieldsInState = stateLink->mutable_fields_in_state();
-//    *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
-//
-//    // Initialize StatsLogProcessor.
-//    const uint64_t bucketStartTimeNs = 10000000000;  // 0:10
-//    const uint64_t bucketSizeNs =
-//            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
-//    int uid = 12345;
-//    int64_t cfgId = 98765;
-//    ConfigKey cfgKey(uid, cfgId);
-//    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-//
-//    // Check that StateTrackers were properly initialized.
-//    EXPECT_EQ(2, StateManager::getInstance().getStateTrackersCount());
-//    EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
-//    EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
-//
-//    // Check that CountMetricProducer was initialized correctly.
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
-//    EXPECT_TRUE(metricsManager->isConfigValid());
-//    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
-//    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
-//    EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 2);
-//    EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID);
-//    EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(1), UID_PROCESS_STATE_ATOM_ID);
-//    EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1);
-//    EXPECT_EQ(metricProducer->mMetric2StateLinks.size(), 1);
-//
-//    StateMap map = state1.map();
-//    for (auto group : map.group()) {
-//        for (auto value : group.value()) {
-//            EXPECT_EQ(metricProducer->mStateGroupMap[SCREEN_STATE_ATOM_ID][value],
-//                      group.group_id());
-//        }
-//    }
-//
-//    /*
-//               bucket #1                      bucket #2
-//    |     1     2     3     4     5     6     7     8     9     10 (minutes)
-//    |-----------------------------|-----------------------------|--
-//      1   1     1       1    1   2      1         1           2     (AppCrashEvents)
-//     -----------------------------------------------------------SCREEN_OFF events
-//       |                                                            (ScreenStateUnknownEvent = 0)
-//             |                                  |                   (ScreenStateOffEvent = 1)
-//                          |                                         (ScreenStateDozeEvent = 3)
-//     -----------------------------------------------------------SCREEN_ON events
-//                    |                                    |          (ScreenStateOnEvent = 2)
-//                                            |                       (ScreenStateOnSuspendEvent = 6)
-//     -----------------------------------------------------------PROCESS STATE events
-//             1                  2                                   (ProcessStateTopEvent = 1002)
-//                                          1                         (ProcessStateForegroundServiceEvent = 1003)
-//                                               2                    (ProcessStateImportantBackgroundEvent = 1006)
-//     1             1                                       1        (ProcessStateImportantForegroundEvent = 1005)
-//
-//     Based on the diagram above, Screen State / Process State pairs for each
-//     AppCrashEvent are:
-//     - StateTracker::kStateUnknown / important foreground
-//     - off / important foreground
-//     - off / Top
-//     - on / important foreground
-//     - off / important foreground
-//     - off / top
-//
-//     - off / important foreground
-//     - off / foreground service
-//     - on / important background
-//
-//    */
-//    // Initialize log events - first bucket.
-//    std::vector<std::unique_ptr<LogEvent>> events;
-//    events.push_back(CreateUidProcessStateChangedEvent(
-//            1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
-//            bucketStartTimeNs + 5 * NS_PER_SEC));  // 0:15
-//    events.push_back(
-//            CreateAppCrashOccurredEvent(1 /* uid */, bucketStartTimeNs + 20 * NS_PER_SEC));  // 0:30
-//    events.push_back(
-//            CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN,
-//                                          bucketStartTimeNs + 30 * NS_PER_SEC));  // 0:40
-//    events.push_back(
-//            CreateAppCrashOccurredEvent(1 /* uid */, bucketStartTimeNs + 60 * NS_PER_SEC));  // 1:10
-//    events.push_back(CreateUidProcessStateChangedEvent(
-//            1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP,
-//            bucketStartTimeNs + 90 * NS_PER_SEC));  // 1:40
-//    events.push_back(
-//            CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
-//                                          bucketStartTimeNs + 90 * NS_PER_SEC));  // 1:40
-//    events.push_back(CreateAppCrashOccurredEvent(1 /* uid */,
-//                                                 bucketStartTimeNs + 120 * NS_PER_SEC));  // 2:10
-//    events.push_back(CreateUidProcessStateChangedEvent(
-//            1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
-//            bucketStartTimeNs + 150 * NS_PER_SEC));  // 2:40
-//    events.push_back(
-//            CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
-//                                          bucketStartTimeNs + 160 * NS_PER_SEC));  // 2:50
-//    events.push_back(CreateAppCrashOccurredEvent(1 /* uid */,
-//                                                 bucketStartTimeNs + 200 * NS_PER_SEC));  // 3:30
-//    events.push_back(
-//            CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE,
-//                                          bucketStartTimeNs + 210 * NS_PER_SEC));  // 3:40
-//    events.push_back(CreateAppCrashOccurredEvent(1 /* uid */,
-//                                                 bucketStartTimeNs + 250 * NS_PER_SEC));  // 4:20
-//    events.push_back(CreateUidProcessStateChangedEvent(
-//            2 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP,
-//            bucketStartTimeNs + 280 * NS_PER_SEC));  // 4:50
-//    events.push_back(CreateAppCrashOccurredEvent(2 /* uid */,
-//                                                 bucketStartTimeNs + 285 * NS_PER_SEC));  // 4:55
-//
-//    // Initialize log events - second bucket.
-//    events.push_back(CreateAppCrashOccurredEvent(1 /* uid */,
-//                                                 bucketStartTimeNs + 360 * NS_PER_SEC));  // 6:10
-//    events.push_back(CreateUidProcessStateChangedEvent(
-//            1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE,
-//            bucketStartTimeNs + 380 * NS_PER_SEC));  // 6:30
-//    events.push_back(
-//            CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND,
-//                                          bucketStartTimeNs + 390 * NS_PER_SEC));  // 6:40
-//    events.push_back(CreateUidProcessStateChangedEvent(
-//            2 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
-//            bucketStartTimeNs + 420 * NS_PER_SEC));  // 7:10
-//    events.push_back(
-//            CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
-//                                          bucketStartTimeNs + 440 * NS_PER_SEC));  // 7:30
-//    events.push_back(CreateAppCrashOccurredEvent(1 /* uid */,
-//                                                 bucketStartTimeNs + 450 * NS_PER_SEC));  // 7:40
-//    events.push_back(
-//            CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
-//                                          bucketStartTimeNs + 520 * NS_PER_SEC));  // 8:50
-//    events.push_back(CreateUidProcessStateChangedEvent(
-//            1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
-//            bucketStartTimeNs + 540 * NS_PER_SEC));  // 9:10
-//    events.push_back(CreateAppCrashOccurredEvent(2 /* uid */,
-//                                                 bucketStartTimeNs + 570 * NS_PER_SEC));  // 9:40
-//
-//    // Send log events to StatsLogProcessor.
-//    for (auto& event : events) {
-//        processor->OnLogEvent(event.get());
-//    }
-//
-//    // Check dump report.
-//    vector<uint8_t> buffer;
-//    ConfigMetricsReportList reports;
-//    processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP,
-//                            FAST, &buffer);
-//    EXPECT_GT(buffer.size(), 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStringInReport(&reports);
-//    backfillStartEndTimestamp(&reports);
-//
-//    EXPECT_EQ(1, reports.reports_size());
-//    EXPECT_EQ(1, reports.reports(0).metrics_size());
-//    EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics());
-//    EXPECT_EQ(6, reports.reports(0).metrics(0).count_metrics().data_size());
-//
-//    // For each CountMetricData, check StateValue info is correct and buckets
-//    // have correct counts.
-//    auto data = reports.reports(0).metrics(0).count_metrics().data(0);
-//    EXPECT_EQ(2, data.slice_by_state_size());
-//    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
-//    EXPECT_TRUE(data.slice_by_state(0).has_group_id());
-//    EXPECT_EQ(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id());
-//    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id());
-//    EXPECT_TRUE(data.slice_by_state(1).has_value());
-//    EXPECT_EQ(android::app::PROCESS_STATE_FOREGROUND_SERVICE, data.slice_by_state(1).value());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//
-//    data = reports.reports(0).metrics(0).count_metrics().data(1);
-//    EXPECT_EQ(2, data.slice_by_state_size());
-//    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
-//    EXPECT_TRUE(data.slice_by_state(0).has_value());
-//    EXPECT_EQ(-1, data.slice_by_state(0).value());
-//    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id());
-//    EXPECT_TRUE(data.slice_by_state(1).has_value());
-//    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//
-//    data = reports.reports(0).metrics(0).count_metrics().data(2);
-//    EXPECT_EQ(2, data.slice_by_state_size());
-//    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
-//    EXPECT_TRUE(data.slice_by_state(0).has_group_id());
-//    EXPECT_EQ(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id());
-//    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id());
-//    EXPECT_TRUE(data.slice_by_state(1).has_value());
-//    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value());
-//    EXPECT_EQ(2, data.bucket_info_size());
-//    EXPECT_EQ(2, data.bucket_info(0).count());
-//    EXPECT_EQ(1, data.bucket_info(1).count());
-//
-//    data = reports.reports(0).metrics(0).count_metrics().data(3);
-//    EXPECT_EQ(2, data.slice_by_state_size());
-//    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
-//    EXPECT_TRUE(data.slice_by_state(0).has_group_id());
-//    EXPECT_EQ(StringToId("SCREEN_ON"), data.slice_by_state(0).group_id());
-//    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id());
-//    EXPECT_TRUE(data.slice_by_state(1).has_value());
-//    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//
-//    data = reports.reports(0).metrics(0).count_metrics().data(4);
-//    EXPECT_EQ(2, data.slice_by_state_size());
-//    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
-//    EXPECT_TRUE(data.slice_by_state(0).has_group_id());
-//    EXPECT_EQ(StringToId("SCREEN_ON"), data.slice_by_state(0).group_id());
-//    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id());
-//    EXPECT_TRUE(data.slice_by_state(1).has_value());
-//    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, data.slice_by_state(1).value());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//
-//    data = reports.reports(0).metrics(0).count_metrics().data(5);
-//    EXPECT_EQ(2, data.slice_by_state_size());
-//    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
-//    EXPECT_TRUE(data.slice_by_state(0).has_group_id());
-//    EXPECT_EQ(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id());
-//    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id());
-//    EXPECT_TRUE(data.slice_by_state(1).has_value());
-//    EXPECT_EQ(android::app::PROCESS_STATE_TOP, data.slice_by_state(1).value());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(2, data.bucket_info(0).count());
-//}
+/**
+* Test a count metric that has one slice_by_state with no primary fields.
+*
+* Once the CountMetricProducer is initialized, it has one atom id in
+* mSlicedStateAtoms and no entries in mStateGroupMap.
+
+* One StateTracker tracks the state atom, and it has one listener which is the
+* CountMetricProducer that was initialized.
+*/
+TEST(CountMetricE2eTest, TestSlicedState) {
+    // Initialize config.
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+
+    auto syncStartMatcher = CreateSyncStartAtomMatcher();
+    *config.add_atom_matcher() = syncStartMatcher;
+
+    auto state = CreateScreenState();
+    *config.add_state() = state;
+
+    // Create count metric that slices by screen state.
+    int64_t metricId = 123456;
+    auto countMetric = config.add_count_metric();
+    countMetric->set_id(metricId);
+    countMetric->set_what(syncStartMatcher.id());
+    countMetric->set_bucket(TimeUnit::FIVE_MINUTES);
+    countMetric->add_slice_by_state(state.id());
+
+    // Initialize StatsLogProcessor.
+    const uint64_t bucketStartTimeNs = 10000000000;  // 0:10
+    const uint64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+    int uid = 12345;
+    int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+
+    // Check that CountMetricProducer was initialized correctly.
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+    EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
+    EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID);
+    EXPECT_EQ(metricProducer->mStateGroupMap.size(), 0);
+
+    // Check that StateTrackers were initialized correctly.
+    EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+    EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
+
+    /*
+               bucket #1                      bucket #2
+    |     1     2     3     4     5     6     7     8     9     10 (minutes)
+    |-----------------------------|-----------------------------|--
+            x                x         x    x        x      x       (syncStartEvents)
+          |                                       |                 (ScreenIsOnEvent)
+                   |     |                                          (ScreenIsOffEvent)
+                                                        |           (ScreenUnknownEvent)
+    */
+    // Initialize log events - first bucket.
+    std::vector<int> attributionUids1 = {123};
+    std::vector<string> attributionTags1 = {"App1"};
+
+    std::vector<std::unique_ptr<LogEvent>> events;
+    events.push_back(CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 50 * NS_PER_SEC,
+            android::view::DisplayStateEnum::DISPLAY_STATE_ON));  // 1:00
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 75 * NS_PER_SEC, attributionUids1,
+                                          attributionTags1, "sync_name"));  // 1:25
+    events.push_back(CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 150 * NS_PER_SEC,
+            android::view::DisplayStateEnum::DISPLAY_STATE_OFF));  // 2:40
+    events.push_back(CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 200 * NS_PER_SEC,
+            android::view::DisplayStateEnum::DISPLAY_STATE_OFF));  // 3:30
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 250 * NS_PER_SEC, attributionUids1,
+                                          attributionTags1, "sync_name"));  // 4:20
+
+    // Initialize log events - second bucket.
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 350 * NS_PER_SEC, attributionUids1,
+                                          attributionTags1, "sync_name"));  // 6:00
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 400 * NS_PER_SEC, attributionUids1,
+                                          attributionTags1, "sync_name"));  // 6:50
+    events.push_back(CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 450 * NS_PER_SEC,
+            android::view::DisplayStateEnum::DISPLAY_STATE_ON));  // 7:40
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 475 * NS_PER_SEC, attributionUids1,
+                                          attributionTags1, "sync_name"));  // 8:05
+    events.push_back(CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 500 * NS_PER_SEC,
+            android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN));  // 8:30
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 520 * NS_PER_SEC, attributionUids1,
+                                          attributionTags1, "sync_name"));  // 8:50
+
+    // Send log events to StatsLogProcessor.
+    for (auto& event : events) {
+        processor->OnLogEvent(event.get());
+    }
+
+    // Check dump report.
+    vector<uint8_t> buffer;
+    ConfigMetricsReportList reports;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP,
+                            FAST, &buffer);
+    EXPECT_GT(buffer.size(), 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics());
+    EXPECT_EQ(3, reports.reports(0).metrics(0).count_metrics().data_size());
+
+    // For each CountMetricData, check StateValue info is correct and buckets
+    // have correct counts.
+    auto data = reports.reports(0).metrics(0).count_metrics().data(0);
+    EXPECT_EQ(1, data.slice_by_state_size());
+    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_TRUE(data.slice_by_state(0).has_value());
+    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value());
+    EXPECT_EQ(2, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(1, data.bucket_info(1).count());
+
+    data = reports.reports(0).metrics(0).count_metrics().data(1);
+    EXPECT_EQ(1, data.slice_by_state_size());
+    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_TRUE(data.slice_by_state(0).has_value());
+    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN,
+              data.slice_by_state(0).value());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+
+    data = reports.reports(0).metrics(0).count_metrics().data(2);
+    EXPECT_EQ(1, data.slice_by_state_size());
+    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_TRUE(data.slice_by_state(0).has_value());
+    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value());
+    EXPECT_EQ(2, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(2, data.bucket_info(1).count());
+}
+
+/**
+ * Test a count metric that has one slice_by_state with a mapping and no
+ * primary fields.
+ *
+ * Once the CountMetricProducer is initialized, it has one atom id in
+ * mSlicedStateAtoms and has one entry per state value in mStateGroupMap.
+ *
+ * One StateTracker tracks the state atom, and it has one listener which is the
+ * CountMetricProducer that was initialized.
+ */
+TEST(CountMetricE2eTest, TestSlicedStateWithMap) {
+    // Initialize config.
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+
+    auto syncStartMatcher = CreateSyncStartAtomMatcher();
+    *config.add_atom_matcher() = syncStartMatcher;
+
+    auto state = CreateScreenStateWithOnOffMap();
+    *config.add_state() = state;
+
+    // Create count metric that slices by screen state with on/off map.
+    int64_t metricId = 123456;
+    auto countMetric = config.add_count_metric();
+    countMetric->set_id(metricId);
+    countMetric->set_what(syncStartMatcher.id());
+    countMetric->set_bucket(TimeUnit::FIVE_MINUTES);
+    countMetric->add_slice_by_state(state.id());
+
+    // Initialize StatsLogProcessor.
+    const uint64_t bucketStartTimeNs = 10000000000;  // 0:10
+    const uint64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+    int uid = 12345;
+    int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+
+    // Check that StateTrackers were initialized correctly.
+    EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+    EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
+
+    // Check that CountMetricProducer was initialized correctly.
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+    EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
+    EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID);
+    EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1);
+
+    StateMap map = state.map();
+    for (auto group : map.group()) {
+        for (auto value : group.value()) {
+            EXPECT_EQ(metricProducer->mStateGroupMap[SCREEN_STATE_ATOM_ID][value],
+                      group.group_id());
+        }
+    }
+
+    /*
+               bucket #1                      bucket #2
+    |     1     2     3     4     5     6     7     8     9     10 (minutes)
+    |-----------------------------|-----------------------------|--
+      x   x     x       x    x   x      x         x         x       (syncStartEvents)
+     -----------------------------------------------------------SCREEN_OFF events
+       |                                                            (ScreenStateUnknownEvent = 0)
+             |                  |                                   (ScreenStateOffEvent = 1)
+                          |                                         (ScreenStateDozeEvent = 3)
+                                                |                   (ScreenStateDozeSuspendEvent =
+    4)
+     -----------------------------------------------------------SCREEN_ON events
+                   |                                       |        (ScreenStateOnEvent = 2)
+                      |                                             (ScreenStateVrEvent = 5)
+                                            |                       (ScreenStateOnSuspendEvent = 6)
+    */
+    // Initialize log events - first bucket.
+    std::vector<int> attributionUids1 = {123};
+    std::vector<string> attributionTags1 = {"App1"};
+
+    std::vector<std::unique_ptr<LogEvent>> events;
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 20 * NS_PER_SEC, attributionUids1,
+                                          attributionTags1, "sync_name"));  // 0:30
+    events.push_back(CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 30 * NS_PER_SEC,
+            android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN));  // 0:40
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 60 * NS_PER_SEC, attributionUids1,
+                                          attributionTags1, "sync_name"));  // 1:10
+    events.push_back(CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 90 * NS_PER_SEC,
+            android::view::DisplayStateEnum::DISPLAY_STATE_OFF));  // 1:40
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 120 * NS_PER_SEC, attributionUids1,
+                                          attributionTags1, "sync_name"));  // 2:10
+    events.push_back(CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 150 * NS_PER_SEC,
+            android::view::DisplayStateEnum::DISPLAY_STATE_ON));  // 2:40
+    events.push_back(CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 180 * NS_PER_SEC,
+            android::view::DisplayStateEnum::DISPLAY_STATE_VR));  // 3:10
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 200 * NS_PER_SEC, attributionUids1,
+                                          attributionTags1, "sync_name"));  // 3:30
+    events.push_back(CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 210 * NS_PER_SEC,
+            android::view::DisplayStateEnum::DISPLAY_STATE_DOZE));  // 3:40
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 250 * NS_PER_SEC, attributionUids1,
+                                          attributionTags1, "sync_name"));  // 4:20
+    events.push_back(CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 280 * NS_PER_SEC,
+            android::view::DisplayStateEnum::DISPLAY_STATE_OFF));  // 4:50
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 285 * NS_PER_SEC, attributionUids1,
+                                          attributionTags1, "sync_name"));  // 4:55
+
+    // Initialize log events - second bucket.
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 360 * NS_PER_SEC, attributionUids1,
+                                          attributionTags1, "sync_name"));  // 6:10
+    events.push_back(CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 390 * NS_PER_SEC,
+            android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND));  // 6:40
+    events.push_back(CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 430 * NS_PER_SEC,
+            android::view::DisplayStateEnum::DISPLAY_STATE_DOZE_SUSPEND));  // 7:20
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 440 * NS_PER_SEC, attributionUids1,
+                                          attributionTags1, "sync_name"));  // 7:30
+    events.push_back(CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 540 * NS_PER_SEC,
+            android::view::DisplayStateEnum::DISPLAY_STATE_ON));  // 9:10
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 570 * NS_PER_SEC, attributionUids1,
+                                          attributionTags1, "sync_name"));  // 9:40
+
+    // Send log events to StatsLogProcessor.
+    for (auto& event : events) {
+        processor->OnLogEvent(event.get());
+    }
+
+    // Check dump report.
+    vector<uint8_t> buffer;
+    ConfigMetricsReportList reports;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP,
+                            FAST, &buffer);
+    EXPECT_GT(buffer.size(), 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics());
+    EXPECT_EQ(3, reports.reports(0).metrics(0).count_metrics().data_size());
+
+    // For each CountMetricData, check StateValue info is correct and buckets
+    // have correct counts.
+    auto data = reports.reports(0).metrics(0).count_metrics().data(0);
+    EXPECT_EQ(1, data.slice_by_state_size());
+    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_TRUE(data.slice_by_state(0).has_value());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+
+    data = reports.reports(0).metrics(0).count_metrics().data(1);
+    EXPECT_EQ(1, data.slice_by_state_size());
+    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_TRUE(data.slice_by_state(0).has_group_id());
+    EXPECT_EQ(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id());
+    EXPECT_EQ(2, data.bucket_info_size());
+    EXPECT_EQ(4, data.bucket_info(0).count());
+    EXPECT_EQ(2, data.bucket_info(1).count());
+
+    data = reports.reports(0).metrics(0).count_metrics().data(2);
+    EXPECT_EQ(1, data.slice_by_state_size());
+    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_TRUE(data.slice_by_state(0).has_group_id());
+    EXPECT_EQ(StringToId("SCREEN_ON"), data.slice_by_state(0).group_id());
+    EXPECT_EQ(2, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(1, data.bucket_info(1).count());
+}
+
+/**
+* Test a count metric that has one slice_by_state with a primary field.
+
+* Once the CountMetricProducer is initialized, it should have one
+* MetricStateLink stored. State querying using a non-empty primary key
+* should also work as intended.
+*/
+TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields) {
+    // Initialize config.
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+
+    auto appCrashMatcher =
+            CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", android::util::APP_CRASH_OCCURRED);
+    *config.add_atom_matcher() = appCrashMatcher;
+
+    auto state = CreateUidProcessState();
+    *config.add_state() = state;
+
+    // Create count metric that slices by uid process state.
+    int64_t metricId = 123456;
+    auto countMetric = config.add_count_metric();
+    countMetric->set_id(metricId);
+    countMetric->set_what(appCrashMatcher.id());
+    countMetric->set_bucket(TimeUnit::FIVE_MINUTES);
+    countMetric->add_slice_by_state(state.id());
+    MetricStateLink* stateLink = countMetric->add_state_link();
+    stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
+    auto fieldsInWhat = stateLink->mutable_fields_in_what();
+    *fieldsInWhat = CreateDimensions(android::util::APP_CRASH_OCCURRED, {1 /*uid*/});
+    auto fieldsInState = stateLink->mutable_fields_in_state();
+    *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /*uid*/});
+
+    // Initialize StatsLogProcessor.
+    const uint64_t bucketStartTimeNs = 10000000000;  // 0:10
+    const uint64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+    int uid = 12345;
+    int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+
+    // Check that StateTrackers were initialized correctly.
+    EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+    EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
+
+    // Check that CountMetricProducer was initialized correctly.
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+    EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
+    EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), UID_PROCESS_STATE_ATOM_ID);
+    EXPECT_EQ(metricProducer->mStateGroupMap.size(), 0);
+    EXPECT_EQ(metricProducer->mMetric2StateLinks.size(), 1);
+
+    /*
+    NOTE: "1" or "2" represents the uid associated with the state/app crash event
+               bucket #1               bucket #2
+    |    1    2    3    4    5    6    7    8    9    10
+    |------------------------|-------------------------|--
+      1  1    1      1   1  2     1        1        2    (AppCrashEvents)
+     -----------------------------------------------------PROCESS STATE events
+           1               2                             (TopEvent = 1002)
+                       1             1                   (ForegroundServiceEvent = 1003)
+                                         2               (ImportantBackgroundEvent = 1006)
+       1          1                               1      (ImportantForegroundEvent = 1005)
+
+    Based on the diagram above, an AppCrashEvent querying for process state value would return:
+    - StateTracker::kStateUnknown
+    - Important foreground
+    - Top
+    - Important foreground
+    - Foreground service
+    - Top (both the app crash and state still have matching uid = 2)
+
+    - Foreground service
+    - Foreground service
+    - Important background
+    */
+    // Initialize log events - first bucket.
+    std::vector<std::unique_ptr<LogEvent>> events;
+    events.push_back(
+            CreateAppCrashOccurredEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /*uid*/));  // 0:30
+    events.push_back(CreateUidProcessStateChangedEvent(
+            bucketStartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/,
+            android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND));  // 0:40
+    events.push_back(
+            CreateAppCrashOccurredEvent(bucketStartTimeNs + 60 * NS_PER_SEC, 1 /*uid*/));  // 1:10
+    events.push_back(CreateUidProcessStateChangedEvent(
+            bucketStartTimeNs + 90 * NS_PER_SEC, 1 /*uid*/,
+            android::app::ProcessStateEnum::PROCESS_STATE_TOP));  // 1:40
+    events.push_back(
+            CreateAppCrashOccurredEvent(bucketStartTimeNs + 120 * NS_PER_SEC, 1 /*uid*/));  // 2:10
+    events.push_back(CreateUidProcessStateChangedEvent(
+            bucketStartTimeNs + 150 * NS_PER_SEC, 1 /*uid*/,
+            android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND));  // 2:40
+    events.push_back(
+            CreateAppCrashOccurredEvent(bucketStartTimeNs + 200 * NS_PER_SEC, 1 /*uid*/));  // 3:30
+    events.push_back(CreateUidProcessStateChangedEvent(
+            bucketStartTimeNs + 210 * NS_PER_SEC, 1 /*uid*/,
+            android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE));  // 3:40
+    events.push_back(
+            CreateAppCrashOccurredEvent(bucketStartTimeNs + 250 * NS_PER_SEC, 1 /*uid*/));  // 4:20
+    events.push_back(CreateUidProcessStateChangedEvent(
+            bucketStartTimeNs + 280 * NS_PER_SEC, 2 /*uid*/,
+            android::app::ProcessStateEnum::PROCESS_STATE_TOP));  // 4:50
+    events.push_back(
+            CreateAppCrashOccurredEvent(bucketStartTimeNs + 285 * NS_PER_SEC, 2 /*uid*/));  // 4:55
+
+    // Initialize log events - second bucket.
+    events.push_back(
+            CreateAppCrashOccurredEvent(bucketStartTimeNs + 360 * NS_PER_SEC, 1 /*uid*/));  // 6:10
+    events.push_back(CreateUidProcessStateChangedEvent(
+            bucketStartTimeNs + 390 * NS_PER_SEC, 1 /*uid*/,
+            android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE));  // 6:40
+    events.push_back(CreateUidProcessStateChangedEvent(
+            bucketStartTimeNs + 430 * NS_PER_SEC, 2 /*uid*/,
+            android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND));  // 7:20
+    events.push_back(
+            CreateAppCrashOccurredEvent(bucketStartTimeNs + 440 * NS_PER_SEC, 1 /*uid*/));  // 7:30
+    events.push_back(CreateUidProcessStateChangedEvent(
+            bucketStartTimeNs + 540 * NS_PER_SEC, 1 /*uid*/,
+            android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND));  // 9:10
+    events.push_back(
+            CreateAppCrashOccurredEvent(bucketStartTimeNs + 570 * NS_PER_SEC, 2 /*uid*/));  // 9:40
+
+    // Send log events to StatsLogProcessor.
+    for (auto& event : events) {
+        processor->OnLogEvent(event.get());
+    }
+
+    // Check dump report.
+    vector<uint8_t> buffer;
+    ConfigMetricsReportList reports;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP,
+                            FAST, &buffer);
+    EXPECT_GT(buffer.size(), 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics());
+    EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size());
+
+    // For each CountMetricData, check StateValue info is correct and buckets
+    // have correct counts.
+    auto data = reports.reports(0).metrics(0).count_metrics().data(0);
+    EXPECT_EQ(1, data.slice_by_state_size());
+    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_TRUE(data.slice_by_state(0).has_value());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, data.slice_by_state(0).value());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+
+    data = reports.reports(0).metrics(0).count_metrics().data(1);
+    EXPECT_EQ(1, data.slice_by_state_size());
+    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_TRUE(data.slice_by_state(0).has_value());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+
+    data = reports.reports(0).metrics(0).count_metrics().data(2);
+    EXPECT_EQ(1, data.slice_by_state_size());
+    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_TRUE(data.slice_by_state(0).has_value());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(0).value());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(2, data.bucket_info(0).count());
+
+    data = reports.reports(0).metrics(0).count_metrics().data(3);
+    EXPECT_EQ(1, data.slice_by_state_size());
+    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_TRUE(data.slice_by_state(0).has_value());
+    EXPECT_EQ(android::app::PROCESS_STATE_TOP, data.slice_by_state(0).value());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(2, data.bucket_info(0).count());
+
+    data = reports.reports(0).metrics(0).count_metrics().data(4);
+    EXPECT_EQ(1, data.slice_by_state_size());
+    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_TRUE(data.slice_by_state(0).has_value());
+    EXPECT_EQ(android::app::PROCESS_STATE_FOREGROUND_SERVICE, data.slice_by_state(0).value());
+    EXPECT_EQ(2, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(2, data.bucket_info(1).count());
+}
+
+TEST(CountMetricE2eTest, TestMultipleSlicedStates) {
+    // Initialize config.
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+
+    auto appCrashMatcher =
+            CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", android::util::APP_CRASH_OCCURRED);
+    *config.add_atom_matcher() = appCrashMatcher;
+
+    auto state1 = CreateScreenStateWithOnOffMap();
+    *config.add_state() = state1;
+    auto state2 = CreateUidProcessState();
+    *config.add_state() = state2;
+
+    // Create count metric that slices by screen state with on/off map and
+    // slices by uid process state.
+    int64_t metricId = 123456;
+    auto countMetric = config.add_count_metric();
+    countMetric->set_id(metricId);
+    countMetric->set_what(appCrashMatcher.id());
+    countMetric->set_bucket(TimeUnit::FIVE_MINUTES);
+    countMetric->add_slice_by_state(state1.id());
+    countMetric->add_slice_by_state(state2.id());
+    MetricStateLink* stateLink = countMetric->add_state_link();
+    stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
+    auto fieldsInWhat = stateLink->mutable_fields_in_what();
+    *fieldsInWhat = CreateDimensions(android::util::APP_CRASH_OCCURRED, {1 /*uid*/});
+    auto fieldsInState = stateLink->mutable_fields_in_state();
+    *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /*uid*/});
+
+    // Initialize StatsLogProcessor.
+    const uint64_t bucketStartTimeNs = 10000000000;  // 0:10
+    const uint64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+    int uid = 12345;
+    int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+
+    // Check that StateTrackers were properly initialized.
+    EXPECT_EQ(2, StateManager::getInstance().getStateTrackersCount());
+    EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
+    EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
+
+    // Check that CountMetricProducer was initialized correctly.
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+    EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 2);
+    EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID);
+    EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(1), UID_PROCESS_STATE_ATOM_ID);
+    EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1);
+    EXPECT_EQ(metricProducer->mMetric2StateLinks.size(), 1);
+
+    StateMap map = state1.map();
+    for (auto group : map.group()) {
+        for (auto value : group.value()) {
+            EXPECT_EQ(metricProducer->mStateGroupMap[SCREEN_STATE_ATOM_ID][value],
+                      group.group_id());
+        }
+    }
+
+    /*
+                 bucket #1                      bucket #2
+      |    1    2    3    4    5    6    7    8    9    10 (minutes)
+      |------------------------|------------------------|--
+        1  1    1     1    1  2     1        1         2   (AppCrashEvents)
+       ---------------------------------------------------SCREEN_OFF events
+         |                                                 (ScreenUnknownEvent = 0)
+             |                              |              (ScreenOffEvent = 1)
+                        |                                  (ScreenDozeEvent = 3)
+       ---------------------------------------------------SCREEN_ON events
+                   |                              |        (ScreenOnEvent = 2)
+                                        |                  (ScreenOnSuspendEvent = 6)
+       ---------------------------------------------------PROCESS STATE events
+             1               2                             (TopEvent = 1002)
+                                      1                    (ForegroundServiceEvent = 1003)
+                                            2              (ImportantBackgroundEvent = 1006)
+       1          1                                   1    (ImportantForegroundEvent = 1005)
+
+       Based on the diagram above, Screen State / Process State pairs for each
+       AppCrashEvent are:
+       - StateTracker::kStateUnknown / important foreground
+       - off / important foreground
+       - off / Top
+       - on / important foreground
+       - off / important foreground
+       - off / top
+
+       - off / important foreground
+       - off / foreground service
+       - on / important background
+
+      */
+    // Initialize log events - first bucket.
+    std::vector<std::unique_ptr<LogEvent>> events;
+    events.push_back(CreateUidProcessStateChangedEvent(
+            bucketStartTimeNs + 5 * NS_PER_SEC, 1 /*uid*/,
+            android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND));  // 0:15
+    events.push_back(
+            CreateAppCrashOccurredEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /*uid*/));  // 0:30
+    events.push_back(CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 30 * NS_PER_SEC,
+            android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN));  // 0:40
+    events.push_back(
+            CreateAppCrashOccurredEvent(bucketStartTimeNs + 60 * NS_PER_SEC, 1 /*uid*/));  // 1:10
+    events.push_back(CreateUidProcessStateChangedEvent(
+            bucketStartTimeNs + 90 * NS_PER_SEC, 1 /*uid*/,
+            android::app::ProcessStateEnum::PROCESS_STATE_TOP));  // 1:40
+    events.push_back(CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 90 * NS_PER_SEC,
+            android::view::DisplayStateEnum::DISPLAY_STATE_OFF));  // 1:40
+    events.push_back(
+            CreateAppCrashOccurredEvent(bucketStartTimeNs + 120 * NS_PER_SEC, 1 /*uid*/));  // 2:10
+    events.push_back(CreateUidProcessStateChangedEvent(
+            bucketStartTimeNs + 150 * NS_PER_SEC, 1 /*uid*/,
+            android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND));  // 2:40
+    events.push_back(CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 160 * NS_PER_SEC,
+            android::view::DisplayStateEnum::DISPLAY_STATE_ON));  // 2:50
+    events.push_back(
+            CreateAppCrashOccurredEvent(bucketStartTimeNs + 200 * NS_PER_SEC, 1 /*uid*/));  // 3:30
+    events.push_back(CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 210 * NS_PER_SEC,
+            android::view::DisplayStateEnum::DISPLAY_STATE_DOZE));  // 3:40
+    events.push_back(
+            CreateAppCrashOccurredEvent(bucketStartTimeNs + 250 * NS_PER_SEC, 1 /*uid*/));  // 4:20
+    events.push_back(CreateUidProcessStateChangedEvent(
+            bucketStartTimeNs + 280 * NS_PER_SEC, 2 /*uid*/,
+            android::app::ProcessStateEnum::PROCESS_STATE_TOP));  // 4:50
+    events.push_back(
+            CreateAppCrashOccurredEvent(bucketStartTimeNs + 285 * NS_PER_SEC, 2 /*uid*/));  // 4:55
+
+    // Initialize log events - second bucket.
+    events.push_back(
+            CreateAppCrashOccurredEvent(bucketStartTimeNs + 360 * NS_PER_SEC, 1 /*uid*/));  // 6:10
+    events.push_back(CreateUidProcessStateChangedEvent(
+            bucketStartTimeNs + 380 * NS_PER_SEC, 1 /*uid*/,
+            android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE));  // 6:30
+    events.push_back(CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 390 * NS_PER_SEC,
+            android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND));  // 6:40
+    events.push_back(CreateUidProcessStateChangedEvent(
+            bucketStartTimeNs + 420 * NS_PER_SEC, 2 /*uid*/,
+            android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND));  // 7:10
+    events.push_back(CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 440 * NS_PER_SEC,
+            android::view::DisplayStateEnum::DISPLAY_STATE_OFF));  // 7:30
+    events.push_back(
+            CreateAppCrashOccurredEvent(bucketStartTimeNs + 450 * NS_PER_SEC, 1 /*uid*/));  // 7:40
+    events.push_back(CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 520 * NS_PER_SEC,
+            android::view::DisplayStateEnum::DISPLAY_STATE_ON));  // 8:50
+    events.push_back(CreateUidProcessStateChangedEvent(
+            bucketStartTimeNs + 540 * NS_PER_SEC, 1 /*uid*/,
+            android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND));  // 9:10
+    events.push_back(
+            CreateAppCrashOccurredEvent(bucketStartTimeNs + 570 * NS_PER_SEC, 2 /*uid*/));  // 9:40
+
+    // Send log events to StatsLogProcessor.
+    for (auto& event : events) {
+        processor->OnLogEvent(event.get());
+    }
+
+    // Check dump report.
+    vector<uint8_t> buffer;
+    ConfigMetricsReportList reports;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP,
+                            FAST, &buffer);
+    EXPECT_GT(buffer.size(), 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics());
+    EXPECT_EQ(6, reports.reports(0).metrics(0).count_metrics().data_size());
+
+    // For each CountMetricData, check StateValue info is correct and buckets
+    // have correct counts.
+    auto data = reports.reports(0).metrics(0).count_metrics().data(0);
+    EXPECT_EQ(2, data.slice_by_state_size());
+    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_TRUE(data.slice_by_state(0).has_group_id());
+    EXPECT_EQ(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id());
+    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id());
+    EXPECT_TRUE(data.slice_by_state(1).has_value());
+    EXPECT_EQ(android::app::PROCESS_STATE_FOREGROUND_SERVICE, data.slice_by_state(1).value());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+
+    data = reports.reports(0).metrics(0).count_metrics().data(1);
+    EXPECT_EQ(2, data.slice_by_state_size());
+    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_TRUE(data.slice_by_state(0).has_value());
+    EXPECT_EQ(-1, data.slice_by_state(0).value());
+    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id());
+    EXPECT_TRUE(data.slice_by_state(1).has_value());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+
+    data = reports.reports(0).metrics(0).count_metrics().data(2);
+    EXPECT_EQ(2, data.slice_by_state_size());
+    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_TRUE(data.slice_by_state(0).has_group_id());
+    EXPECT_EQ(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id());
+    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id());
+    EXPECT_TRUE(data.slice_by_state(1).has_value());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value());
+    EXPECT_EQ(2, data.bucket_info_size());
+    EXPECT_EQ(2, data.bucket_info(0).count());
+    EXPECT_EQ(1, data.bucket_info(1).count());
+
+    data = reports.reports(0).metrics(0).count_metrics().data(3);
+    EXPECT_EQ(2, data.slice_by_state_size());
+    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_TRUE(data.slice_by_state(0).has_group_id());
+    EXPECT_EQ(StringToId("SCREEN_ON"), data.slice_by_state(0).group_id());
+    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id());
+    EXPECT_TRUE(data.slice_by_state(1).has_value());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+
+    data = reports.reports(0).metrics(0).count_metrics().data(4);
+    EXPECT_EQ(2, data.slice_by_state_size());
+    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_TRUE(data.slice_by_state(0).has_group_id());
+    EXPECT_EQ(StringToId("SCREEN_ON"), data.slice_by_state(0).group_id());
+    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id());
+    EXPECT_TRUE(data.slice_by_state(1).has_value());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, data.slice_by_state(1).value());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+
+    data = reports.reports(0).metrics(0).count_metrics().data(5);
+    EXPECT_EQ(2, data.slice_by_state_size());
+    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_TRUE(data.slice_by_state(0).has_group_id());
+    EXPECT_EQ(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id());
+    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id());
+    EXPECT_TRUE(data.slice_by_state(1).has_value());
+    EXPECT_EQ(android::app::PROCESS_STATE_TOP, data.slice_by_state(1).value());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(2, data.bucket_info(0).count());
+}
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp
index 8eb5f69..b586b06 100644
--- a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp
@@ -26,688 +26,688 @@
 
 #ifdef __ANDROID__
 
-// TODO(b/149590301): Update these tests to use new socket schema.
-//TEST(DurationMetricE2eTest, TestOneBucket) {
-//    StatsdConfig config;
-//    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-//
-//    auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
-//    auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
-//    *config.add_atom_matcher() = screenOnMatcher;
-//    *config.add_atom_matcher() = screenOffMatcher;
-//
-//    auto durationPredicate = CreateScreenIsOnPredicate();
-//    *config.add_predicate() = durationPredicate;
-//
-//    int64_t metricId = 123456;
-//    auto durationMetric = config.add_duration_metric();
-//    durationMetric->set_id(metricId);
-//    durationMetric->set_what(durationPredicate.id());
-//    durationMetric->set_bucket(FIVE_MINUTES);
-//    durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
-//
-//
-//    const int64_t baseTimeNs = 0; // 0:00
-//    const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01
-//    const int64_t bucketSizeNs =
-//            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL;
-//
-//    int uid = 12345;
-//    int64_t cfgId = 98765;
-//    ConfigKey cfgKey(uid, cfgId);
-//
-//    auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey);
-//
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
-//    EXPECT_TRUE(metricsManager->isConfigValid());
-//    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
-//    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//
-//    std::unique_ptr<LogEvent> event;
-//
-//    // Screen is off at start of bucket.
-//    event = CreateScreenStateChangedEvent(
-//            android::view::DISPLAY_STATE_OFF, configAddedTimeNs); // 0:01
-//    processor->OnLogEvent(event.get());
-//
-//    // Turn screen on.
-//    const int64_t durationStartNs = configAddedTimeNs + 10 * NS_PER_SEC; // 0:11
-//    event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, durationStartNs);
-//    processor->OnLogEvent(event.get());
-//
-//    // Turn off screen 30 seconds after turning on.
-//    const int64_t durationEndNs = durationStartNs + 30 * NS_PER_SEC; // 0:41
-//    event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, durationEndNs);
-//    processor->OnLogEvent(event.get());
-//
-//    event = CreateScreenBrightnessChangedEvent(64, durationEndNs + 1 * NS_PER_SEC); // 0:42
-//    processor->OnLogEvent(event.get());
-//
-//    ConfigMetricsReportList reports;
-//    vector<uint8_t> buffer;
-//    processor->onDumpReport(cfgKey, configAddedTimeNs + bucketSizeNs + 1 * NS_PER_SEC, false, true,
-//                            ADB_DUMP, FAST, &buffer); // 5:01
-//    EXPECT_TRUE(buffer.size() > 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStartEndTimestamp(&reports);
-//    EXPECT_EQ(1, reports.reports_size());
-//    EXPECT_EQ(1, reports.reports(0).metrics_size());
-//    EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id());
-//    EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
-//
-//    const StatsLogReport::DurationMetricDataWrapper& durationMetrics =
-//            reports.reports(0).metrics(0).duration_metrics();
-//    EXPECT_EQ(1, durationMetrics.data_size());
-//
-//    auto data = durationMetrics.data(0);
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(durationEndNs - durationStartNs, data.bucket_info(0).duration_nanos());
-//    EXPECT_EQ(configAddedTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(baseTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-//}
-//
-//TEST(DurationMetricE2eTest, TestTwoBuckets) {
-//    StatsdConfig config;
-//    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-//
-//    auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
-//    auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
-//    *config.add_atom_matcher() = screenOnMatcher;
-//    *config.add_atom_matcher() = screenOffMatcher;
-//
-//    auto durationPredicate = CreateScreenIsOnPredicate();
-//    *config.add_predicate() = durationPredicate;
-//
-//    int64_t metricId = 123456;
-//    auto durationMetric = config.add_duration_metric();
-//    durationMetric->set_id(metricId);
-//    durationMetric->set_what(durationPredicate.id());
-//    durationMetric->set_bucket(FIVE_MINUTES);
-//    durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
-//
-//
-//    const int64_t baseTimeNs = 0; // 0:00
-//    const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01
-//    const int64_t bucketSizeNs =
-//            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL;
-//
-//    int uid = 12345;
-//    int64_t cfgId = 98765;
-//    ConfigKey cfgKey(uid, cfgId);
-//
-//    auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey);
-//
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
-//    EXPECT_TRUE(metricsManager->isConfigValid());
-//    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
-//    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//
-//    std::unique_ptr<LogEvent> event;
-//
-//    // Screen is off at start of bucket.
-//    event = CreateScreenStateChangedEvent(
-//            android::view::DISPLAY_STATE_OFF, configAddedTimeNs); // 0:01
-//    processor->OnLogEvent(event.get());
-//
-//    // Turn screen on.
-//    const int64_t durationStartNs = configAddedTimeNs + 10 * NS_PER_SEC; // 0:11
-//    event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, durationStartNs);
-//    processor->OnLogEvent(event.get());
-//
-//    // Turn off screen 30 seconds after turning on.
-//    const int64_t durationEndNs = durationStartNs + 30 * NS_PER_SEC; // 0:41
-//    event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, durationEndNs);
-//    processor->OnLogEvent(event.get());
-//
-//    event = CreateScreenBrightnessChangedEvent(64, durationEndNs + 1 * NS_PER_SEC); // 0:42
-//    processor->OnLogEvent(event.get());
-//
-//    ConfigMetricsReportList reports;
-//    vector<uint8_t> buffer;
-//    processor->onDumpReport(cfgKey, configAddedTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC, false, true,
-//                            ADB_DUMP, FAST, &buffer); // 10:01
-//    EXPECT_TRUE(buffer.size() > 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStartEndTimestamp(&reports);
-//    EXPECT_EQ(1, reports.reports_size());
-//    EXPECT_EQ(1, reports.reports(0).metrics_size());
-//    EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id());
-//    EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
-//
-//    const StatsLogReport::DurationMetricDataWrapper& durationMetrics =
-//            reports.reports(0).metrics(0).duration_metrics();
-//    EXPECT_EQ(1, durationMetrics.data_size());
-//
-//    auto data = durationMetrics.data(0);
-//    EXPECT_EQ(1, data.bucket_info_size());
-//
-//    auto bucketInfo = data.bucket_info(0);
-//    EXPECT_EQ(0, bucketInfo.bucket_num());
-//    EXPECT_EQ(durationEndNs - durationStartNs, bucketInfo.duration_nanos());
-//    EXPECT_EQ(configAddedTimeNs, bucketInfo.start_bucket_elapsed_nanos());
-//    EXPECT_EQ(baseTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
-//}
-//
-//TEST(DurationMetricE2eTest, TestWithActivation) {
-//    StatsdConfig config;
-//    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-//
-//    auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
-//    auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
-//    auto crashMatcher = CreateProcessCrashAtomMatcher();
-//    *config.add_atom_matcher() = screenOnMatcher;
-//    *config.add_atom_matcher() = screenOffMatcher;
-//    *config.add_atom_matcher() = crashMatcher;
-//
-//    auto durationPredicate = CreateScreenIsOnPredicate();
-//    *config.add_predicate() = durationPredicate;
-//
-//    int64_t metricId = 123456;
-//    auto durationMetric = config.add_duration_metric();
-//    durationMetric->set_id(metricId);
-//    durationMetric->set_what(durationPredicate.id());
-//    durationMetric->set_bucket(FIVE_MINUTES);
-//    durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
-//
-//    auto metric_activation1 = config.add_metric_activation();
-//    metric_activation1->set_metric_id(metricId);
-//    auto event_activation1 = metric_activation1->add_event_activation();
-//    event_activation1->set_atom_matcher_id(crashMatcher.id());
-//    event_activation1->set_ttl_seconds(30); // 30 secs.
-//
-//    const int64_t bucketStartTimeNs = 10000000000;
-//    const int64_t bucketSizeNs =
-//            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL;
-//
-//    int uid = 12345;
-//    int64_t cfgId = 98765;
-//    ConfigKey cfgKey(uid, cfgId);
-//
-//    sp<UidMap> m = new UidMap();
-//    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
-//    sp<AlarmMonitor> anomalyAlarmMonitor;
-//    sp<AlarmMonitor> subscriberAlarmMonitor;
-//    vector<int64_t> activeConfigsBroadcast;
-//
-//    int broadcastCount = 0;
-//    StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor,
-//            bucketStartTimeNs, [](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(bucketStartTimeNs, cfgKey, config); // 0:00
-//
-//    EXPECT_EQ(processor.mMetricsManagers.size(), 1u);
-//    sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
-//    EXPECT_TRUE(metricsManager->isConfigValid());
-//    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
-//    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
-//    auto& eventActivationMap = metricProducer->mEventActivationMap;
-//
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    EXPECT_EQ(eventActivationMap.size(), 1u);
-//    EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
-//
-//    std::unique_ptr<LogEvent> event;
-//
-//    // Turn screen off.
-//    event = CreateScreenStateChangedEvent(
-//            android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 2 * NS_PER_SEC); // 0:02
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + 2 * NS_PER_SEC);
-//
-//    // Turn screen on.
-//    const int64_t durationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC; // 0:05
-//    event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, durationStartNs);
-//    processor.OnLogEvent(event.get(), durationStartNs);
-//
-//    // Activate metric.
-//    const int64_t activationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC; // 0:10
-//    const int64_t activationEndNs =
-//            activationStartNs + event_activation1->ttl_seconds() * NS_PER_SEC; // 0:40
-//    event = CreateAppCrashEvent(111, activationStartNs);
-//    processor.OnLogEvent(event.get(), activationStartNs);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(broadcastCount, 1);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
-//    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, activationStartNs);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
-//
-//    // Expire activation.
-//    const int64_t expirationNs = activationEndNs + 7 * NS_PER_SEC;
-//    event = CreateScreenBrightnessChangedEvent(64, expirationNs); // 0:47
-//    processor.OnLogEvent(event.get(), expirationNs);
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    EXPECT_EQ(broadcastCount, 2);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
-//    EXPECT_EQ(eventActivationMap.size(), 1u);
-//    EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, activationStartNs);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
-//
-//    // Turn off screen 10 seconds after activation expiration.
-//    const int64_t durationEndNs = activationEndNs + 10 * NS_PER_SEC; // 0:50
-//    event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, durationEndNs);
-//    processor.OnLogEvent(event.get(),durationEndNs);
-//
-//    // Turn screen on.
-//    const int64_t duration2StartNs = durationEndNs + 5 * NS_PER_SEC; // 0:55
-//    event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, duration2StartNs);
-//    processor.OnLogEvent(event.get(), duration2StartNs);
-//
-//    // Turn off screen.
-//    const int64_t duration2EndNs = duration2StartNs + 10 * NS_PER_SEC; // 1:05
-//    event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, duration2EndNs);
-//    processor.OnLogEvent(event.get(), duration2EndNs);
-//
-//    // Activate metric.
-//    const int64_t activation2StartNs = duration2EndNs + 5 * NS_PER_SEC; // 1:10
-//    const int64_t activation2EndNs =
-//            activation2StartNs + event_activation1->ttl_seconds() * NS_PER_SEC; // 1:40
-//    event = CreateAppCrashEvent(211, activation2StartNs);
-//    processor.OnLogEvent(event.get(), activation2StartNs);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(broadcastCount, 3);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
-//    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, activation2StartNs);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
-//
-//    ConfigMetricsReportList reports;
-//    vector<uint8_t> buffer;
-//    processor.onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1 * NS_PER_SEC, false, true,
-//                            ADB_DUMP, FAST, &buffer); // 5:01
-//    EXPECT_TRUE(buffer.size() > 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStartEndTimestamp(&reports);
-//    EXPECT_EQ(1, reports.reports_size());
-//    EXPECT_EQ(1, reports.reports(0).metrics_size());
-//    EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id());
-//    EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
-//
-//    const StatsLogReport::DurationMetricDataWrapper& durationMetrics =
-//            reports.reports(0).metrics(0).duration_metrics();
-//    EXPECT_EQ(1, durationMetrics.data_size());
-//
-//    auto data = durationMetrics.data(0);
-//    EXPECT_EQ(1, data.bucket_info_size());
-//
-//    auto bucketInfo = data.bucket_info(0);
-//    EXPECT_EQ(0, bucketInfo.bucket_num());
-//    EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos());
-//    EXPECT_EQ(expirationNs, bucketInfo.end_bucket_elapsed_nanos());
-//    EXPECT_EQ(expirationNs - durationStartNs, bucketInfo.duration_nanos());
-//}
-//
-//TEST(DurationMetricE2eTest, TestWithCondition) {
-//    StatsdConfig config;
-//    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-//    *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
-//    *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
-//    *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
-//    *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
-//
-//    auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
-//    *config.add_predicate() = holdingWakelockPredicate;
-//
-//    auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
-//    *config.add_predicate() = isInBackgroundPredicate;
-//
-//    auto durationMetric = config.add_duration_metric();
-//    durationMetric->set_id(StringToId("WakelockDuration"));
-//    durationMetric->set_what(holdingWakelockPredicate.id());
-//    durationMetric->set_condition(isInBackgroundPredicate.id());
-//    durationMetric->set_aggregation_type(DurationMetric::SUM);
-//    durationMetric->set_bucket(FIVE_MINUTES);
-//
-//    ConfigKey cfgKey;
-//    uint64_t bucketStartTimeNs = 10000000000;
-//    uint64_t bucketSizeNs =
-//            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-//    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
-//    EXPECT_TRUE(metricsManager->isConfigValid());
-//    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
-//    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
-//    auto& eventActivationMap = metricProducer->mEventActivationMap;
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_TRUE(eventActivationMap.empty());
-//
-//    int appUid = 123;
-//    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(appUid, "App1")};
-//
-//    auto event = CreateAcquireWakelockEvent(
-//            attributions1, "wl1", bucketStartTimeNs + 10 * NS_PER_SEC); // 0:10
-//    processor->OnLogEvent(event.get());
-//
-//    event = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 22 * NS_PER_SEC); // 0:22
-//    processor->OnLogEvent(event.get());
-//
-//    event = CreateMoveToForegroundEvent(
-//            appUid, bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC); // 3:15
-//    processor->OnLogEvent(event.get());
-//
-//    event = CreateReleaseWakelockEvent(
-//            attributions1, "wl1", bucketStartTimeNs + 4 * 60 * NS_PER_SEC); // 4:00
-//    processor->OnLogEvent(event.get());
-//
-//    vector<uint8_t> buffer;
-//    ConfigMetricsReportList reports;
-//    processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true,
-//                            ADB_DUMP, FAST, &buffer);
-//    EXPECT_GT(buffer.size(), 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStringInReport(&reports);
-//    backfillStartEndTimestamp(&reports);
-//
-//    EXPECT_EQ(1, reports.reports_size());
-//    EXPECT_EQ(1, reports.reports(0).metrics_size());
-//    EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size());
-//
-//    auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
-//
-//    // Validate bucket info.
-//    EXPECT_EQ(1, data.bucket_info_size());
-//
-//    auto bucketInfo = data.bucket_info(0);
-//    EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
-//    EXPECT_EQ((2 * 60 + 53) * NS_PER_SEC, bucketInfo.duration_nanos());
-//}
-//
-//TEST(DurationMetricE2eTest, TestWithSlicedCondition) {
-//    StatsdConfig config;
-//    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-//    auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
-//    *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
-//    *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
-//    *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
-//    *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
-//
-//    auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
-//    // The predicate is dimensioning by first attribution node by uid.
-//    FieldMatcher dimensions = CreateAttributionUidDimensions(
-//            android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
-//    *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions;
-//    *config.add_predicate() = holdingWakelockPredicate;
-//
-//    auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
-//    *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
-//        CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST});
-//    *config.add_predicate() = isInBackgroundPredicate;
-//
-//    auto durationMetric = config.add_duration_metric();
-//    durationMetric->set_id(StringToId("WakelockDuration"));
-//    durationMetric->set_what(holdingWakelockPredicate.id());
-//    durationMetric->set_condition(isInBackgroundPredicate.id());
-//    durationMetric->set_aggregation_type(DurationMetric::SUM);
-//    // The metric is dimensioning by first attribution node and only by uid.
-//    *durationMetric->mutable_dimensions_in_what() =
-//        CreateAttributionUidDimensions(
-//            android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
-//    durationMetric->set_bucket(FIVE_MINUTES);
-//
-//    // Links between wakelock state atom and condition of app is in background.
-//    auto links = durationMetric->add_links();
-//    links->set_condition(isInBackgroundPredicate.id());
-//    auto dimensionWhat = links->mutable_fields_in_what();
-//    dimensionWhat->set_field(android::util::WAKELOCK_STATE_CHANGED);
-//    dimensionWhat->add_child()->set_field(1);  // uid field.
-//    *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
-//            android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, { Position::FIRST });
-//
-//    ConfigKey cfgKey;
-//    uint64_t bucketStartTimeNs = 10000000000;
-//    uint64_t bucketSizeNs =
-//            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-//    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
-//    EXPECT_TRUE(metricsManager->isConfigValid());
-//    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
-//    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
-//    auto& eventActivationMap = metricProducer->mEventActivationMap;
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_TRUE(eventActivationMap.empty());
-//
-//    int appUid = 123;
-//    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(appUid, "App1")};
-//
-//    auto event = CreateAcquireWakelockEvent(
-//            attributions1, "wl1", bucketStartTimeNs + 10 * NS_PER_SEC); // 0:10
-//    processor->OnLogEvent(event.get());
-//
-//    event = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 22 * NS_PER_SEC); // 0:22
-//    processor->OnLogEvent(event.get());
-//
-//    event = CreateReleaseWakelockEvent(
-//            attributions1, "wl1", bucketStartTimeNs + 60 * NS_PER_SEC); // 1:00
-//    processor->OnLogEvent(event.get());
-//
-//
-//    event = CreateMoveToForegroundEvent(
-//            appUid, bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC); // 3:15
-//    processor->OnLogEvent(event.get());
-//
-//    vector<uint8_t> buffer;
-//    ConfigMetricsReportList reports;
-//    processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true,
-//                            ADB_DUMP, FAST, &buffer);
-//    EXPECT_GT(buffer.size(), 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStringInReport(&reports);
-//    backfillStartEndTimestamp(&reports);
-//
-//    EXPECT_EQ(1, reports.reports_size());
-//    EXPECT_EQ(1, reports.reports(0).metrics_size());
-//    EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size());
-//
-//    auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
-//    // Validate dimension value.
-//    ValidateAttributionUidDimension(data.dimensions_in_what(),
-//                                    android::util::WAKELOCK_STATE_CHANGED, appUid);
-//    // Validate bucket info.
-//    EXPECT_EQ(1, data.bucket_info_size());
-//
-//    auto bucketInfo = data.bucket_info(0);
-//    EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
-//    EXPECT_EQ(38 * NS_PER_SEC, bucketInfo.duration_nanos());
-//}
-//
-//TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition) {
-//    StatsdConfig config;
-//    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-//    auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
-//    *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
-//    *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
-//    *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
-//    *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
-//    *config.add_atom_matcher() = screenOnMatcher;
-//
-//    auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
-//    // The predicate is dimensioning by first attribution node by uid.
-//    FieldMatcher dimensions = CreateAttributionUidDimensions(
-//            android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
-//    *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions;
-//    *config.add_predicate() = holdingWakelockPredicate;
-//
-//    auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
-//    *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
-//        CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST});
-//    *config.add_predicate() = isInBackgroundPredicate;
-//
-//    auto durationMetric = config.add_duration_metric();
-//    durationMetric->set_id(StringToId("WakelockDuration"));
-//    durationMetric->set_what(holdingWakelockPredicate.id());
-//    durationMetric->set_condition(isInBackgroundPredicate.id());
-//    durationMetric->set_aggregation_type(DurationMetric::SUM);
-//    // The metric is dimensioning by first attribution node and only by uid.
-//    *durationMetric->mutable_dimensions_in_what() =
-//        CreateAttributionUidDimensions(
-//            android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
-//    durationMetric->set_bucket(FIVE_MINUTES);
-//
-//    // Links between wakelock state atom and condition of app is in background.
-//    auto links = durationMetric->add_links();
-//    links->set_condition(isInBackgroundPredicate.id());
-//    auto dimensionWhat = links->mutable_fields_in_what();
-//    dimensionWhat->set_field(android::util::WAKELOCK_STATE_CHANGED);
-//    dimensionWhat->add_child()->set_field(1);  // uid field.
-//    *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
-//            android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, { Position::FIRST });
-//
-//    auto metric_activation1 = config.add_metric_activation();
-//    metric_activation1->set_metric_id(durationMetric->id());
-//    auto event_activation1 = metric_activation1->add_event_activation();
-//    event_activation1->set_atom_matcher_id(screenOnMatcher.id());
-//    event_activation1->set_ttl_seconds(60 * 2);  // 2 minutes.
-//
-//    ConfigKey cfgKey;
-//    uint64_t bucketStartTimeNs = 10000000000;
-//    uint64_t bucketSizeNs =
-//            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-//    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
-//    EXPECT_TRUE(metricsManager->isConfigValid());
-//    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
-//    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
-//    auto& eventActivationMap = metricProducer->mEventActivationMap;
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    EXPECT_EQ(eventActivationMap.size(), 1u);
-//    EXPECT_TRUE(eventActivationMap.find(4) != eventActivationMap.end());
-//    EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[4]->start_ns, 0);
-//    EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
-//
-//    int appUid = 123;
-//    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(appUid, "App1")};
-//
-//    auto event = CreateAcquireWakelockEvent(
-//            attributions1, "wl1", bucketStartTimeNs + 10 * NS_PER_SEC); // 0:10
-//    processor->OnLogEvent(event.get());
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[4]->start_ns, 0);
-//    EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
-//
-//    event = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 22 * NS_PER_SEC); // 0:22
-//    processor->OnLogEvent(event.get());
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[4]->start_ns, 0);
-//    EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
-//
-//    const int64_t durationStartNs = bucketStartTimeNs + 30 * NS_PER_SEC; // 0:30
-//    event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, durationStartNs);
-//    processor->OnLogEvent(event.get());
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[4]->start_ns, durationStartNs);
-//    EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
-//
-//    const int64_t durationEndNs =
-//            durationStartNs + (event_activation1->ttl_seconds() + 30) * NS_PER_SEC; // 3:00
-//    event = CreateAppCrashEvent(333, durationEndNs);
-//    processor->OnLogEvent(event.get());
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[4]->start_ns, durationStartNs);
-//    EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
-//
-//    event = CreateMoveToForegroundEvent(
-//            appUid, bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC); // 3:15
-//    processor->OnLogEvent(event.get());
-//
-//    event = CreateReleaseWakelockEvent(
-//            attributions1, "wl1", bucketStartTimeNs + (4 * 60 + 17) * NS_PER_SEC); // 4:17
-//    processor->OnLogEvent(event.get());
-//
-//    event = CreateMoveToBackgroundEvent(
-//            appUid, bucketStartTimeNs + (4 * 60 + 20) * NS_PER_SEC); // 4:20
-//    processor->OnLogEvent(event.get());
-//
-//    event = CreateAcquireWakelockEvent(
-//            attributions1, "wl1", bucketStartTimeNs + (4 * 60 + 25) * NS_PER_SEC); // 4:25
-//    processor->OnLogEvent(event.get());
-//
-//    const int64_t duration2StartNs = bucketStartTimeNs + (4 * 60 + 30) * NS_PER_SEC; // 4:30
-//    event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, duration2StartNs);
-//    processor->OnLogEvent(event.get());
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[4]->start_ns, duration2StartNs);
-//    EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
-//
-//    vector<uint8_t> buffer;
-//    ConfigMetricsReportList reports;
-//    processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true,
-//                            ADB_DUMP, FAST, &buffer);
-//    EXPECT_GT(buffer.size(), 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStringInReport(&reports);
-//    backfillStartEndTimestamp(&reports);
-//
-//    EXPECT_EQ(1, reports.reports_size());
-//    EXPECT_EQ(1, reports.reports(0).metrics_size());
-//    EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size());
-//
-//    auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
-//    // Validate dimension value.
-//    ValidateAttributionUidDimension(data.dimensions_in_what(),
-//                                    android::util::WAKELOCK_STATE_CHANGED, appUid);
-//    // Validate bucket info.
-//    EXPECT_EQ(2, data.bucket_info_size());
-//
-//    auto bucketInfo = data.bucket_info(0);
-//    EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos());
-//    EXPECT_EQ(durationEndNs, bucketInfo.end_bucket_elapsed_nanos());
-//    EXPECT_EQ(durationEndNs - durationStartNs, bucketInfo.duration_nanos());
-//
-//    bucketInfo = data.bucket_info(1);
-//    EXPECT_EQ(durationEndNs, bucketInfo.start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - duration2StartNs, bucketInfo.duration_nanos());
-//}
+TEST(DurationMetricE2eTest, TestOneBucket) {
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+
+    auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+    auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = screenOnMatcher;
+    *config.add_atom_matcher() = screenOffMatcher;
+
+    auto durationPredicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = durationPredicate;
+
+    int64_t metricId = 123456;
+    auto durationMetric = config.add_duration_metric();
+    durationMetric->set_id(metricId);
+    durationMetric->set_what(durationPredicate.id());
+    durationMetric->set_bucket(FIVE_MINUTES);
+    durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
+
+    const int64_t baseTimeNs = 0;                                   // 0:00
+    const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC;  // 0:01
+    const int64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL;
+
+    int uid = 12345;
+    int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+
+    auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey);
+
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+
+    std::unique_ptr<LogEvent> event;
+
+    // Screen is off at start of bucket.
+    event = CreateScreenStateChangedEvent(configAddedTimeNs,
+                                          android::view::DISPLAY_STATE_OFF);  // 0:01
+    processor->OnLogEvent(event.get());
+
+    // Turn screen on.
+    const int64_t durationStartNs = configAddedTimeNs + 10 * NS_PER_SEC;  // 0:11
+    event = CreateScreenStateChangedEvent(durationStartNs, android::view::DISPLAY_STATE_ON);
+    processor->OnLogEvent(event.get());
+
+    // Turn off screen 30 seconds after turning on.
+    const int64_t durationEndNs = durationStartNs + 30 * NS_PER_SEC;  // 0:41
+    event = CreateScreenStateChangedEvent(durationEndNs, android::view::DISPLAY_STATE_OFF);
+    processor->OnLogEvent(event.get());
+
+    event = CreateScreenBrightnessChangedEvent(durationEndNs + 1 * NS_PER_SEC, 64);  // 0:42
+    processor->OnLogEvent(event.get());
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, configAddedTimeNs + bucketSizeNs + 1 * NS_PER_SEC, false, true,
+                            ADB_DUMP, FAST, &buffer);  // 5:01
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStartEndTimestamp(&reports);
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id());
+    EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
+
+    const StatsLogReport::DurationMetricDataWrapper& durationMetrics =
+            reports.reports(0).metrics(0).duration_metrics();
+    EXPECT_EQ(1, durationMetrics.data_size());
+
+    auto data = durationMetrics.data(0);
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(durationEndNs - durationStartNs, data.bucket_info(0).duration_nanos());
+    EXPECT_EQ(configAddedTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+}
+
+TEST(DurationMetricE2eTest, TestTwoBuckets) {
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+
+    auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+    auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = screenOnMatcher;
+    *config.add_atom_matcher() = screenOffMatcher;
+
+    auto durationPredicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = durationPredicate;
+
+    int64_t metricId = 123456;
+    auto durationMetric = config.add_duration_metric();
+    durationMetric->set_id(metricId);
+    durationMetric->set_what(durationPredicate.id());
+    durationMetric->set_bucket(FIVE_MINUTES);
+    durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
+
+    const int64_t baseTimeNs = 0;                                   // 0:00
+    const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC;  // 0:01
+    const int64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL;
+
+    int uid = 12345;
+    int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+
+    auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey);
+
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+
+    std::unique_ptr<LogEvent> event;
+
+    // Screen is off at start of bucket.
+    event = CreateScreenStateChangedEvent(configAddedTimeNs,
+                                          android::view::DISPLAY_STATE_OFF);  // 0:01
+    processor->OnLogEvent(event.get());
+
+    // Turn screen on.
+    const int64_t durationStartNs = configAddedTimeNs + 10 * NS_PER_SEC;  // 0:11
+    event = CreateScreenStateChangedEvent(durationStartNs, android::view::DISPLAY_STATE_ON);
+    processor->OnLogEvent(event.get());
+
+    // Turn off screen 30 seconds after turning on.
+    const int64_t durationEndNs = durationStartNs + 30 * NS_PER_SEC;  // 0:41
+    event = CreateScreenStateChangedEvent(durationEndNs, android::view::DISPLAY_STATE_OFF);
+    processor->OnLogEvent(event.get());
+
+    event = CreateScreenBrightnessChangedEvent(durationEndNs + 1 * NS_PER_SEC, 64);  // 0:42
+    processor->OnLogEvent(event.get());
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC, false,
+                            true, ADB_DUMP, FAST, &buffer);  // 10:01
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStartEndTimestamp(&reports);
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id());
+    EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
+
+    const StatsLogReport::DurationMetricDataWrapper& durationMetrics =
+            reports.reports(0).metrics(0).duration_metrics();
+    EXPECT_EQ(1, durationMetrics.data_size());
+
+    auto data = durationMetrics.data(0);
+    EXPECT_EQ(1, data.bucket_info_size());
+
+    auto bucketInfo = data.bucket_info(0);
+    EXPECT_EQ(0, bucketInfo.bucket_num());
+    EXPECT_EQ(durationEndNs - durationStartNs, bucketInfo.duration_nanos());
+    EXPECT_EQ(configAddedTimeNs, bucketInfo.start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
+}
+
+TEST(DurationMetricE2eTest, TestWithActivation) {
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+
+    auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+    auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
+    auto crashMatcher = CreateProcessCrashAtomMatcher();
+    *config.add_atom_matcher() = screenOnMatcher;
+    *config.add_atom_matcher() = screenOffMatcher;
+    *config.add_atom_matcher() = crashMatcher;
+
+    auto durationPredicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = durationPredicate;
+
+    int64_t metricId = 123456;
+    auto durationMetric = config.add_duration_metric();
+    durationMetric->set_id(metricId);
+    durationMetric->set_what(durationPredicate.id());
+    durationMetric->set_bucket(FIVE_MINUTES);
+    durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
+
+    auto metric_activation1 = config.add_metric_activation();
+    metric_activation1->set_metric_id(metricId);
+    auto event_activation1 = metric_activation1->add_event_activation();
+    event_activation1->set_atom_matcher_id(crashMatcher.id());
+    event_activation1->set_ttl_seconds(30);  // 30 secs.
+
+    const int64_t bucketStartTimeNs = 10000000000;
+    const int64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL;
+
+    int uid = 12345;
+    int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+
+    sp<UidMap> m = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> subscriberAlarmMonitor;
+    vector<int64_t> activeConfigsBroadcast;
+
+    int broadcastCount = 0;
+    StatsLogProcessor processor(
+            m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs,
+            [](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(bucketStartTimeNs, cfgKey, config);  // 0:00
+
+    EXPECT_EQ(processor.mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+    auto& eventActivationMap = metricProducer->mEventActivationMap;
+
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap.size(), 1u);
+    EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
+
+    std::unique_ptr<LogEvent> event;
+
+    // Turn screen off.
+    event = CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * NS_PER_SEC,
+                                          android::view::DISPLAY_STATE_OFF);  // 0:02
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + 2 * NS_PER_SEC);
+
+    // Turn screen on.
+    const int64_t durationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC;  // 0:05
+    event = CreateScreenStateChangedEvent(durationStartNs, android::view::DISPLAY_STATE_ON);
+    processor.OnLogEvent(event.get(), durationStartNs);
+
+    // Activate metric.
+    const int64_t activationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC;  // 0:10
+    const int64_t activationEndNs =
+            activationStartNs + event_activation1->ttl_seconds() * NS_PER_SEC;  // 0:40
+    event = CreateAppCrashEvent(activationStartNs, 111);
+    processor.OnLogEvent(event.get(), activationStartNs);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 1);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, activationStartNs);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
+
+    // Expire activation.
+    const int64_t expirationNs = activationEndNs + 7 * NS_PER_SEC;
+    event = CreateScreenBrightnessChangedEvent(expirationNs, 64);  // 0:47
+    processor.OnLogEvent(event.get(), expirationNs);
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 2);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+    EXPECT_EQ(eventActivationMap.size(), 1u);
+    EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, activationStartNs);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
+
+    // Turn off screen 10 seconds after activation expiration.
+    const int64_t durationEndNs = activationEndNs + 10 * NS_PER_SEC;  // 0:50
+    event = CreateScreenStateChangedEvent(durationEndNs, android::view::DISPLAY_STATE_OFF);
+    processor.OnLogEvent(event.get(), durationEndNs);
+
+    // Turn screen on.
+    const int64_t duration2StartNs = durationEndNs + 5 * NS_PER_SEC;  // 0:55
+    event = CreateScreenStateChangedEvent(duration2StartNs, android::view::DISPLAY_STATE_ON);
+    processor.OnLogEvent(event.get(), duration2StartNs);
+
+    // Turn off screen.
+    const int64_t duration2EndNs = duration2StartNs + 10 * NS_PER_SEC;  // 1:05
+    event = CreateScreenStateChangedEvent(duration2EndNs, android::view::DISPLAY_STATE_OFF);
+    processor.OnLogEvent(event.get(), duration2EndNs);
+
+    // Activate metric.
+    const int64_t activation2StartNs = duration2EndNs + 5 * NS_PER_SEC;  // 1:10
+    const int64_t activation2EndNs =
+            activation2StartNs + event_activation1->ttl_seconds() * NS_PER_SEC;  // 1:40
+    event = CreateAppCrashEvent(activation2StartNs, 211);
+    processor.OnLogEvent(event.get(), activation2StartNs);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 3);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, activation2StartNs);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor.onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1 * NS_PER_SEC, false, true,
+                           ADB_DUMP, FAST, &buffer);  // 5:01
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStartEndTimestamp(&reports);
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id());
+    EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
+
+    const StatsLogReport::DurationMetricDataWrapper& durationMetrics =
+            reports.reports(0).metrics(0).duration_metrics();
+    EXPECT_EQ(1, durationMetrics.data_size());
+
+    auto data = durationMetrics.data(0);
+    EXPECT_EQ(1, data.bucket_info_size());
+
+    auto bucketInfo = data.bucket_info(0);
+    EXPECT_EQ(0, bucketInfo.bucket_num());
+    EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos());
+    EXPECT_EQ(expirationNs, bucketInfo.end_bucket_elapsed_nanos());
+    EXPECT_EQ(expirationNs - durationStartNs, bucketInfo.duration_nanos());
+}
+
+TEST(DurationMetricE2eTest, TestWithCondition) {
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+    *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
+    *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
+    *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
+    *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
+
+    auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+    *config.add_predicate() = holdingWakelockPredicate;
+
+    auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
+    *config.add_predicate() = isInBackgroundPredicate;
+
+    auto durationMetric = config.add_duration_metric();
+    durationMetric->set_id(StringToId("WakelockDuration"));
+    durationMetric->set_what(holdingWakelockPredicate.id());
+    durationMetric->set_condition(isInBackgroundPredicate.id());
+    durationMetric->set_aggregation_type(DurationMetric::SUM);
+    durationMetric->set_bucket(FIVE_MINUTES);
+
+    ConfigKey cfgKey;
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+    auto& eventActivationMap = metricProducer->mEventActivationMap;
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_TRUE(eventActivationMap.empty());
+
+    int appUid = 123;
+    vector<int> attributionUids1 = {appUid};
+    vector<string> attributionTags1 = {"App1"};
+
+    auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, attributionUids1,
+                                            attributionTags1,
+                                            "wl1");  // 0:10
+    processor->OnLogEvent(event.get());
+
+    event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 22 * NS_PER_SEC, appUid);  // 0:22
+    processor->OnLogEvent(event.get());
+
+    event = CreateMoveToForegroundEvent(bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC,
+                                        appUid);  // 3:15
+    processor->OnLogEvent(event.get());
+
+    event = CreateReleaseWakelockEvent(bucketStartTimeNs + 4 * 60 * NS_PER_SEC, attributionUids1,
+                                       attributionTags1,
+                                       "wl1");  // 4:00
+    processor->OnLogEvent(event.get());
+
+    vector<uint8_t> buffer;
+    ConfigMetricsReportList reports;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
+                            FAST, &buffer);
+    EXPECT_GT(buffer.size(), 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size());
+
+    auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
+
+    // Validate bucket info.
+    EXPECT_EQ(1, data.bucket_info_size());
+
+    auto bucketInfo = data.bucket_info(0);
+    EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
+    EXPECT_EQ((2 * 60 + 53) * NS_PER_SEC, bucketInfo.duration_nanos());
+}
+
+TEST(DurationMetricE2eTest, TestWithSlicedCondition) {
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+    auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
+    *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
+    *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
+    *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
+
+    auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+    // The predicate is dimensioning by first attribution node by uid.
+    FieldMatcher dimensions = CreateAttributionUidDimensions(android::util::WAKELOCK_STATE_CHANGED,
+                                                             {Position::FIRST});
+    *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions;
+    *config.add_predicate() = holdingWakelockPredicate;
+
+    auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
+    *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
+            CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST});
+    *config.add_predicate() = isInBackgroundPredicate;
+
+    auto durationMetric = config.add_duration_metric();
+    durationMetric->set_id(StringToId("WakelockDuration"));
+    durationMetric->set_what(holdingWakelockPredicate.id());
+    durationMetric->set_condition(isInBackgroundPredicate.id());
+    durationMetric->set_aggregation_type(DurationMetric::SUM);
+    // The metric is dimensioning by first attribution node and only by uid.
+    *durationMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
+            android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    durationMetric->set_bucket(FIVE_MINUTES);
+
+    // Links between wakelock state atom and condition of app is in background.
+    auto links = durationMetric->add_links();
+    links->set_condition(isInBackgroundPredicate.id());
+    auto dimensionWhat = links->mutable_fields_in_what();
+    dimensionWhat->set_field(android::util::WAKELOCK_STATE_CHANGED);
+    dimensionWhat->add_child()->set_field(1);  // uid field.
+    *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
+            android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST});
+
+    ConfigKey cfgKey;
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+    auto& eventActivationMap = metricProducer->mEventActivationMap;
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_TRUE(eventActivationMap.empty());
+
+    int appUid = 123;
+    std::vector<int> attributionUids1 = {appUid};
+    std::vector<string> attributionTags1 = {"App1"};
+
+    auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, attributionUids1,
+                                            attributionTags1, "wl1");  // 0:10
+    processor->OnLogEvent(event.get());
+
+    event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 22 * NS_PER_SEC, appUid);  // 0:22
+    processor->OnLogEvent(event.get());
+
+    event = CreateReleaseWakelockEvent(bucketStartTimeNs + 60 * NS_PER_SEC, attributionUids1,
+                                       attributionTags1, "wl1");  // 1:00
+    processor->OnLogEvent(event.get());
+
+    event = CreateMoveToForegroundEvent(bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC,
+                                        appUid);  // 3:15
+    processor->OnLogEvent(event.get());
+
+    vector<uint8_t> buffer;
+    ConfigMetricsReportList reports;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
+                            FAST, &buffer);
+    EXPECT_GT(buffer.size(), 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size());
+
+    auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
+    // Validate dimension value.
+    ValidateAttributionUidDimension(data.dimensions_in_what(),
+                                    android::util::WAKELOCK_STATE_CHANGED, appUid);
+    // Validate bucket info.
+    EXPECT_EQ(1, data.bucket_info_size());
+
+    auto bucketInfo = data.bucket_info(0);
+    EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
+    EXPECT_EQ(38 * NS_PER_SEC, bucketInfo.duration_nanos());
+}
+
+TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition) {
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+    auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
+    *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
+    *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
+    *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
+    *config.add_atom_matcher() = screenOnMatcher;
+
+    auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+    // The predicate is dimensioning by first attribution node by uid.
+    FieldMatcher dimensions = CreateAttributionUidDimensions(android::util::WAKELOCK_STATE_CHANGED,
+                                                             {Position::FIRST});
+    *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions;
+    *config.add_predicate() = holdingWakelockPredicate;
+
+    auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
+    *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
+            CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST});
+    *config.add_predicate() = isInBackgroundPredicate;
+
+    auto durationMetric = config.add_duration_metric();
+    durationMetric->set_id(StringToId("WakelockDuration"));
+    durationMetric->set_what(holdingWakelockPredicate.id());
+    durationMetric->set_condition(isInBackgroundPredicate.id());
+    durationMetric->set_aggregation_type(DurationMetric::SUM);
+    // The metric is dimensioning by first attribution node and only by uid.
+    *durationMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
+            android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    durationMetric->set_bucket(FIVE_MINUTES);
+
+    // Links between wakelock state atom and condition of app is in background.
+    auto links = durationMetric->add_links();
+    links->set_condition(isInBackgroundPredicate.id());
+    auto dimensionWhat = links->mutable_fields_in_what();
+    dimensionWhat->set_field(android::util::WAKELOCK_STATE_CHANGED);
+    dimensionWhat->add_child()->set_field(1);  // uid field.
+    *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
+            android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST});
+
+    auto metric_activation1 = config.add_metric_activation();
+    metric_activation1->set_metric_id(durationMetric->id());
+    auto event_activation1 = metric_activation1->add_event_activation();
+    event_activation1->set_atom_matcher_id(screenOnMatcher.id());
+    event_activation1->set_ttl_seconds(60 * 2);  // 2 minutes.
+
+    ConfigKey cfgKey;
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+    auto& eventActivationMap = metricProducer->mEventActivationMap;
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap.size(), 1u);
+    EXPECT_TRUE(eventActivationMap.find(4) != eventActivationMap.end());
+    EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[4]->start_ns, 0);
+    EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
+
+    int appUid = 123;
+    std::vector<int> attributionUids1 = {appUid};
+    std::vector<string> attributionTags1 = {"App1"};
+
+    auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, attributionUids1,
+                                            attributionTags1, "wl1");  // 0:10
+    processor->OnLogEvent(event.get());
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[4]->start_ns, 0);
+    EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
+
+    event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 22 * NS_PER_SEC, appUid);  // 0:22
+    processor->OnLogEvent(event.get());
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[4]->start_ns, 0);
+    EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
+
+    const int64_t durationStartNs = bucketStartTimeNs + 30 * NS_PER_SEC;  // 0:30
+    event = CreateScreenStateChangedEvent(durationStartNs, android::view::DISPLAY_STATE_ON);
+    processor->OnLogEvent(event.get());
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[4]->start_ns, durationStartNs);
+    EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
+
+    const int64_t durationEndNs =
+            durationStartNs + (event_activation1->ttl_seconds() + 30) * NS_PER_SEC;  // 3:00
+    event = CreateAppCrashEvent(durationEndNs, 333);
+    processor->OnLogEvent(event.get());
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[4]->start_ns, durationStartNs);
+    EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
+
+    event = CreateMoveToForegroundEvent(bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC,
+                                        appUid);  // 3:15
+    processor->OnLogEvent(event.get());
+
+    event = CreateReleaseWakelockEvent(bucketStartTimeNs + (4 * 60 + 17) * NS_PER_SEC,
+                                       attributionUids1, attributionTags1, "wl1");  // 4:17
+    processor->OnLogEvent(event.get());
+
+    event = CreateMoveToBackgroundEvent(bucketStartTimeNs + (4 * 60 + 20) * NS_PER_SEC,
+                                        appUid);  // 4:20
+    processor->OnLogEvent(event.get());
+
+    event = CreateAcquireWakelockEvent(bucketStartTimeNs + (4 * 60 + 25) * NS_PER_SEC,
+                                       attributionUids1, attributionTags1, "wl1");  // 4:25
+    processor->OnLogEvent(event.get());
+
+    const int64_t duration2StartNs = bucketStartTimeNs + (4 * 60 + 30) * NS_PER_SEC;  // 4:30
+    event = CreateScreenStateChangedEvent(duration2StartNs, android::view::DISPLAY_STATE_ON);
+    processor->OnLogEvent(event.get());
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[4]->start_ns, duration2StartNs);
+    EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
+
+    vector<uint8_t> buffer;
+    ConfigMetricsReportList reports;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
+                            FAST, &buffer);
+    EXPECT_GT(buffer.size(), 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size());
+
+    auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
+    // Validate dimension value.
+    ValidateAttributionUidDimension(data.dimensions_in_what(),
+                                    android::util::WAKELOCK_STATE_CHANGED, appUid);
+    // Validate bucket info.
+    EXPECT_EQ(2, data.bucket_info_size());
+
+    auto bucketInfo = data.bucket_info(0);
+    EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos());
+    EXPECT_EQ(durationEndNs, bucketInfo.end_bucket_elapsed_nanos());
+    EXPECT_EQ(durationEndNs - durationStartNs, bucketInfo.duration_nanos());
+
+    bucketInfo = data.bucket_info(1);
+    EXPECT_EQ(durationEndNs, bucketInfo.start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - duration2StartNs, bucketInfo.duration_nanos());
+}
 
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
index 7f651d4..594c1e6 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
@@ -65,482 +65,465 @@
 
 }  // namespaces
 
-// TODO(b/149590301): Update this test to use new socket schema.
-//TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) {
-//    auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE);
-//    int64_t baseTimeNs = getElapsedRealtimeNs();
-//    int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
-//    int64_t bucketSizeNs =
-//        TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
-//
-//    ConfigKey cfgKey;
-//    auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
-//                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
-//                                             ATOM_TAG);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-//    processor->mPullerManager->ForceClearPullerCache();
-//
-//    int startBucketNum = processor->mMetricsManagers.begin()->second->
-//            mAllMetricProducers[0]->getCurrentBucketNum();
-//    EXPECT_GT(startBucketNum, (int64_t)0);
-//
-//    // When creating the config, the gauge metric producer should register the alarm at the
-//    // end of the current bucket.
-//    EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
-//    EXPECT_EQ(bucketSizeNs,
-//              processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
-//    int64_t& nextPullTimeNs =
-//            processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs);
-//
-//    auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-//                                                        configAddedTimeNs + 55);
-//    processor->OnLogEvent(screenOffEvent.get());
-//
-//    // Pulling alarm arrives on time and reset the sequential pulling alarm.
-//    processor->informPullAlarmFired(nextPullTimeNs + 1);
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, nextPullTimeNs);
-//
-//    auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-//                                                       configAddedTimeNs + bucketSizeNs + 10);
-//    processor->OnLogEvent(screenOnEvent.get());
-//
-//    screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-//                                                   configAddedTimeNs + bucketSizeNs + 100);
-//    processor->OnLogEvent(screenOffEvent.get());
-//
-//    processor->informPullAlarmFired(nextPullTimeNs + 1);
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs,
-//              nextPullTimeNs);
-//
-//    processor->informPullAlarmFired(nextPullTimeNs + 1);
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, nextPullTimeNs);
-//
-//    screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-//                                                  configAddedTimeNs + 3 * bucketSizeNs + 2);
-//    processor->OnLogEvent(screenOnEvent.get());
-//
-//    processor->informPullAlarmFired(nextPullTimeNs + 3);
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs);
-//
-//    screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-//                                                  configAddedTimeNs + 5 * bucketSizeNs + 1);
-//    processor->OnLogEvent(screenOffEvent.get());
-//
-//    processor->informPullAlarmFired(nextPullTimeNs + 2);
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, nextPullTimeNs);
-//
-//    processor->informPullAlarmFired(nextPullTimeNs + 2);
-//
-//    ConfigMetricsReportList reports;
-//    vector<uint8_t> buffer;
-//    processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
-//                            ADB_DUMP, FAST, &buffer);
-//    EXPECT_TRUE(buffer.size() > 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStringInReport(&reports);
-//    backfillStartEndTimestamp(&reports);
-//    EXPECT_EQ(1, reports.reports_size());
-//    EXPECT_EQ(1, reports.reports(0).metrics_size());
-//    StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
-//    sortMetricDataByDimensionsValue(
-//            reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
-//    EXPECT_GT((int)gaugeMetrics.data_size(), 1);
-//
-//    auto data = gaugeMetrics.data(0);
-//    EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* subsystem name field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
-//    EXPECT_EQ(6, data.bucket_info_size());
-//
-//    EXPECT_EQ(1, data.bucket_info(0).atom_size());
-//    EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
-//    EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
-//    EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size());
-//    EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-//    EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty());
-//    EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0);
-//
-//    EXPECT_EQ(1, data.bucket_info(1).atom_size());
-//    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1,
-//              data.bucket_info(1).elapsed_timestamp_nanos(0));
-//    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1, data.bucket_info(1).elapsed_timestamp_nanos(0));
-//    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
-//    EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty());
-//    EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0);
-//
-//    EXPECT_EQ(1, data.bucket_info(2).atom_size());
-//    EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
-//    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 1,
-//              data.bucket_info(2).elapsed_timestamp_nanos(0));
-//    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
-//    EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty());
-//    EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0);
-//
-//    EXPECT_EQ(1, data.bucket_info(3).atom_size());
-//    EXPECT_EQ(1, data.bucket_info(3).elapsed_timestamp_nanos_size());
-//    EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs + 1,
-//              data.bucket_info(3).elapsed_timestamp_nanos(0));
-//    EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos());
-//    EXPECT_TRUE(data.bucket_info(3).atom(0).subsystem_sleep_state().subsystem_name().empty());
-//    EXPECT_GT(data.bucket_info(3).atom(0).subsystem_sleep_state().time_millis(), 0);
-//
-//    EXPECT_EQ(1, data.bucket_info(4).atom_size());
-//    EXPECT_EQ(1, data.bucket_info(4).elapsed_timestamp_nanos_size());
-//    EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 1,
-//              data.bucket_info(4).elapsed_timestamp_nanos(0));
-//    EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(4).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(4).end_bucket_elapsed_nanos());
-//    EXPECT_TRUE(data.bucket_info(4).atom(0).subsystem_sleep_state().subsystem_name().empty());
-//    EXPECT_GT(data.bucket_info(4).atom(0).subsystem_sleep_state().time_millis(), 0);
-//
-//    EXPECT_EQ(1, data.bucket_info(5).atom_size());
-//    EXPECT_EQ(1, data.bucket_info(5).elapsed_timestamp_nanos_size());
-//    EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs + 2,
-//              data.bucket_info(5).elapsed_timestamp_nanos(0));
-//    EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(5).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(5).end_bucket_elapsed_nanos());
-//    EXPECT_TRUE(data.bucket_info(5).atom(0).subsystem_sleep_state().subsystem_name().empty());
-//    EXPECT_GT(data.bucket_info(5).atom(0).subsystem_sleep_state().time_millis(), 0);
-//}
-//
-//TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents) {
-//    auto config = CreateStatsdConfig(GaugeMetric::CONDITION_CHANGE_TO_TRUE);
-//    int64_t baseTimeNs = getElapsedRealtimeNs();
-//    int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
-//    int64_t bucketSizeNs =
-//        TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
-//
-//    ConfigKey cfgKey;
-//    auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
-//                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
-//                                             ATOM_TAG);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-//    processor->mPullerManager->ForceClearPullerCache();
-//
-//    int startBucketNum = processor->mMetricsManagers.begin()->second->
-//            mAllMetricProducers[0]->getCurrentBucketNum();
-//    EXPECT_GT(startBucketNum, (int64_t)0);
-//
-//    auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-//                                                        configAddedTimeNs + 55);
-//    processor->OnLogEvent(screenOffEvent.get());
-//
-//    auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-//                                                       configAddedTimeNs + bucketSizeNs + 10);
-//    processor->OnLogEvent(screenOnEvent.get());
-//
-//    screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-//                                                   configAddedTimeNs + bucketSizeNs + 100);
-//    processor->OnLogEvent(screenOffEvent.get());
-//
-//    screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-//                                                  configAddedTimeNs + 3 * bucketSizeNs + 2);
-//    processor->OnLogEvent(screenOnEvent.get());
-//
-//    screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-//                                                  configAddedTimeNs + 5 * bucketSizeNs + 1);
-//    processor->OnLogEvent(screenOffEvent.get());
-//    screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-//                                                  configAddedTimeNs + 5 * bucketSizeNs + 3);
-//    processor->OnLogEvent(screenOnEvent.get());
-//    screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-//                                                  configAddedTimeNs + 5 * bucketSizeNs + 10);
-//    processor->OnLogEvent(screenOffEvent.get());
-//
-//    ConfigMetricsReportList reports;
-//    vector<uint8_t> buffer;
-//    processor->onDumpReport(cfgKey, configAddedTimeNs + 8 * bucketSizeNs + 10, false, true,
-//                            ADB_DUMP, FAST, &buffer);
-//    EXPECT_TRUE(buffer.size() > 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStringInReport(&reports);
-//    backfillStartEndTimestamp(&reports);
-//    EXPECT_EQ(1, reports.reports_size());
-//    EXPECT_EQ(1, reports.reports(0).metrics_size());
-//    StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
-//    sortMetricDataByDimensionsValue(
-//            reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
-//    EXPECT_GT((int)gaugeMetrics.data_size(), 1);
-//
-//    auto data = gaugeMetrics.data(0);
-//    EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* subsystem name field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
-//    EXPECT_EQ(3, data.bucket_info_size());
-//
-//    EXPECT_EQ(1, data.bucket_info(0).atom_size());
-//    EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
-//    EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
-//    EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size());
-//    EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-//    EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty());
-//    EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0);
-//
-//    EXPECT_EQ(1, data.bucket_info(1).atom_size());
-//    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 100,
-//              data.bucket_info(1).elapsed_timestamp_nanos(0));
-//    EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
-//    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
-//    EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty());
-//    EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0);
-//
-//    EXPECT_EQ(2, data.bucket_info(2).atom_size());
-//    EXPECT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size());
-//    EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 1,
-//              data.bucket_info(2).elapsed_timestamp_nanos(0));
-//    EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 10,
-//              data.bucket_info(2).elapsed_timestamp_nanos(1));
-//    EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
-//    EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty());
-//    EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0);
-//    EXPECT_TRUE(data.bucket_info(2).atom(1).subsystem_sleep_state().subsystem_name().empty());
-//    EXPECT_GT(data.bucket_info(2).atom(1).subsystem_sleep_state().time_millis(), 0);
-//}
-//
-//
-//TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm) {
-//    auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE);
-//    int64_t baseTimeNs = getElapsedRealtimeNs();
-//    int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
-//    int64_t bucketSizeNs =
-//        TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
-//
-//    ConfigKey cfgKey;
-//    auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
-//                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
-//                                             ATOM_TAG);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-//    processor->mPullerManager->ForceClearPullerCache();
-//
-//    int startBucketNum = processor->mMetricsManagers.begin()->second->
-//            mAllMetricProducers[0]->getCurrentBucketNum();
-//    EXPECT_GT(startBucketNum, (int64_t)0);
-//
-//    // When creating the config, the gauge metric producer should register the alarm at the
-//    // end of the current bucket.
-//    EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
-//    EXPECT_EQ(bucketSizeNs,
-//              processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
-//    int64_t& nextPullTimeNs =
-//            processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs);
-//
-//    auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-//                                                        configAddedTimeNs + 55);
-//    processor->OnLogEvent(screenOffEvent.get());
-//
-//    auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-//                                                       configAddedTimeNs + bucketSizeNs + 10);
-//    processor->OnLogEvent(screenOnEvent.get());
-//
-//    // Pulling alarm arrives one bucket size late.
-//    processor->informPullAlarmFired(nextPullTimeNs + bucketSizeNs);
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, nextPullTimeNs);
-//
-//    screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-//                                                   configAddedTimeNs + 3 * bucketSizeNs + 11);
-//    processor->OnLogEvent(screenOffEvent.get());
-//
-//    // Pulling alarm arrives more than one bucket size late.
-//    processor->informPullAlarmFired(nextPullTimeNs + bucketSizeNs + 12);
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs);
-//
-//    ConfigMetricsReportList reports;
-//    vector<uint8_t> buffer;
-//    processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
-//                            ADB_DUMP, FAST, &buffer);
-//    EXPECT_TRUE(buffer.size() > 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStringInReport(&reports);
-//    backfillStartEndTimestamp(&reports);
-//    EXPECT_EQ(1, reports.reports_size());
-//    EXPECT_EQ(1, reports.reports(0).metrics_size());
-//    StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
-//    sortMetricDataByDimensionsValue(
-//            reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
-//    EXPECT_GT((int)gaugeMetrics.data_size(), 1);
-//
-//    auto data = gaugeMetrics.data(0);
-//    EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* subsystem name field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
-//    EXPECT_EQ(3, data.bucket_info_size());
-//
-//    EXPECT_EQ(1, data.bucket_info(0).atom_size());
-//    EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
-//    EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
-//    EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-//    EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty());
-//    EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0);
-//
-//    EXPECT_EQ(1, data.bucket_info(1).atom_size());
-//    EXPECT_EQ(configAddedTimeNs + 3 * bucketSizeNs + 11,
-//              data.bucket_info(1).elapsed_timestamp_nanos(0));
-//    EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
-//    EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
-//    EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty());
-//    EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0);
-//
-//    EXPECT_EQ(1, data.bucket_info(2).atom_size());
-//    EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
-//    EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs + 12,
-//              data.bucket_info(2).elapsed_timestamp_nanos(0));
-//    EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
-//    EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty());
-//    EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0);
-//}
-//
-//TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation) {
-//    auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE, /*useCondition=*/false);
-//
-//    int64_t baseTimeNs = getElapsedRealtimeNs();
-//    int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
-//    int64_t bucketSizeNs =
-//        TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
-//
-//    auto batterySaverStartMatcher = CreateBatterySaverModeStartAtomMatcher();
-//    *config.add_atom_matcher() = batterySaverStartMatcher;
-//    const int64_t ttlNs = 2 * bucketSizeNs; // Two buckets.
-//    auto metric_activation = config.add_metric_activation();
-//    metric_activation->set_metric_id(metricId);
-//    metric_activation->set_activation_type(ACTIVATE_IMMEDIATELY);
-//    auto event_activation = metric_activation->add_event_activation();
-//    event_activation->set_atom_matcher_id(batterySaverStartMatcher.id());
-//    event_activation->set_ttl_seconds(ttlNs / 1000000000);
-//
-//    ConfigKey cfgKey;
-//    auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
-//                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
-//                                             ATOM_TAG);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-//    processor->mPullerManager->ForceClearPullerCache();
-//
-//    int startBucketNum = processor->mMetricsManagers.begin()->second->
-//            mAllMetricProducers[0]->getCurrentBucketNum();
-//    EXPECT_GT(startBucketNum, (int64_t)0);
-//    EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
-//
-//    // When creating the config, the gauge metric producer should register the alarm at the
-//    // end of the current bucket.
-//    EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
-//    EXPECT_EQ(bucketSizeNs,
-//              processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
-//    int64_t& nextPullTimeNs =
-//            processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs);
-//
-//    // Pulling alarm arrives on time and reset the sequential pulling alarm.
-//    // Event should not be kept.
-//    processor->informPullAlarmFired(nextPullTimeNs + 1); // 15 mins + 1 ns.
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, nextPullTimeNs);
-//    EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
-//
-//    // Activate the metric. A pull occurs upon activation.
-//    const int64_t activationNs = configAddedTimeNs + bucketSizeNs + (2 * 1000 * 1000); // 2 millis.
-//    auto batterySaverOnEvent = CreateBatterySaverOnEvent(activationNs);
-//    processor->OnLogEvent(batterySaverOnEvent.get()); // 15 mins + 2 ms.
-//    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
-//
-//    // This event should be kept. 2 total.
-//    processor->informPullAlarmFired(nextPullTimeNs + 1); // 20 mins + 1 ns.
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs,
-//              nextPullTimeNs);
-//
-//    // This event should be kept. 3 total.
-//    processor->informPullAlarmFired(nextPullTimeNs + 2); // 25 mins + 2 ns.
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, nextPullTimeNs);
-//
-//    // Create random event to deactivate metric.
-//    auto deactivationEvent = CreateScreenBrightnessChangedEvent(50, activationNs + ttlNs + 1);
-//    processor->OnLogEvent(deactivationEvent.get());
-//    EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
-//
-//    // Event should not be kept. 3 total.
-//    processor->informPullAlarmFired(nextPullTimeNs + 3);
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs);
-//
-//    processor->informPullAlarmFired(nextPullTimeNs + 2);
-//
-//    ConfigMetricsReportList reports;
-//    vector<uint8_t> buffer;
-//    processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
-//                            ADB_DUMP, FAST, &buffer);
-//    EXPECT_TRUE(buffer.size() > 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStringInReport(&reports);
-//    backfillStartEndTimestamp(&reports);
-//    EXPECT_EQ(1, reports.reports_size());
-//    EXPECT_EQ(1, reports.reports(0).metrics_size());
-//    StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
-//    sortMetricDataByDimensionsValue(
-//            reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
-//    EXPECT_GT((int)gaugeMetrics.data_size(), 0);
-//
-//    auto data = gaugeMetrics.data(0);
-//    EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* subsystem name field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
-//    EXPECT_EQ(3, data.bucket_info_size());
-//
-//    auto bucketInfo = data.bucket_info(0);
-//    EXPECT_EQ(1, bucketInfo.atom_size());
-//    EXPECT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size());
-//    EXPECT_EQ(activationNs, bucketInfo.elapsed_timestamp_nanos(0));
-//    EXPECT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size());
-//    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos());
-//    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
-//    EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty());
-//    EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0);
-//
-//    bucketInfo = data.bucket_info(1);
-//    EXPECT_EQ(1, bucketInfo.atom_size());
-//    EXPECT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size());
-//    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 1, bucketInfo.elapsed_timestamp_nanos(0));
-//    EXPECT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size());
-//    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos());
-//    EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
-//    EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty());
-//    EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0);
-//
-//    bucketInfo = data.bucket_info(2);
-//    EXPECT_EQ(1, bucketInfo.atom_size());
-//    EXPECT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size());
-//    EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs + 2, bucketInfo.elapsed_timestamp_nanos(0));
-//    EXPECT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size());
-//    EXPECT_EQ(MillisToNano(NanoToMillis(baseTimeNs + 5 * bucketSizeNs)),
-//            bucketInfo.start_bucket_elapsed_nanos());
-//    EXPECT_EQ(MillisToNano(NanoToMillis(activationNs + ttlNs + 1)),
-//            bucketInfo.end_bucket_elapsed_nanos());
-//    EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty());
-//    EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0);
-//}
+TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) {
+    auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE);
+    int64_t baseTimeNs = getElapsedRealtimeNs();
+    int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
+
+    ConfigKey cfgKey;
+    auto processor =
+            CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+                                    SharedRefBase::make<FakeSubsystemSleepCallback>(), ATOM_TAG);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    processor->mPullerManager->ForceClearPullerCache();
+
+    int startBucketNum = processor->mMetricsManagers.begin()
+                                 ->second->mAllMetricProducers[0]
+                                 ->getCurrentBucketNum();
+    EXPECT_GT(startBucketNum, (int64_t)0);
+
+    // When creating the config, the gauge metric producer should register the alarm at the
+    // end of the current bucket.
+    EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
+    EXPECT_EQ(bucketSizeNs,
+              processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
+    int64_t& nextPullTimeNs =
+            processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs);
+
+    auto screenOffEvent =
+            CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    // Pulling alarm arrives on time and reset the sequential pulling alarm.
+    processor->informPullAlarmFired(nextPullTimeNs + 1);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, nextPullTimeNs);
+
+    auto screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 10,
+                                                       android::view::DISPLAY_STATE_ON);
+    processor->OnLogEvent(screenOnEvent.get());
+
+    screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 100,
+                                                   android::view::DISPLAY_STATE_OFF);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    processor->informPullAlarmFired(nextPullTimeNs + 1);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, nextPullTimeNs);
+
+    processor->informPullAlarmFired(nextPullTimeNs + 1);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, nextPullTimeNs);
+
+    screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 3 * bucketSizeNs + 2,
+                                                  android::view::DISPLAY_STATE_ON);
+    processor->OnLogEvent(screenOnEvent.get());
+
+    processor->informPullAlarmFired(nextPullTimeNs + 3);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs);
+
+    screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 5 * bucketSizeNs + 1,
+                                                   android::view::DISPLAY_STATE_OFF);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    processor->informPullAlarmFired(nextPullTimeNs + 2);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, nextPullTimeNs);
+
+    processor->informPullAlarmFired(nextPullTimeNs + 2);
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
+                            ADB_DUMP, FAST, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+    EXPECT_GT((int)gaugeMetrics.data_size(), 1);
+
+    auto data = gaugeMetrics.data(0);
+    EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* subsystem name field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
+    EXPECT_EQ(6, data.bucket_info_size());
+
+    EXPECT_EQ(1, data.bucket_info(0).atom_size());
+    EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
+    EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size());
+    EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+    EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0);
+
+    EXPECT_EQ(1, data.bucket_info(1).atom_size());
+    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1, data.bucket_info(1).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1, data.bucket_info(1).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
+    EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0);
+
+    EXPECT_EQ(1, data.bucket_info(2).atom_size());
+    EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
+    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 1, data.bucket_info(2).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
+    EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0);
+
+    EXPECT_EQ(1, data.bucket_info(3).atom_size());
+    EXPECT_EQ(1, data.bucket_info(3).elapsed_timestamp_nanos_size());
+    EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs + 1, data.bucket_info(3).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos());
+    EXPECT_TRUE(data.bucket_info(3).atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(3).atom(0).subsystem_sleep_state().time_millis(), 0);
+
+    EXPECT_EQ(1, data.bucket_info(4).atom_size());
+    EXPECT_EQ(1, data.bucket_info(4).elapsed_timestamp_nanos_size());
+    EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 1, data.bucket_info(4).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(4).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(4).end_bucket_elapsed_nanos());
+    EXPECT_TRUE(data.bucket_info(4).atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(4).atom(0).subsystem_sleep_state().time_millis(), 0);
+
+    EXPECT_EQ(1, data.bucket_info(5).atom_size());
+    EXPECT_EQ(1, data.bucket_info(5).elapsed_timestamp_nanos_size());
+    EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs + 2, data.bucket_info(5).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(5).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(5).end_bucket_elapsed_nanos());
+    EXPECT_TRUE(data.bucket_info(5).atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(5).atom(0).subsystem_sleep_state().time_millis(), 0);
+}
+
+TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents) {
+    auto config = CreateStatsdConfig(GaugeMetric::CONDITION_CHANGE_TO_TRUE);
+    int64_t baseTimeNs = getElapsedRealtimeNs();
+    int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
+
+    ConfigKey cfgKey;
+    auto processor =
+            CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+                                    SharedRefBase::make<FakeSubsystemSleepCallback>(), ATOM_TAG);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    processor->mPullerManager->ForceClearPullerCache();
+
+    int startBucketNum = processor->mMetricsManagers.begin()
+                                 ->second->mAllMetricProducers[0]
+                                 ->getCurrentBucketNum();
+    EXPECT_GT(startBucketNum, (int64_t)0);
+
+    auto screenOffEvent =
+            CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    auto screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 10,
+                                                       android::view::DISPLAY_STATE_ON);
+    processor->OnLogEvent(screenOnEvent.get());
+
+    screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 100,
+                                                   android::view::DISPLAY_STATE_OFF);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 3 * bucketSizeNs + 2,
+                                                  android::view::DISPLAY_STATE_ON);
+    processor->OnLogEvent(screenOnEvent.get());
+
+    screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 5 * bucketSizeNs + 1,
+                                                   android::view::DISPLAY_STATE_OFF);
+    processor->OnLogEvent(screenOffEvent.get());
+    screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 5 * bucketSizeNs + 3,
+                                                  android::view::DISPLAY_STATE_ON);
+    processor->OnLogEvent(screenOnEvent.get());
+    screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 5 * bucketSizeNs + 10,
+                                                   android::view::DISPLAY_STATE_OFF);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 8 * bucketSizeNs + 10, false, true,
+                            ADB_DUMP, FAST, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+    EXPECT_GT((int)gaugeMetrics.data_size(), 1);
+
+    auto data = gaugeMetrics.data(0);
+    EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* subsystem name field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
+    EXPECT_EQ(3, data.bucket_info_size());
+
+    EXPECT_EQ(1, data.bucket_info(0).atom_size());
+    EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
+    EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size());
+    EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+    EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0);
+
+    EXPECT_EQ(1, data.bucket_info(1).atom_size());
+    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 100, data.bucket_info(1).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
+    EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0);
+
+    EXPECT_EQ(2, data.bucket_info(2).atom_size());
+    EXPECT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size());
+    EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 1, data.bucket_info(2).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 10, data.bucket_info(2).elapsed_timestamp_nanos(1));
+    EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
+    EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0);
+    EXPECT_TRUE(data.bucket_info(2).atom(1).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(2).atom(1).subsystem_sleep_state().time_millis(), 0);
+}
+
+TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm) {
+    auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE);
+    int64_t baseTimeNs = getElapsedRealtimeNs();
+    int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
+
+    ConfigKey cfgKey;
+    auto processor =
+            CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+                                    SharedRefBase::make<FakeSubsystemSleepCallback>(), ATOM_TAG);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    processor->mPullerManager->ForceClearPullerCache();
+
+    int startBucketNum = processor->mMetricsManagers.begin()
+                                 ->second->mAllMetricProducers[0]
+                                 ->getCurrentBucketNum();
+    EXPECT_GT(startBucketNum, (int64_t)0);
+
+    // When creating the config, the gauge metric producer should register the alarm at the
+    // end of the current bucket.
+    EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
+    EXPECT_EQ(bucketSizeNs,
+              processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
+    int64_t& nextPullTimeNs =
+            processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs);
+
+    auto screenOffEvent =
+            CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    auto screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 10,
+                                                       android::view::DISPLAY_STATE_ON);
+    processor->OnLogEvent(screenOnEvent.get());
+
+    // Pulling alarm arrives one bucket size late.
+    processor->informPullAlarmFired(nextPullTimeNs + bucketSizeNs);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, nextPullTimeNs);
+
+    screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 3 * bucketSizeNs + 11,
+                                                   android::view::DISPLAY_STATE_OFF);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    // Pulling alarm arrives more than one bucket size late.
+    processor->informPullAlarmFired(nextPullTimeNs + bucketSizeNs + 12);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs);
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
+                            ADB_DUMP, FAST, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+    EXPECT_GT((int)gaugeMetrics.data_size(), 1);
+
+    auto data = gaugeMetrics.data(0);
+    EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* subsystem name field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
+    EXPECT_EQ(3, data.bucket_info_size());
+
+    EXPECT_EQ(1, data.bucket_info(0).atom_size());
+    EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
+    EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+    EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0);
+
+    EXPECT_EQ(1, data.bucket_info(1).atom_size());
+    EXPECT_EQ(configAddedTimeNs + 3 * bucketSizeNs + 11,
+              data.bucket_info(1).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
+    EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0);
+
+    EXPECT_EQ(1, data.bucket_info(2).atom_size());
+    EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
+    EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs + 12, data.bucket_info(2).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
+    EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0);
+}
+
+TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation) {
+    auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE, /*useCondition=*/false);
+
+    int64_t baseTimeNs = getElapsedRealtimeNs();
+    int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
+
+    auto batterySaverStartMatcher = CreateBatterySaverModeStartAtomMatcher();
+    *config.add_atom_matcher() = batterySaverStartMatcher;
+    const int64_t ttlNs = 2 * bucketSizeNs;  // Two buckets.
+    auto metric_activation = config.add_metric_activation();
+    metric_activation->set_metric_id(metricId);
+    metric_activation->set_activation_type(ACTIVATE_IMMEDIATELY);
+    auto event_activation = metric_activation->add_event_activation();
+    event_activation->set_atom_matcher_id(batterySaverStartMatcher.id());
+    event_activation->set_ttl_seconds(ttlNs / 1000000000);
+
+    ConfigKey cfgKey;
+    auto processor =
+            CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+                                    SharedRefBase::make<FakeSubsystemSleepCallback>(), ATOM_TAG);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    processor->mPullerManager->ForceClearPullerCache();
+
+    int startBucketNum = processor->mMetricsManagers.begin()
+                                 ->second->mAllMetricProducers[0]
+                                 ->getCurrentBucketNum();
+    EXPECT_GT(startBucketNum, (int64_t)0);
+    EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
+
+    // When creating the config, the gauge metric producer should register the alarm at the
+    // end of the current bucket.
+    EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
+    EXPECT_EQ(bucketSizeNs,
+              processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
+    int64_t& nextPullTimeNs =
+            processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs);
+
+    // Pulling alarm arrives on time and reset the sequential pulling alarm.
+    // Event should not be kept.
+    processor->informPullAlarmFired(nextPullTimeNs + 1);  // 15 mins + 1 ns.
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, nextPullTimeNs);
+    EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
+
+    // Activate the metric. A pull occurs upon activation.
+    const int64_t activationNs = configAddedTimeNs + bucketSizeNs + (2 * 1000 * 1000);  // 2 millis.
+    auto batterySaverOnEvent = CreateBatterySaverOnEvent(activationNs);
+    processor->OnLogEvent(batterySaverOnEvent.get());  // 15 mins + 2 ms.
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
+
+    // This event should be kept. 2 total.
+    processor->informPullAlarmFired(nextPullTimeNs + 1);  // 20 mins + 1 ns.
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, nextPullTimeNs);
+
+    // This event should be kept. 3 total.
+    processor->informPullAlarmFired(nextPullTimeNs + 2);  // 25 mins + 2 ns.
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, nextPullTimeNs);
+
+    // Create random event to deactivate metric.
+    auto deactivationEvent = CreateScreenBrightnessChangedEvent(activationNs + ttlNs + 1, 50);
+    processor->OnLogEvent(deactivationEvent.get());
+    EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
+
+    // Event should not be kept. 3 total.
+    processor->informPullAlarmFired(nextPullTimeNs + 3);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs);
+
+    processor->informPullAlarmFired(nextPullTimeNs + 2);
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
+                            ADB_DUMP, FAST, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+    EXPECT_GT((int)gaugeMetrics.data_size(), 0);
+
+    auto data = gaugeMetrics.data(0);
+    EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* subsystem name field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
+    EXPECT_EQ(3, data.bucket_info_size());
+
+    auto bucketInfo = data.bucket_info(0);
+    EXPECT_EQ(1, bucketInfo.atom_size());
+    EXPECT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size());
+    EXPECT_EQ(activationNs, bucketInfo.elapsed_timestamp_nanos(0));
+    EXPECT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size());
+    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
+    EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0);
+
+    bucketInfo = data.bucket_info(1);
+    EXPECT_EQ(1, bucketInfo.atom_size());
+    EXPECT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size());
+    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 1, bucketInfo.elapsed_timestamp_nanos(0));
+    EXPECT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size());
+    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
+    EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0);
+
+    bucketInfo = data.bucket_info(2);
+    EXPECT_EQ(1, bucketInfo.atom_size());
+    EXPECT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size());
+    EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs + 2, bucketInfo.elapsed_timestamp_nanos(0));
+    EXPECT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size());
+    EXPECT_EQ(MillisToNano(NanoToMillis(baseTimeNs + 5 * bucketSizeNs)),
+              bucketInfo.start_bucket_elapsed_nanos());
+    EXPECT_EQ(MillisToNano(NanoToMillis(activationNs + ttlNs + 1)),
+              bucketInfo.end_bucket_elapsed_nanos());
+    EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty());
+    EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0);
+}
 
 TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition) {
     auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE, /*useCondition=*/false);
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
index ef6e753..6e3d93a 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
@@ -14,12 +14,13 @@
 
 #include <gtest/gtest.h>
 
+#include <vector>
+
 #include "src/StatsLogProcessor.h"
 #include "src/stats_log_util.h"
+#include "stats_event.h"
 #include "tests/statsd_test_util.h"
 
-#include <vector>
-
 namespace android {
 namespace os {
 namespace statsd {
@@ -68,221 +69,227 @@
     return config;
 }
 
-// TODO(b/149590301): Update this helper to use new socket schema.
-//std::unique_ptr<LogEvent> CreateAppStartOccurredEvent(
-//    const int uid, const string& pkg_name, AppStartOccurred::TransitionType type,
-//    const string& activity_name, const string& calling_pkg_name, const bool is_instant_app,
-//    int64_t activity_start_msec, uint64_t timestampNs) {
-//    auto logEvent = std::make_unique<LogEvent>(
-//        android::util::APP_START_OCCURRED, timestampNs);
-//    logEvent->write(uid);
-//    logEvent->write(pkg_name);
-//    logEvent->write(type);
-//    logEvent->write(activity_name);
-//    logEvent->write(calling_pkg_name);
-//    logEvent->write(is_instant_app);
-//    logEvent->write(activity_start_msec);
-//    logEvent->init();
-//    return logEvent;
-//}
+std::unique_ptr<LogEvent> CreateAppStartOccurredEvent(
+        uint64_t timestampNs, const int uid, const string& pkg_name,
+        AppStartOccurred::TransitionType type, const string& activity_name,
+        const string& calling_pkg_name, const bool is_instant_app, int64_t activity_start_msec) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, android::util::APP_START_OCCURRED);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+    AStatsEvent_writeInt32(statsEvent, uid);
+    AStatsEvent_writeString(statsEvent, pkg_name.c_str());
+    AStatsEvent_writeInt32(statsEvent, type);
+    AStatsEvent_writeString(statsEvent, activity_name.c_str());
+    AStatsEvent_writeString(statsEvent, calling_pkg_name.c_str());
+    AStatsEvent_writeInt32(statsEvent, is_instant_app);
+    AStatsEvent_writeInt32(statsEvent, activity_start_msec);
+    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;
+}
 
 }  // namespace
 
-// TODO(b/149590301): Update this test to use new socket schema.
-//TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) {
-//    for (const auto& sampling_type :
-//            { GaugeMetric::FIRST_N_SAMPLES, GaugeMetric:: RANDOM_ONE_SAMPLE }) {
-//        auto config = CreateStatsdConfigForPushedEvent(sampling_type);
-//        int64_t bucketStartTimeNs = 10000000000;
-//        int64_t bucketSizeNs =
-//            TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
-//
-//        ConfigKey cfgKey;
-//        auto processor = CreateStatsLogProcessor(
-//                bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-//        EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//        EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-//
-//        int appUid1 = 123;
-//        int appUid2 = 456;
-//        std::vector<std::unique_ptr<LogEvent>> events;
-//        events.push_back(CreateMoveToBackgroundEvent(appUid1, bucketStartTimeNs + 15));
-//        events.push_back(CreateMoveToForegroundEvent(
-//                appUid1, bucketStartTimeNs + bucketSizeNs + 250));
-//        events.push_back(CreateMoveToBackgroundEvent(
-//                appUid1, bucketStartTimeNs + bucketSizeNs + 350));
-//        events.push_back(CreateMoveToForegroundEvent(
-//            appUid1, bucketStartTimeNs + 2 * bucketSizeNs + 100));
-//
-//
-//        events.push_back(CreateAppStartOccurredEvent(
-//            appUid1, "app1", AppStartOccurred::WARM, "activity_name1", "calling_pkg_name1",
-//            true /*is_instant_app*/, 101 /*activity_start_msec*/, bucketStartTimeNs + 10));
-//        events.push_back(CreateAppStartOccurredEvent(
-//            appUid1, "app1", AppStartOccurred::HOT, "activity_name2", "calling_pkg_name2",
-//            true /*is_instant_app*/, 102 /*activity_start_msec*/, bucketStartTimeNs + 20));
-//        events.push_back(CreateAppStartOccurredEvent(
-//            appUid1, "app1", AppStartOccurred::COLD, "activity_name3", "calling_pkg_name3",
-//            true /*is_instant_app*/, 103 /*activity_start_msec*/, bucketStartTimeNs + 30));
-//        events.push_back(CreateAppStartOccurredEvent(
-//            appUid1, "app1", AppStartOccurred::WARM, "activity_name4", "calling_pkg_name4",
-//            true /*is_instant_app*/, 104 /*activity_start_msec*/,
-//            bucketStartTimeNs + bucketSizeNs + 30));
-//        events.push_back(CreateAppStartOccurredEvent(
-//            appUid1, "app1", AppStartOccurred::COLD, "activity_name5", "calling_pkg_name5",
-//            true /*is_instant_app*/, 105 /*activity_start_msec*/,
-//            bucketStartTimeNs + 2 * bucketSizeNs));
-//        events.push_back(CreateAppStartOccurredEvent(
-//            appUid1, "app1", AppStartOccurred::HOT, "activity_name6", "calling_pkg_name6",
-//            false /*is_instant_app*/, 106 /*activity_start_msec*/,
-//            bucketStartTimeNs + 2 * bucketSizeNs + 10));
-//
-//        events.push_back(CreateMoveToBackgroundEvent(
-//                appUid2, bucketStartTimeNs + bucketSizeNs + 10));
-//        events.push_back(CreateAppStartOccurredEvent(
-//            appUid2, "app2", AppStartOccurred::COLD, "activity_name7", "calling_pkg_name7",
-//            true /*is_instant_app*/, 201 /*activity_start_msec*/,
-//            bucketStartTimeNs + 2 * bucketSizeNs + 10));
-//
-//        sortLogEventsByTimestamp(&events);
-//
-//        for (const auto& event : events) {
-//            processor->OnLogEvent(event.get());
-//        }
-//        ConfigMetricsReportList reports;
-//        vector<uint8_t> buffer;
-//        processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, false, true,
-//                                ADB_DUMP, FAST, &buffer);
-//        EXPECT_TRUE(buffer.size() > 0);
-//        EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//        backfillDimensionPath(&reports);
-//        backfillStringInReport(&reports);
-//        backfillStartEndTimestamp(&reports);
-//        EXPECT_EQ(1, reports.reports_size());
-//        EXPECT_EQ(1, reports.reports(0).metrics_size());
-//        StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
-//        sortMetricDataByDimensionsValue(
-//                reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
-//        EXPECT_EQ(2, gaugeMetrics.data_size());
-//
-//        auto data = gaugeMetrics.data(0);
-//        EXPECT_EQ(android::util::APP_START_OCCURRED, data.dimensions_in_what().field());
-//        EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//        EXPECT_EQ(1 /* uid field */,
-//                  data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//        EXPECT_EQ(appUid1, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//        EXPECT_EQ(3, data.bucket_info_size());
-//        if (sampling_type == GaugeMetric::FIRST_N_SAMPLES) {
-//            EXPECT_EQ(2, data.bucket_info(0).atom_size());
-//            EXPECT_EQ(2, data.bucket_info(0).elapsed_timestamp_nanos_size());
-//            EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size());
-//            EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//            EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
-//                      data.bucket_info(0).end_bucket_elapsed_nanos());
-//            EXPECT_EQ(AppStartOccurred::HOT,
-//                      data.bucket_info(0).atom(0).app_start_occurred().type());
-//            EXPECT_EQ("activity_name2",
-//                      data.bucket_info(0).atom(0).app_start_occurred().activity_name());
-//            EXPECT_EQ(102L,
-//                      data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis());
-//            EXPECT_EQ(AppStartOccurred::COLD,
-//                      data.bucket_info(0).atom(1).app_start_occurred().type());
-//            EXPECT_EQ("activity_name3",
-//                      data.bucket_info(0).atom(1).app_start_occurred().activity_name());
-//            EXPECT_EQ(103L,
-//                      data.bucket_info(0).atom(1).app_start_occurred().activity_start_millis());
-//
-//            EXPECT_EQ(1, data.bucket_info(1).atom_size());
-//            EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size());
-//            EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
-//                      data.bucket_info(1).start_bucket_elapsed_nanos());
-//            EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
-//                      data.bucket_info(1).end_bucket_elapsed_nanos());
-//            EXPECT_EQ(AppStartOccurred::WARM,
-//                      data.bucket_info(1).atom(0).app_start_occurred().type());
-//            EXPECT_EQ("activity_name4",
-//                      data.bucket_info(1).atom(0).app_start_occurred().activity_name());
-//            EXPECT_EQ(104L,
-//                      data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis());
-//
-//            EXPECT_EQ(2, data.bucket_info(2).atom_size());
-//            EXPECT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size());
-//            EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
-//                      data.bucket_info(2).start_bucket_elapsed_nanos());
-//            EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
-//                      data.bucket_info(2).end_bucket_elapsed_nanos());
-//            EXPECT_EQ(AppStartOccurred::COLD,
-//                      data.bucket_info(2).atom(0).app_start_occurred().type());
-//            EXPECT_EQ("activity_name5",
-//                      data.bucket_info(2).atom(0).app_start_occurred().activity_name());
-//            EXPECT_EQ(105L,
-//                      data.bucket_info(2).atom(0).app_start_occurred().activity_start_millis());
-//            EXPECT_EQ(AppStartOccurred::HOT,
-//                      data.bucket_info(2).atom(1).app_start_occurred().type());
-//            EXPECT_EQ("activity_name6",
-//                      data.bucket_info(2).atom(1).app_start_occurred().activity_name());
-//            EXPECT_EQ(106L,
-//                      data.bucket_info(2).atom(1).app_start_occurred().activity_start_millis());
-//        } else {
-//            EXPECT_EQ(1, data.bucket_info(0).atom_size());
-//            EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
-//            EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//            EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
-//                      data.bucket_info(0).end_bucket_elapsed_nanos());
-//            EXPECT_EQ(AppStartOccurred::HOT,
-//                      data.bucket_info(0).atom(0).app_start_occurred().type());
-//            EXPECT_EQ("activity_name2",
-//                      data.bucket_info(0).atom(0).app_start_occurred().activity_name());
-//            EXPECT_EQ(102L,
-//                      data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis());
-//
-//            EXPECT_EQ(1, data.bucket_info(1).atom_size());
-//            EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size());
-//            EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
-//                      data.bucket_info(1).start_bucket_elapsed_nanos());
-//            EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
-//                      data.bucket_info(1).end_bucket_elapsed_nanos());
-//            EXPECT_EQ(AppStartOccurred::WARM,
-//                      data.bucket_info(1).atom(0).app_start_occurred().type());
-//            EXPECT_EQ("activity_name4",
-//                      data.bucket_info(1).atom(0).app_start_occurred().activity_name());
-//            EXPECT_EQ(104L,
-//                      data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis());
-//
-//            EXPECT_EQ(1, data.bucket_info(2).atom_size());
-//            EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
-//            EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
-//                      data.bucket_info(2).start_bucket_elapsed_nanos());
-//            EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
-//                      data.bucket_info(2).end_bucket_elapsed_nanos());
-//            EXPECT_EQ(AppStartOccurred::COLD,
-//                      data.bucket_info(2).atom(0).app_start_occurred().type());
-//            EXPECT_EQ("activity_name5",
-//                      data.bucket_info(2).atom(0).app_start_occurred().activity_name());
-//            EXPECT_EQ(105L,
-//                      data.bucket_info(2).atom(0).app_start_occurred().activity_start_millis());
-//        }
-//
-//        data = gaugeMetrics.data(1);
-//
-//        EXPECT_EQ(data.dimensions_in_what().field(), android::util::APP_START_OCCURRED);
-//        EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
-//        EXPECT_EQ(1 /* uid field */,
-//                  data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//        EXPECT_EQ(appUid2, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//        EXPECT_EQ(1, data.bucket_info_size());
-//        EXPECT_EQ(1, data.bucket_info(0).atom_size());
-//        EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
-//        EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
-//                  data.bucket_info(0).start_bucket_elapsed_nanos());
-//        EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
-//                  data.bucket_info(0).end_bucket_elapsed_nanos());
-//        EXPECT_EQ(AppStartOccurred::COLD, data.bucket_info(0).atom(0).app_start_occurred().type());
-//        EXPECT_EQ("activity_name7",
-//                  data.bucket_info(0).atom(0).app_start_occurred().activity_name());
-//        EXPECT_EQ(201L, data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis());
-//    }
-//}
+TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) {
+    for (const auto& sampling_type :
+         {GaugeMetric::FIRST_N_SAMPLES, GaugeMetric::RANDOM_ONE_SAMPLE}) {
+        auto config = CreateStatsdConfigForPushedEvent(sampling_type);
+        int64_t bucketStartTimeNs = 10000000000;
+        int64_t bucketSizeNs =
+                TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
+
+        ConfigKey cfgKey;
+        auto processor =
+                CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+        EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+        EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+        int appUid1 = 123;
+        int appUid2 = 456;
+        std::vector<std::unique_ptr<LogEvent>> events;
+        events.push_back(CreateMoveToBackgroundEvent(bucketStartTimeNs + 15, appUid1));
+        events.push_back(
+                CreateMoveToForegroundEvent(bucketStartTimeNs + bucketSizeNs + 250, appUid1));
+        events.push_back(
+                CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 350, appUid1));
+        events.push_back(
+                CreateMoveToForegroundEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100, appUid1));
+
+        events.push_back(CreateAppStartOccurredEvent(
+                bucketStartTimeNs + 10, appUid1, "app1", AppStartOccurred::WARM, "activity_name1",
+                "calling_pkg_name1", true /*is_instant_app*/, 101 /*activity_start_msec*/));
+        events.push_back(CreateAppStartOccurredEvent(
+                bucketStartTimeNs + 20, appUid1, "app1", AppStartOccurred::HOT, "activity_name2",
+                "calling_pkg_name2", true /*is_instant_app*/, 102 /*activity_start_msec*/));
+        events.push_back(CreateAppStartOccurredEvent(
+                bucketStartTimeNs + 30, appUid1, "app1", AppStartOccurred::COLD, "activity_name3",
+                "calling_pkg_name3", true /*is_instant_app*/, 103 /*activity_start_msec*/));
+        events.push_back(CreateAppStartOccurredEvent(
+                bucketStartTimeNs + bucketSizeNs + 30, appUid1, "app1", AppStartOccurred::WARM,
+                "activity_name4", "calling_pkg_name4", true /*is_instant_app*/,
+                104 /*activity_start_msec*/));
+        events.push_back(CreateAppStartOccurredEvent(
+                bucketStartTimeNs + 2 * bucketSizeNs, appUid1, "app1", AppStartOccurred::COLD,
+                "activity_name5", "calling_pkg_name5", true /*is_instant_app*/,
+                105 /*activity_start_msec*/));
+        events.push_back(CreateAppStartOccurredEvent(
+                bucketStartTimeNs + 2 * bucketSizeNs + 10, appUid1, "app1", AppStartOccurred::HOT,
+                "activity_name6", "calling_pkg_name6", false /*is_instant_app*/,
+                106 /*activity_start_msec*/));
+
+        events.push_back(
+                CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 10, appUid2));
+        events.push_back(CreateAppStartOccurredEvent(
+                bucketStartTimeNs + 2 * bucketSizeNs + 10, appUid2, "app2", AppStartOccurred::COLD,
+                "activity_name7", "calling_pkg_name7", true /*is_instant_app*/,
+                201 /*activity_start_msec*/));
+
+        sortLogEventsByTimestamp(&events);
+
+        for (const auto& event : events) {
+            processor->OnLogEvent(event.get());
+        }
+        ConfigMetricsReportList reports;
+        vector<uint8_t> buffer;
+        processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, false, true, ADB_DUMP,
+                                FAST, &buffer);
+        EXPECT_TRUE(buffer.size() > 0);
+        EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+        backfillDimensionPath(&reports);
+        backfillStringInReport(&reports);
+        backfillStartEndTimestamp(&reports);
+        EXPECT_EQ(1, reports.reports_size());
+        EXPECT_EQ(1, reports.reports(0).metrics_size());
+        StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+        sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(),
+                                        &gaugeMetrics);
+        EXPECT_EQ(2, gaugeMetrics.data_size());
+
+        auto data = gaugeMetrics.data(0);
+        EXPECT_EQ(android::util::APP_START_OCCURRED, data.dimensions_in_what().field());
+        EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+        EXPECT_EQ(1 /* uid field */,
+                  data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+        EXPECT_EQ(appUid1, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+        EXPECT_EQ(3, data.bucket_info_size());
+        if (sampling_type == GaugeMetric::FIRST_N_SAMPLES) {
+            EXPECT_EQ(2, data.bucket_info(0).atom_size());
+            EXPECT_EQ(2, data.bucket_info(0).elapsed_timestamp_nanos_size());
+            EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size());
+            EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+            EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
+                      data.bucket_info(0).end_bucket_elapsed_nanos());
+            EXPECT_EQ(AppStartOccurred::HOT,
+                      data.bucket_info(0).atom(0).app_start_occurred().type());
+            EXPECT_EQ("activity_name2",
+                      data.bucket_info(0).atom(0).app_start_occurred().activity_name());
+            EXPECT_EQ(102L,
+                      data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis());
+            EXPECT_EQ(AppStartOccurred::COLD,
+                      data.bucket_info(0).atom(1).app_start_occurred().type());
+            EXPECT_EQ("activity_name3",
+                      data.bucket_info(0).atom(1).app_start_occurred().activity_name());
+            EXPECT_EQ(103L,
+                      data.bucket_info(0).atom(1).app_start_occurred().activity_start_millis());
+
+            EXPECT_EQ(1, data.bucket_info(1).atom_size());
+            EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size());
+            EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
+                      data.bucket_info(1).start_bucket_elapsed_nanos());
+            EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+                      data.bucket_info(1).end_bucket_elapsed_nanos());
+            EXPECT_EQ(AppStartOccurred::WARM,
+                      data.bucket_info(1).atom(0).app_start_occurred().type());
+            EXPECT_EQ("activity_name4",
+                      data.bucket_info(1).atom(0).app_start_occurred().activity_name());
+            EXPECT_EQ(104L,
+                      data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis());
+
+            EXPECT_EQ(2, data.bucket_info(2).atom_size());
+            EXPECT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size());
+            EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+                      data.bucket_info(2).start_bucket_elapsed_nanos());
+            EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
+                      data.bucket_info(2).end_bucket_elapsed_nanos());
+            EXPECT_EQ(AppStartOccurred::COLD,
+                      data.bucket_info(2).atom(0).app_start_occurred().type());
+            EXPECT_EQ("activity_name5",
+                      data.bucket_info(2).atom(0).app_start_occurred().activity_name());
+            EXPECT_EQ(105L,
+                      data.bucket_info(2).atom(0).app_start_occurred().activity_start_millis());
+            EXPECT_EQ(AppStartOccurred::HOT,
+                      data.bucket_info(2).atom(1).app_start_occurred().type());
+            EXPECT_EQ("activity_name6",
+                      data.bucket_info(2).atom(1).app_start_occurred().activity_name());
+            EXPECT_EQ(106L,
+                      data.bucket_info(2).atom(1).app_start_occurred().activity_start_millis());
+        } else {
+            EXPECT_EQ(1, data.bucket_info(0).atom_size());
+            EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
+            EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+            EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
+                      data.bucket_info(0).end_bucket_elapsed_nanos());
+            EXPECT_EQ(AppStartOccurred::HOT,
+                      data.bucket_info(0).atom(0).app_start_occurred().type());
+            EXPECT_EQ("activity_name2",
+                      data.bucket_info(0).atom(0).app_start_occurred().activity_name());
+            EXPECT_EQ(102L,
+                      data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis());
+
+            EXPECT_EQ(1, data.bucket_info(1).atom_size());
+            EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size());
+            EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
+                      data.bucket_info(1).start_bucket_elapsed_nanos());
+            EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+                      data.bucket_info(1).end_bucket_elapsed_nanos());
+            EXPECT_EQ(AppStartOccurred::WARM,
+                      data.bucket_info(1).atom(0).app_start_occurred().type());
+            EXPECT_EQ("activity_name4",
+                      data.bucket_info(1).atom(0).app_start_occurred().activity_name());
+            EXPECT_EQ(104L,
+                      data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis());
+
+            EXPECT_EQ(1, data.bucket_info(2).atom_size());
+            EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
+            EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+                      data.bucket_info(2).start_bucket_elapsed_nanos());
+            EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
+                      data.bucket_info(2).end_bucket_elapsed_nanos());
+            EXPECT_EQ(AppStartOccurred::COLD,
+                      data.bucket_info(2).atom(0).app_start_occurred().type());
+            EXPECT_EQ("activity_name5",
+                      data.bucket_info(2).atom(0).app_start_occurred().activity_name());
+            EXPECT_EQ(105L,
+                      data.bucket_info(2).atom(0).app_start_occurred().activity_start_millis());
+        }
+
+        data = gaugeMetrics.data(1);
+
+        EXPECT_EQ(data.dimensions_in_what().field(), android::util::APP_START_OCCURRED);
+        EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+        EXPECT_EQ(1 /* uid field */,
+                  data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+        EXPECT_EQ(appUid2, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+        EXPECT_EQ(1, data.bucket_info_size());
+        EXPECT_EQ(1, data.bucket_info(0).atom_size());
+        EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
+        EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+                  data.bucket_info(0).start_bucket_elapsed_nanos());
+        EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
+                  data.bucket_info(0).end_bucket_elapsed_nanos());
+        EXPECT_EQ(AppStartOccurred::COLD, data.bucket_info(0).atom(0).app_start_occurred().type());
+        EXPECT_EQ("activity_name7",
+                  data.bucket_info(0).atom(0).app_start_occurred().activity_name());
+        EXPECT_EQ(201L, data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis());
+    }
+}
 
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
diff --git a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
index f3f7df77..1dd90e2 100644
--- a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
@@ -233,1609 +233,1602 @@
 
 }  // namespace
 
-// TODO(b/149590301): Update these tests to use new socket schema.
-//TEST(MetricActivationE2eTest, TestCountMetric) {
-//    auto config = CreateStatsdConfig();
-//
-//    int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs
-//    int64_t bucketSizeNs =
-//            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
-//
-//    int uid = 12345;
-//    int64_t cfgId = 98765;
-//    ConfigKey cfgKey(uid, cfgId);
-//
-//    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,
-//            bucketStartTimeNs, [](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(bucketStartTimeNs, cfgKey, config);
-//
-//    EXPECT_EQ(processor.mMetricsManagers.size(), 1u);
-//    sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
-//    EXPECT_TRUE(metricsManager->isConfigValid());
-//    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
-//    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
-//    auto& eventActivationMap = metricProducer->mEventActivationMap;
-//
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    // Two activations: one is triggered by battery saver mode (tracker index 0), the other is
-//    // triggered by screen on event (tracker index 2).
-//    EXPECT_EQ(eventActivationMap.size(), 2u);
-//    EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
-//    EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, 0);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//
-//    std::unique_ptr<LogEvent> event;
-//
-//    event = CreateAppCrashEvent(111, bucketStartTimeNs + 5);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + 5);
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    EXPECT_EQ(broadcastCount, 0);
-//
-//    // Activated by battery save mode.
-//    event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + 10);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(broadcastCount, 1);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
-//    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//
-//    // First processed event.
-//    event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
-//
-//    // Activated by screen on event.
-//    event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-//                                          bucketStartTimeNs + 20);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + 20);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//
-//    // 2nd processed event.
-//    // The activation by screen_on event expires, but the one by battery save mode is still active.
-//    event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    // No new broadcast since the config should still be active.
-//    EXPECT_EQ(broadcastCount, 1);
-//
-//    // 3rd processed event.
-//    event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
-//
-//    // All activations expired.
-//    event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8);
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    // New broadcast since the config is no longer active.
-//    EXPECT_EQ(broadcastCount, 2);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//
-//    // Re-activate metric via screen on.
-//    event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-//                                          bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(broadcastCount, 3);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
-//    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//
-//    // 4th processed event.
-//    event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
-//
-//    ConfigMetricsReportList reports;
-//    vector<uint8_t> buffer;
-//    processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
-//                            ADB_DUMP, FAST, &buffer);
-//    EXPECT_TRUE(buffer.size() > 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStartEndTimestamp(&reports);
-//    EXPECT_EQ(1, reports.reports_size());
-//    EXPECT_EQ(1, reports.reports(0).metrics_size());
-//    EXPECT_EQ(4, reports.reports(0).metrics(0).count_metrics().data_size());
-//
-//    StatsLogReport::CountMetricDataWrapper countMetrics;
-//    sortMetricDataByDimensionsValue(
-//            reports.reports(0).metrics(0).count_metrics(), &countMetrics);
-//    EXPECT_EQ(4, countMetrics.data_size());
-//
-//    auto data = countMetrics.data(0);
-//    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-//
-//    data = countMetrics.data(1);
-//    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-//
-//    data = countMetrics.data(2);
-//    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    // Partial bucket as metric is deactivated.
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8,
-//              data.bucket_info(0).end_bucket_elapsed_nanos());
-//
-//    data = countMetrics.data(3);
-//    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
-//              data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
-//              data.bucket_info(0).end_bucket_elapsed_nanos());
-//}
-//
-//TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) {
-//    auto config = CreateStatsdConfigWithOneDeactivation();
-//
-//    int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs
-//    int64_t bucketSizeNs =
-//            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
-//
-//    int uid = 12345;
-//    int64_t cfgId = 98765;
-//    ConfigKey cfgKey(uid, cfgId);
-//
-//    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,
-//            bucketStartTimeNs, [](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(bucketStartTimeNs, cfgKey, config);
-//
-//    EXPECT_EQ(processor.mMetricsManagers.size(), 1u);
-//    sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
-//    EXPECT_TRUE(metricsManager->isConfigValid());
-//    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
-//    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
-//    auto& eventActivationMap = metricProducer->mEventActivationMap;
-//    auto& eventDeactivationMap = metricProducer->mEventDeactivationMap;
-//
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    // Two activations: one is triggered by battery saver mode (tracker index 0), the other is
-//    // triggered by screen on event (tracker index 2).
-//    EXPECT_EQ(eventActivationMap.size(), 2u);
-//    EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
-//    EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, 0);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap.size(), 1u);
-//    EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end());
-//    EXPECT_EQ(eventDeactivationMap[3].size(), 1u);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//
-//    std::unique_ptr<LogEvent> event;
-//
-//    event = CreateAppCrashEvent(111, bucketStartTimeNs + 5);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + 5);
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    EXPECT_EQ(broadcastCount, 0);
-//
-//    // Activated by battery save mode.
-//    event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + 10);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(broadcastCount, 1);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
-//    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//
-//    // First processed event.
-//    event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
-//
-//    // Activated by screen on event.
-//    event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-//                                          bucketStartTimeNs + 20);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + 20);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//
-//    // 2nd processed event.
-//    // The activation by screen_on event expires, but the one by battery save mode is still active.
-//    event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
-//    processor.OnLogEvent(event.get(),bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//    // No new broadcast since the config should still be active.
-//    EXPECT_EQ(broadcastCount, 1);
-//
-//    // 3rd processed event.
-//    event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
-//
-//    // All activations expired.
-//    event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8);
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    // New broadcast since the config is no longer active.
-//    EXPECT_EQ(broadcastCount, 2);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//
-//    // Re-activate metric via screen on.
-//    event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-//                                          bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(broadcastCount, 3);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
-//    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//
-//    // 4th processed event.
-//    event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
-//
-//    // Re-enable battery saver mode activation.
-//    event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(broadcastCount, 3);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
-//    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//
-//    // 5th processed event.
-//    event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
-//
-//    // Cancel battery saver mode activation.
-//    event = CreateScreenBrightnessChangedEvent(64, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(broadcastCount, 3);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
-//    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//
-//    // Screen-on activation expired.
-//    event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13);
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    // New broadcast since the config is no longer active.
-//    EXPECT_EQ(broadcastCount, 4);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//
-//    event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
-//
-//    // Re-enable battery saver mode activation.
-//    event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(broadcastCount, 5);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
-//    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//
-//    // Cancel battery saver mode activation.
-//    event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16);
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    EXPECT_EQ(broadcastCount, 6);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//
-//    ConfigMetricsReportList reports;
-//    vector<uint8_t> buffer;
-//    processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
-//                            ADB_DUMP, FAST, &buffer);
-//    EXPECT_TRUE(buffer.size() > 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStartEndTimestamp(&reports);
-//    EXPECT_EQ(1, reports.reports_size());
-//    EXPECT_EQ(1, reports.reports(0).metrics_size());
-//    EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size());
-//
-//    StatsLogReport::CountMetricDataWrapper countMetrics;
-//    sortMetricDataByDimensionsValue(
-//            reports.reports(0).metrics(0).count_metrics(), &countMetrics);
-//    EXPECT_EQ(5, countMetrics.data_size());
-//
-//    auto data = countMetrics.data(0);
-//    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-//
-//    data = countMetrics.data(1);
-//    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-//
-//    data = countMetrics.data(2);
-//    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    // Partial bucket as metric is deactivated.
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8,
-//              data.bucket_info(0).end_bucket_elapsed_nanos());
-//
-//    data = countMetrics.data(3);
-//    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
-//              data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 13,
-//              data.bucket_info(0).end_bucket_elapsed_nanos());
-//
-//    data = countMetrics.data(4);
-//    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
-//              data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 13,
-//              data.bucket_info(0).end_bucket_elapsed_nanos());
-//}
-//
-//TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) {
-//    auto config = CreateStatsdConfigWithTwoDeactivations();
-//
-//    int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs
-//    int64_t bucketSizeNs =
-//            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
-//
-//    int uid = 12345;
-//    int64_t cfgId = 98765;
-//    ConfigKey cfgKey(uid, cfgId);
-//
-//    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,
-//            bucketStartTimeNs, [](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(bucketStartTimeNs, cfgKey, config);
-//
-//    EXPECT_EQ(processor.mMetricsManagers.size(), 1u);
-//    sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
-//    EXPECT_TRUE(metricsManager->isConfigValid());
-//    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
-//    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
-//    auto& eventActivationMap = metricProducer->mEventActivationMap;
-//    auto& eventDeactivationMap = metricProducer->mEventDeactivationMap;
-//
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    // Two activations: one is triggered by battery saver mode (tracker index 0), the other is
-//    // triggered by screen on event (tracker index 2).
-//    EXPECT_EQ(eventActivationMap.size(), 2u);
-//    EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
-//    EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, 0);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap.size(), 2u);
-//    EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end());
-//    EXPECT_TRUE(eventDeactivationMap.find(4) != eventDeactivationMap.end());
-//    EXPECT_EQ(eventDeactivationMap[3].size(), 1u);
-//    EXPECT_EQ(eventDeactivationMap[4].size(), 1u);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-//
-//    std::unique_ptr<LogEvent> event;
-//
-//    event = CreateAppCrashEvent(111, bucketStartTimeNs + 5);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + 5);
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    EXPECT_EQ(broadcastCount, 0);
-//
-//    // Activated by battery save mode.
-//    event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + 10);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(broadcastCount, 1);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
-//    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-//
-//    // First processed event.
-//    event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
-//
-//    // Activated by screen on event.
-//    event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-//                                          bucketStartTimeNs + 20);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + 20);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-//
-//    // 2nd processed event.
-//    // The activation by screen_on event expires, but the one by battery save mode is still active.
-//    event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-//    // No new broadcast since the config should still be active.
-//    EXPECT_EQ(broadcastCount, 1);
-//
-//    // 3rd processed event.
-//    event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
-//
-//    // All activations expired.
-//    event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8);
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    // New broadcast since the config is no longer active.
-//    EXPECT_EQ(broadcastCount, 2);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-//
-//    // Re-activate metric via screen on.
-//    event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-//                                          bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(broadcastCount, 3);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
-//    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-//
-//    // 4th processed event.
-//    event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
-//
-//    // Re-enable battery saver mode activation.
-//    event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(broadcastCount, 3);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
-//    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-//
-//    // 5th processed event.
-//    event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
-//
-//    // Cancel battery saver mode and screen on activation.
-//    event = CreateScreenBrightnessChangedEvent(64, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    // New broadcast since the config is no longer active.
-//    EXPECT_EQ(broadcastCount, 4);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-//
-//    // Screen-on activation expired.
-//    event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13);
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    EXPECT_EQ(broadcastCount, 4);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-//
-//    event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
-//
-//    // Re-enable battery saver mode activation.
-//    event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(broadcastCount, 5);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
-//    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-//
-//    // Cancel battery saver mode and screen on activation.
-//    event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16);
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    EXPECT_EQ(broadcastCount, 6);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-//
-//    ConfigMetricsReportList reports;
-//    vector<uint8_t> buffer;
-//    processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
-//                            ADB_DUMP, FAST, &buffer);
-//    EXPECT_TRUE(buffer.size() > 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStartEndTimestamp(&reports);
-//    EXPECT_EQ(1, reports.reports_size());
-//    EXPECT_EQ(1, reports.reports(0).metrics_size());
-//    EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size());
-//
-//    StatsLogReport::CountMetricDataWrapper countMetrics;
-//    sortMetricDataByDimensionsValue(
-//            reports.reports(0).metrics(0).count_metrics(), &countMetrics);
-//    EXPECT_EQ(5, countMetrics.data_size());
-//
-//    auto data = countMetrics.data(0);
-//    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-//
-//    data = countMetrics.data(1);
-//    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-//
-//    data = countMetrics.data(2);
-//    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    // Partial bucket as metric is deactivated.
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8,
-//              data.bucket_info(0).end_bucket_elapsed_nanos());
-//
-//    data = countMetrics.data(3);
-//    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
-//              data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11,
-//              data.bucket_info(0).end_bucket_elapsed_nanos());
-//
-//    data = countMetrics.data(4);
-//    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
-//              data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11,
-//              data.bucket_info(0).end_bucket_elapsed_nanos());
-//}
-//
-//TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) {
-//    auto config = CreateStatsdConfigWithSameDeactivations();
-//
-//    int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs
-//    int64_t bucketSizeNs =
-//            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
-//
-//    int uid = 12345;
-//    int64_t cfgId = 98765;
-//    ConfigKey cfgKey(uid, cfgId);
-//
-//    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,
-//            bucketStartTimeNs, [](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(bucketStartTimeNs, cfgKey, config);
-//
-//    EXPECT_EQ(processor.mMetricsManagers.size(), 1u);
-//    sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
-//    EXPECT_TRUE(metricsManager->isConfigValid());
-//    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
-//    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
-//    auto& eventActivationMap = metricProducer->mEventActivationMap;
-//    auto& eventDeactivationMap = metricProducer->mEventDeactivationMap;
-//
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    // Two activations: one is triggered by battery saver mode (tracker index 0), the other is
-//    // triggered by screen on event (tracker index 2).
-//    EXPECT_EQ(eventActivationMap.size(), 2u);
-//    EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
-//    EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, 0);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap.size(), 1u);
-//    EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end());
-//    EXPECT_EQ(eventDeactivationMap[3].size(), 2u);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//    EXPECT_EQ(eventDeactivationMap[3][1], eventActivationMap[2]);
-//    EXPECT_EQ(broadcastCount, 0);
-//
-//    std::unique_ptr<LogEvent> event;
-//
-//    // Event that should be ignored.
-//    event = CreateAppCrashEvent(111, bucketStartTimeNs + 1);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + 1);
-//
-//    // Activate metric via screen on for 2 minutes.
-//    event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + 10);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(broadcastCount, 1);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
-//    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 10);
-//
-//    // 1st processed event.
-//    event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
-//
-//    // Enable battery saver mode activation for 5 minutes.
-//    event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 + 10);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 + 10);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(broadcastCount, 1);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 + 10);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 10);
-//
-//    // 2nd processed event.
-//    event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 + 40);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 + 40);
-//
-//    // Cancel battery saver mode and screen on activation.
-//    int64_t firstDeactivation = bucketStartTimeNs + NS_PER_SEC * 61;
-//    event = CreateScreenBrightnessChangedEvent(64, firstDeactivation);
-//    processor.OnLogEvent(event.get(), firstDeactivation);
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    // New broadcast since the config is no longer active.
-//    EXPECT_EQ(broadcastCount, 2);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//
-//    // Should be ignored
-//    event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 61 + 80);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 61 + 80);
-//
-//    // Re-enable battery saver mode activation.
-//    event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(broadcastCount, 3);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
-//    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//
-//    // 3rd processed event.
-//    event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 80);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 80);
-//
-//    // Cancel battery saver mode activation.
-//    int64_t secondDeactivation = bucketStartTimeNs + NS_PER_SEC * 60 * 13;
-//    event = CreateScreenBrightnessChangedEvent(140, secondDeactivation);
-//    processor.OnLogEvent(event.get(), secondDeactivation);
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    EXPECT_EQ(broadcastCount, 4);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//
-//    // Should be ignored.
-//    event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80);
-//
-//    ConfigMetricsReportList reports;
-//    vector<uint8_t> buffer;
-//    processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
-//                            ADB_DUMP, FAST, &buffer);
-//    EXPECT_TRUE(buffer.size() > 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStartEndTimestamp(&reports);
-//    EXPECT_EQ(1, reports.reports_size());
-//    EXPECT_EQ(1, reports.reports(0).metrics_size());
-//    EXPECT_EQ(3, reports.reports(0).metrics(0).count_metrics().data_size());
-//
-//    StatsLogReport::CountMetricDataWrapper countMetrics;
-//    sortMetricDataByDimensionsValue(
-//            reports.reports(0).metrics(0).count_metrics(), &countMetrics);
-//    EXPECT_EQ(3, countMetrics.data_size());
-//
-//    auto data = countMetrics.data(0);
-//    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(firstDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos());
-//
-//    data = countMetrics.data(1);
-//    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(firstDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos());
-//
-//    data = countMetrics.data(2);
-//    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(555, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    // Partial bucket as metric is deactivated.
-//    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(secondDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos());
-//}
-//
-//TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
-//    auto config = CreateStatsdConfigWithTwoMetricsTwoDeactivations();
-//
-//    int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs
-//    int64_t bucketSizeNs =
-//            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
-//
-//    int uid = 12345;
-//    int64_t cfgId = 98765;
-//    ConfigKey cfgKey(uid, cfgId);
-//
-//    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,
-//            bucketStartTimeNs, [](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(bucketStartTimeNs, cfgKey, config);
-//
-//    EXPECT_EQ(processor.mMetricsManagers.size(), 1u);
-//    sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
-//    EXPECT_TRUE(metricsManager->isConfigValid());
-//    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 2);
-//    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
-//    auto& eventActivationMap = metricProducer->mEventActivationMap;
-//    auto& eventDeactivationMap = metricProducer->mEventDeactivationMap;
-//    sp<MetricProducer> metricProducer2 = metricsManager->mAllMetricProducers[1];
-//    auto& eventActivationMap2 = metricProducer2->mEventActivationMap;
-//    auto& eventDeactivationMap2 = metricProducer2->mEventDeactivationMap;
-//
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    EXPECT_FALSE(metricProducer2->mIsActive);
-//    // Two activations: one is triggered by battery saver mode (tracker index 0), the other is
-//    // triggered by screen on event (tracker index 2).
-//    EXPECT_EQ(eventActivationMap.size(), 2u);
-//    EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
-//    EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, 0);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap.size(), 2u);
-//    EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end());
-//    EXPECT_TRUE(eventDeactivationMap.find(4) != eventDeactivationMap.end());
-//    EXPECT_EQ(eventDeactivationMap[3].size(), 1u);
-//    EXPECT_EQ(eventDeactivationMap[4].size(), 1u);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-//
-//    EXPECT_EQ(eventActivationMap2.size(), 2u);
-//    EXPECT_TRUE(eventActivationMap2.find(0) != eventActivationMap2.end());
-//    EXPECT_TRUE(eventActivationMap2.find(2) != eventActivationMap2.end());
-//    EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap2[0]->start_ns, 0);
-//    EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap2[2]->start_ns, 0);
-//    EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap2.size(), 2u);
-//    EXPECT_TRUE(eventDeactivationMap2.find(3) != eventDeactivationMap2.end());
-//    EXPECT_TRUE(eventDeactivationMap2.find(4) != eventDeactivationMap2.end());
-//    EXPECT_EQ(eventDeactivationMap[3].size(), 1u);
-//    EXPECT_EQ(eventDeactivationMap[4].size(), 1u);
-//    EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
-//    EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
-//
-//    std::unique_ptr<LogEvent> event;
-//
-//    event = CreateAppCrashEvent(111, bucketStartTimeNs + 5);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + 5);
-//    event = CreateMoveToForegroundEvent(1111, bucketStartTimeNs + 5);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + 5);
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    EXPECT_FALSE(metricProducer2->mIsActive);
-//    EXPECT_EQ(broadcastCount, 0);
-//
-//    // Activated by battery save mode.
-//    event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + 10);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_EQ(broadcastCount, 1);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
-//    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-//    EXPECT_TRUE(metricProducer2->mIsActive);
-//    EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10);
-//    EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap2[2]->start_ns, 0);
-//    EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
-//    EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
-//
-//    // First processed event.
-//    event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
-//    event = CreateMoveToForegroundEvent(2222, bucketStartTimeNs + 15);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
-//
-//    // Activated by screen on event.
-//    event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-//                                          bucketStartTimeNs + 20);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + 20);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-//    EXPECT_TRUE(metricProducer2->mIsActive);
-//    EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10);
-//    EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20);
-//    EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
-//    EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
-//
-//    // 2nd processed event.
-//    // The activation by screen_on event expires, but the one by battery save mode is still active.
-//    event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
-//    event = CreateMoveToForegroundEvent(3333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-//    EXPECT_TRUE(metricProducer2->mIsActive);
-//    EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10);
-//    EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20);
-//    EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
-//    EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
-//    // No new broadcast since the config should still be active.
-//    EXPECT_EQ(broadcastCount, 1);
-//
-//    // 3rd processed event.
-//    event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
-//    event = CreateMoveToForegroundEvent(4444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
-//
-//    // All activations expired.
-//    event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8);
-//    event = CreateMoveToForegroundEvent(5555, bucketStartTimeNs + NS_PER_SEC * 60 * 8);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8);
-//    EXPECT_FALSE(metricsManager->isActive());
-//    // New broadcast since the config is no longer active.
-//    EXPECT_EQ(broadcastCount, 2);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-//    EXPECT_FALSE(metricProducer2->mIsActive);
-//    EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10);
-//    EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20);
-//    EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
-//    EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
-//
-//    // Re-activate metric via screen on.
-//    event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-//                                          bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_EQ(broadcastCount, 3);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
-//    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-//    EXPECT_TRUE(metricProducer2->mIsActive);
-//    EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10);
-//    EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
-//    EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
-//
-//    // 4th processed event.
-//    event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
-//    event = CreateMoveToForegroundEvent(6666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
-//
-//    // Re-enable battery saver mode activation.
-//    event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_EQ(broadcastCount, 3);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
-//    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-//    EXPECT_TRUE(metricProducer2->mIsActive);
-//    EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
-//    EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
-//    EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
-//
-//    // 5th processed event.
-//    event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
-//    event = CreateMoveToForegroundEvent(7777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
-//
-//    // Cancel battery saver mode and screen on activation.
-//    event = CreateScreenBrightnessChangedEvent(64, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
-//    processor.OnLogEvent(event.get(),bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
-//    EXPECT_FALSE(metricsManager->isActive());
-//    // New broadcast since the config is no longer active.
-//    EXPECT_EQ(broadcastCount, 4);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-//    EXPECT_FALSE(metricProducer2->mIsActive);
-//    EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
-//    EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
-//    EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
-//
-//    // Screen-on activation expired.
-//    event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13);
-//    event = CreateMoveToForegroundEvent(8888, bucketStartTimeNs + NS_PER_SEC * 60 * 13);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13);
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_EQ(broadcastCount, 4);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-//    EXPECT_FALSE(metricProducer2->mIsActive);
-//    EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
-//    EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
-//    EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
-//
-//    event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
-//    event = CreateMoveToForegroundEvent(9999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
-//
-//    // Re-enable battery saver mode activation.
-//    event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
-//    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
-//    EXPECT_TRUE(metricsManager->isActive());
-//    EXPECT_EQ(broadcastCount, 5);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
-//    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
-//    EXPECT_TRUE(metricProducer->mIsActive);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-//    EXPECT_TRUE(metricProducer2->mIsActive);
-//    EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
-//    EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
-//    EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
-//    EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
-//
-//    // Cancel battery saver mode and screen on activation.
-//    event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16);
-//    processor.OnLogEvent(event.get(),bucketStartTimeNs + NS_PER_SEC * 60 * 16);
-//    EXPECT_FALSE(metricsManager->isActive());
-//    EXPECT_EQ(broadcastCount, 6);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
-//    EXPECT_FALSE(metricProducer->mIsActive);
-//    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
-//    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-//    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-//    EXPECT_FALSE(metricProducer2->mIsActive);
-//    EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
-//    EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
-//    EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
-//    EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-//    EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-//    EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
-//    EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
-//
-//    ConfigMetricsReportList reports;
-//    vector<uint8_t> buffer;
-//    processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
-//                            ADB_DUMP, FAST, &buffer);
-//    EXPECT_TRUE(buffer.size() > 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStartEndTimestamp(&reports);
-//    EXPECT_EQ(1, reports.reports_size());
-//    EXPECT_EQ(2, reports.reports(0).metrics_size());
-//    EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size());
-//    EXPECT_EQ(5, reports.reports(0).metrics(1).count_metrics().data_size());
-//
-//    StatsLogReport::CountMetricDataWrapper countMetrics;
-//
-//    sortMetricDataByDimensionsValue(
-//            reports.reports(0).metrics(0).count_metrics(), &countMetrics);
-//    EXPECT_EQ(5, countMetrics.data_size());
-//
-//    auto data = countMetrics.data(0);
-//    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-//
-//    data = countMetrics.data(1);
-//    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-//
-//    data = countMetrics.data(2);
-//    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    // Partial bucket as metric is deactivated.
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8,
-//              data.bucket_info(0).end_bucket_elapsed_nanos());
-//
-//    data = countMetrics.data(3);
-//    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
-//              data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11,
-//              data.bucket_info(0).end_bucket_elapsed_nanos());
-//
-//    data = countMetrics.data(4);
-//    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
-//              data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11,
-//              data.bucket_info(0).end_bucket_elapsed_nanos());
-//
-//
-//   countMetrics.clear_data();
-//    sortMetricDataByDimensionsValue(
-//            reports.reports(0).metrics(1).count_metrics(), &countMetrics);
-//    EXPECT_EQ(5, countMetrics.data_size());
-//
-//    data = countMetrics.data(0);
-//    EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(2222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-//
-//    data = countMetrics.data(1);
-//    EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(3333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-//
-//    data = countMetrics.data(2);
-//    EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(4444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    // Partial bucket as metric is deactivated.
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8,
-//              data.bucket_info(0).end_bucket_elapsed_nanos());
-//
-//    data = countMetrics.data(3);
-//    EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(6666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
-//              data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11,
-//              data.bucket_info(0).end_bucket_elapsed_nanos());
-//
-//    data = countMetrics.data(4);
-//    EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* uid field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_EQ(7777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
-//    EXPECT_EQ(1, data.bucket_info_size());
-//    EXPECT_EQ(1, data.bucket_info(0).count());
-//    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
-//              data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11,
-//              data.bucket_info(0).end_bucket_elapsed_nanos());
-//}
+TEST(MetricActivationE2eTest, TestCountMetric) {
+    auto config = CreateStatsdConfig();
+
+    int64_t bucketStartTimeNs = NS_PER_SEC * 10;  // 10 secs
+    int64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
+
+    int uid = 12345;
+    int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+
+    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, bucketStartTimeNs,
+            [](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(bucketStartTimeNs, cfgKey, config);
+
+    EXPECT_EQ(processor.mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+    auto& eventActivationMap = metricProducer->mEventActivationMap;
+
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    // Two activations: one is triggered by battery saver mode (tracker index 0), the other is
+    // triggered by screen on event (tracker index 2).
+    EXPECT_EQ(eventActivationMap.size(), 2u);
+    EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
+    EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, 0);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+
+    std::unique_ptr<LogEvent> event;
+
+    event = CreateAppCrashEvent(bucketStartTimeNs + 5, 111);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + 5);
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 0);
+
+    // Activated by battery save mode.
+    event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + 10);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 1);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+
+    // First processed event.
+    event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
+
+    // Activated by screen on event.
+    event = CreateScreenStateChangedEvent(bucketStartTimeNs + 20, android::view::DISPLAY_STATE_ON);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + 20);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+
+    // 2nd processed event.
+    // The activation by screen_on event expires, but the one by battery save mode is still active.
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 333);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    // No new broadcast since the config should still be active.
+    EXPECT_EQ(broadcastCount, 1);
+
+    // 3rd processed event.
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 444);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
+
+    // All activations expired.
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 555);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8);
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    // New broadcast since the config is no longer active.
+    EXPECT_EQ(broadcastCount, 2);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+
+    // Re-activate metric via screen on.
+    event = CreateScreenStateChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10,
+                                          android::view::DISPLAY_STATE_ON);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 3);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+
+    // 4th processed event.
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 666);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
+                           ADB_DUMP, FAST, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStartEndTimestamp(&reports);
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    EXPECT_EQ(4, reports.reports(0).metrics(0).count_metrics().data_size());
+
+    StatsLogReport::CountMetricDataWrapper countMetrics;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+    EXPECT_EQ(4, countMetrics.data_size());
+
+    auto data = countMetrics.data(0);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(1);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(2);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    // Partial bucket as metric is deactivated.
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8,
+              data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(3);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+              data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+}
+
+TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) {
+    auto config = CreateStatsdConfigWithOneDeactivation();
+
+    int64_t bucketStartTimeNs = NS_PER_SEC * 10;  // 10 secs
+    int64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
+
+    int uid = 12345;
+    int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+
+    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, bucketStartTimeNs,
+            [](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(bucketStartTimeNs, cfgKey, config);
+
+    EXPECT_EQ(processor.mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+    auto& eventActivationMap = metricProducer->mEventActivationMap;
+    auto& eventDeactivationMap = metricProducer->mEventDeactivationMap;
+
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    // Two activations: one is triggered by battery saver mode (tracker index 0), the other is
+    // triggered by screen on event (tracker index 2).
+    EXPECT_EQ(eventActivationMap.size(), 2u);
+    EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
+    EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, 0);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap.size(), 1u);
+    EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end());
+    EXPECT_EQ(eventDeactivationMap[3].size(), 1u);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+
+    std::unique_ptr<LogEvent> event;
+
+    event = CreateAppCrashEvent(bucketStartTimeNs + 5, 111);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + 5);
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 0);
+
+    // Activated by battery save mode.
+    event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + 10);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 1);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+
+    // First processed event.
+    event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
+
+    // Activated by screen on event.
+    event = CreateScreenStateChangedEvent(bucketStartTimeNs + 20, android::view::DISPLAY_STATE_ON);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + 20);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+
+    // 2nd processed event.
+    // The activation by screen_on event expires, but the one by battery save mode is still active.
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 333);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+    // No new broadcast since the config should still be active.
+    EXPECT_EQ(broadcastCount, 1);
+
+    // 3rd processed event.
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 444);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
+
+    // All activations expired.
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 555);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8);
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    // New broadcast since the config is no longer active.
+    EXPECT_EQ(broadcastCount, 2);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+
+    // Re-activate metric via screen on.
+    event = CreateScreenStateChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10,
+                                          android::view::DISPLAY_STATE_ON);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 3);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+
+    // 4th processed event.
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 666);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
+
+    // Re-enable battery saver mode activation.
+    event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 3);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+
+    // 5th processed event.
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40, 777);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
+
+    // Cancel battery saver mode activation.
+    event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60, 64);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 3);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+
+    // Screen-on activation expired.
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13, 888);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13);
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    // New broadcast since the config is no longer active.
+    EXPECT_EQ(broadcastCount, 4);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1, 999);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
+
+    // Re-enable battery saver mode activation.
+    event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 5);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+
+    // Cancel battery saver mode activation.
+    event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 16, 140);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16);
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 6);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
+                           ADB_DUMP, FAST, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStartEndTimestamp(&reports);
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size());
+
+    StatsLogReport::CountMetricDataWrapper countMetrics;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+    EXPECT_EQ(5, countMetrics.data_size());
+
+    auto data = countMetrics.data(0);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(1);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(2);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    // Partial bucket as metric is deactivated.
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8,
+              data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(3);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+              data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 13,
+              data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(4);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+              data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 13,
+              data.bucket_info(0).end_bucket_elapsed_nanos());
+}
+
+TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) {
+    auto config = CreateStatsdConfigWithTwoDeactivations();
+
+    int64_t bucketStartTimeNs = NS_PER_SEC * 10;  // 10 secs
+    int64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
+
+    int uid = 12345;
+    int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+
+    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, bucketStartTimeNs,
+            [](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(bucketStartTimeNs, cfgKey, config);
+
+    EXPECT_EQ(processor.mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+    auto& eventActivationMap = metricProducer->mEventActivationMap;
+    auto& eventDeactivationMap = metricProducer->mEventDeactivationMap;
+
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    // Two activations: one is triggered by battery saver mode (tracker index 0), the other is
+    // triggered by screen on event (tracker index 2).
+    EXPECT_EQ(eventActivationMap.size(), 2u);
+    EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
+    EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, 0);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap.size(), 2u);
+    EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end());
+    EXPECT_TRUE(eventDeactivationMap.find(4) != eventDeactivationMap.end());
+    EXPECT_EQ(eventDeactivationMap[3].size(), 1u);
+    EXPECT_EQ(eventDeactivationMap[4].size(), 1u);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
+
+    std::unique_ptr<LogEvent> event;
+
+    event = CreateAppCrashEvent(bucketStartTimeNs + 5, 111);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + 5);
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 0);
+
+    // Activated by battery save mode.
+    event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + 10);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 1);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
+
+    // First processed event.
+    event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
+
+    // Activated by screen on event.
+    event = CreateScreenStateChangedEvent(bucketStartTimeNs + 20, android::view::DISPLAY_STATE_ON);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + 20);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
+
+    // 2nd processed event.
+    // The activation by screen_on event expires, but the one by battery save mode is still active.
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 333);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
+    // No new broadcast since the config should still be active.
+    EXPECT_EQ(broadcastCount, 1);
+
+    // 3rd processed event.
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 444);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
+
+    // All activations expired.
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 555);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8);
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    // New broadcast since the config is no longer active.
+    EXPECT_EQ(broadcastCount, 2);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
+
+    // Re-activate metric via screen on.
+    event = CreateScreenStateChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10,
+                                          android::view::DISPLAY_STATE_ON);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 3);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
+
+    // 4th processed event.
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 666);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
+
+    // Re-enable battery saver mode activation.
+    event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 3);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
+
+    // 5th processed event.
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40, 777);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
+
+    // Cancel battery saver mode and screen on activation.
+    event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60, 64);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    // New broadcast since the config is no longer active.
+    EXPECT_EQ(broadcastCount, 4);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
+
+    // Screen-on activation expired.
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13, 888);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13);
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 4);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
+
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1, 999);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
+
+    // Re-enable battery saver mode activation.
+    event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 5);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
+
+    // Cancel battery saver mode and screen on activation.
+    event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 16, 140);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16);
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 6);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
+                           ADB_DUMP, FAST, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStartEndTimestamp(&reports);
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size());
+
+    StatsLogReport::CountMetricDataWrapper countMetrics;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+    EXPECT_EQ(5, countMetrics.data_size());
+
+    auto data = countMetrics.data(0);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(1);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(2);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    // Partial bucket as metric is deactivated.
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8,
+              data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(3);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+              data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11,
+              data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(4);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+              data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11,
+              data.bucket_info(0).end_bucket_elapsed_nanos());
+}
+
+TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) {
+    auto config = CreateStatsdConfigWithSameDeactivations();
+
+    int64_t bucketStartTimeNs = NS_PER_SEC * 10;  // 10 secs
+    int64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
+
+    int uid = 12345;
+    int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+
+    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, bucketStartTimeNs,
+            [](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(bucketStartTimeNs, cfgKey, config);
+
+    EXPECT_EQ(processor.mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+    auto& eventActivationMap = metricProducer->mEventActivationMap;
+    auto& eventDeactivationMap = metricProducer->mEventDeactivationMap;
+
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    // Two activations: one is triggered by battery saver mode (tracker index 0), the other is
+    // triggered by screen on event (tracker index 2).
+    EXPECT_EQ(eventActivationMap.size(), 2u);
+    EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
+    EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, 0);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap.size(), 1u);
+    EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end());
+    EXPECT_EQ(eventDeactivationMap[3].size(), 2u);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+    EXPECT_EQ(eventDeactivationMap[3][1], eventActivationMap[2]);
+    EXPECT_EQ(broadcastCount, 0);
+
+    std::unique_ptr<LogEvent> event;
+
+    // Event that should be ignored.
+    event = CreateAppCrashEvent(bucketStartTimeNs + 1, 111);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + 1);
+
+    // Activate metric via screen on for 2 minutes.
+    event = CreateScreenStateChangedEvent(bucketStartTimeNs + 10, android::view::DISPLAY_STATE_ON);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + 10);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 1);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 10);
+
+    // 1st processed event.
+    event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
+
+    // Enable battery saver mode activation for 5 minutes.
+    event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 + 10);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 + 10);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 1);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 + 10);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 10);
+
+    // 2nd processed event.
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 + 40, 333);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 + 40);
+
+    // Cancel battery saver mode and screen on activation.
+    int64_t firstDeactivation = bucketStartTimeNs + NS_PER_SEC * 61;
+    event = CreateScreenBrightnessChangedEvent(firstDeactivation, 64);
+    processor.OnLogEvent(event.get(), firstDeactivation);
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    // New broadcast since the config is no longer active.
+    EXPECT_EQ(broadcastCount, 2);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+
+    // Should be ignored
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 61 + 80, 444);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 61 + 80);
+
+    // Re-enable battery saver mode activation.
+    event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 3);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+
+    // 3rd processed event.
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 80, 555);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 80);
+
+    // Cancel battery saver mode activation.
+    int64_t secondDeactivation = bucketStartTimeNs + NS_PER_SEC * 60 * 13;
+    event = CreateScreenBrightnessChangedEvent(secondDeactivation, 140);
+    processor.OnLogEvent(event.get(), secondDeactivation);
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 4);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+
+    // Should be ignored.
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80, 666);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80);
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
+                           ADB_DUMP, FAST, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStartEndTimestamp(&reports);
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    EXPECT_EQ(3, reports.reports(0).metrics(0).count_metrics().data_size());
+
+    StatsLogReport::CountMetricDataWrapper countMetrics;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+    EXPECT_EQ(3, countMetrics.data_size());
+
+    auto data = countMetrics.data(0);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(firstDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(1);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(firstDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(2);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(555, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    // Partial bucket as metric is deactivated.
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+              data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(secondDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos());
+}
+
+TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
+    auto config = CreateStatsdConfigWithTwoMetricsTwoDeactivations();
+
+    int64_t bucketStartTimeNs = NS_PER_SEC * 10;  // 10 secs
+    int64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
+
+    int uid = 12345;
+    int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+
+    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, bucketStartTimeNs,
+            [](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(bucketStartTimeNs, cfgKey, config);
+
+    EXPECT_EQ(processor.mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 2);
+    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+    auto& eventActivationMap = metricProducer->mEventActivationMap;
+    auto& eventDeactivationMap = metricProducer->mEventDeactivationMap;
+    sp<MetricProducer> metricProducer2 = metricsManager->mAllMetricProducers[1];
+    auto& eventActivationMap2 = metricProducer2->mEventActivationMap;
+    auto& eventDeactivationMap2 = metricProducer2->mEventDeactivationMap;
+
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    EXPECT_FALSE(metricProducer2->mIsActive);
+    // Two activations: one is triggered by battery saver mode (tracker index 0), the other is
+    // triggered by screen on event (tracker index 2).
+    EXPECT_EQ(eventActivationMap.size(), 2u);
+    EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
+    EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, 0);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap.size(), 2u);
+    EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end());
+    EXPECT_TRUE(eventDeactivationMap.find(4) != eventDeactivationMap.end());
+    EXPECT_EQ(eventDeactivationMap[3].size(), 1u);
+    EXPECT_EQ(eventDeactivationMap[4].size(), 1u);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
+
+    EXPECT_EQ(eventActivationMap2.size(), 2u);
+    EXPECT_TRUE(eventActivationMap2.find(0) != eventActivationMap2.end());
+    EXPECT_TRUE(eventActivationMap2.find(2) != eventActivationMap2.end());
+    EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap2[0]->start_ns, 0);
+    EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap2[2]->start_ns, 0);
+    EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap2.size(), 2u);
+    EXPECT_TRUE(eventDeactivationMap2.find(3) != eventDeactivationMap2.end());
+    EXPECT_TRUE(eventDeactivationMap2.find(4) != eventDeactivationMap2.end());
+    EXPECT_EQ(eventDeactivationMap[3].size(), 1u);
+    EXPECT_EQ(eventDeactivationMap[4].size(), 1u);
+    EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
+    EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
+
+    std::unique_ptr<LogEvent> event;
+
+    event = CreateAppCrashEvent(bucketStartTimeNs + 5, 111);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + 5);
+    event = CreateMoveToForegroundEvent(bucketStartTimeNs + 5, 1111);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + 5);
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    EXPECT_FALSE(metricProducer2->mIsActive);
+    EXPECT_EQ(broadcastCount, 0);
+
+    // Activated by battery save mode.
+    event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + 10);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_EQ(broadcastCount, 1);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
+    EXPECT_TRUE(metricProducer2->mIsActive);
+    EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap2[2]->start_ns, 0);
+    EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
+    EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
+
+    // First processed event.
+    event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
+    event = CreateMoveToForegroundEvent(bucketStartTimeNs + 15, 2222);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
+
+    // Activated by screen on event.
+    event = CreateScreenStateChangedEvent(bucketStartTimeNs + 20, android::view::DISPLAY_STATE_ON);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + 20);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
+    EXPECT_TRUE(metricProducer2->mIsActive);
+    EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20);
+    EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
+    EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
+
+    // 2nd processed event.
+    // The activation by screen_on event expires, but the one by battery save mode is still active.
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 333);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
+    event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 3333);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
+    EXPECT_TRUE(metricProducer2->mIsActive);
+    EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20);
+    EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
+    EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
+    // No new broadcast since the config should still be active.
+    EXPECT_EQ(broadcastCount, 1);
+
+    // 3rd processed event.
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 444);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
+    event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 4444);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
+
+    // All activations expired.
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 555);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8);
+    event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 5555);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8);
+    EXPECT_FALSE(metricsManager->isActive());
+    // New broadcast since the config is no longer active.
+    EXPECT_EQ(broadcastCount, 2);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+    EXPECT_FALSE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
+    EXPECT_FALSE(metricProducer2->mIsActive);
+    EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20);
+    EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
+    EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
+
+    // Re-activate metric via screen on.
+    event = CreateScreenStateChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10,
+                                          android::view::DISPLAY_STATE_ON);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_EQ(broadcastCount, 3);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
+    EXPECT_TRUE(metricProducer2->mIsActive);
+    EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
+    EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
+
+    // 4th processed event.
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 666);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
+    event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 6666);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
+
+    // Re-enable battery saver mode activation.
+    event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_EQ(broadcastCount, 3);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
+    EXPECT_TRUE(metricProducer2->mIsActive);
+    EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+    EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
+    EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
+
+    // 5th processed event.
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40, 777);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
+    event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40, 7777);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
+
+    // Cancel battery saver mode and screen on activation.
+    event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60, 64);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
+    EXPECT_FALSE(metricsManager->isActive());
+    // New broadcast since the config is no longer active.
+    EXPECT_EQ(broadcastCount, 4);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+    EXPECT_FALSE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
+    EXPECT_FALSE(metricProducer2->mIsActive);
+    EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+    EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
+    EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
+
+    // Screen-on activation expired.
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13, 888);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13);
+    event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13, 8888);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13);
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_EQ(broadcastCount, 4);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+    EXPECT_FALSE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
+    EXPECT_FALSE(metricProducer2->mIsActive);
+    EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+    EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
+    EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
+
+    event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1, 999);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
+    event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1, 9999);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
+
+    // Re-enable battery saver mode activation.
+    event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+    EXPECT_TRUE(metricsManager->isActive());
+    EXPECT_EQ(broadcastCount, 5);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
+    EXPECT_TRUE(metricProducer2->mIsActive);
+    EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+    EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
+    EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
+
+    // Cancel battery saver mode and screen on activation.
+    event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 16, 140);
+    processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16);
+    EXPECT_FALSE(metricsManager->isActive());
+    EXPECT_EQ(broadcastCount, 6);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+    EXPECT_FALSE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+    EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+    EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
+    EXPECT_FALSE(metricProducer2->mIsActive);
+    EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+    EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
+    EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
+    EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
+                           ADB_DUMP, FAST, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStartEndTimestamp(&reports);
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(2, reports.reports(0).metrics_size());
+    EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size());
+    EXPECT_EQ(5, reports.reports(0).metrics(1).count_metrics().data_size());
+
+    StatsLogReport::CountMetricDataWrapper countMetrics;
+
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+    EXPECT_EQ(5, countMetrics.data_size());
+
+    auto data = countMetrics.data(0);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(1);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(2);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    // Partial bucket as metric is deactivated.
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8,
+              data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(3);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+              data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11,
+              data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(4);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+              data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11,
+              data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    countMetrics.clear_data();
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(1).count_metrics(), &countMetrics);
+    EXPECT_EQ(5, countMetrics.data_size());
+
+    data = countMetrics.data(0);
+    EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(2222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(1);
+    EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(3333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(2);
+    EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(4444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    // Partial bucket as metric is deactivated.
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8,
+              data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(3);
+    EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(6666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+              data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11,
+              data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(4);
+    EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(7777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+              data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11,
+              data.bucket_info(0).end_bucket_elapsed_nanos());
+}
 
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
index 7d93fcc..e8fb523 100644
--- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -97,250 +97,247 @@
 }
 }  // namespace
 
-// TODO(b/149590301): Update these tests to use new socket schema.
-//// If we want to test multiple dump data, we must do it in separate tests, because in the e2e tests,
-//// we should use the real API which will clear the data after dump data is called.
-//TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks1) {
-//    auto config = CreateStatsdConfig();
-//    uint64_t bucketStartTimeNs = 10000000000;
-//    uint64_t bucketSizeNs =
-//        TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
-//
-//    ConfigKey cfgKey;
-//    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-//
-//    int appUid = 123;
-//    auto crashEvent1 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 1);
-//    auto crashEvent2 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 201);
-//    auto crashEvent3= CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 101);
-//
-//    auto crashEvent4 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 51);
-//    auto crashEvent5 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 299);
-//    auto crashEvent6 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 2001);
-//
-//    auto crashEvent7 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 16);
-//    auto crashEvent8 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 249);
-//
-//    auto crashEvent9 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 351);
-//    auto crashEvent10 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 2);
-//
-//    auto screenTurnedOnEvent =
-//        CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
-//                                      bucketStartTimeNs + 2);
-//    auto screenTurnedOffEvent =
-//        CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
-//                                      bucketStartTimeNs + 200);
-//    auto screenTurnedOnEvent2 =
-//        CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
-//                                      bucketStartTimeNs + 2 * bucketSizeNs - 100);
-//
-//    std::vector<AttributionNodeInternal> attributions = {
-//            CreateAttribution(appUid, "App1"), CreateAttribution(appUid + 1, "GMSCoreModule1")};
-//    auto syncOnEvent1 =
-//        CreateSyncStartEvent(attributions, "ReadEmail", bucketStartTimeNs + 50);
-//    auto syncOffEvent1 =
-//        CreateSyncEndEvent(attributions, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300);
-//    auto syncOnEvent2 =
-//        CreateSyncStartEvent(attributions, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000);
-//
-//    auto moveToBackgroundEvent1 =
-//        CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 15);
-//    auto moveToForegroundEvent1 =
-//        CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 250);
-//
-//    auto moveToBackgroundEvent2 =
-//        CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 350);
-//    auto moveToForegroundEvent2 =
-//        CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 1);
-//
-//    /*
-//                    bucket #1                               bucket #2
-//
-//
-//       |      |   |  |                      |   |          |        |   |   |     (crashEvents)
-//    |-------------------------------------|-----------------------------------|---------
-//
-//             |                                           |                        (MoveToBkground)
-//
-//                                             |                               |    (MoveToForeground)
-//
-//                |                                                 |                (SyncIsOn)
-//                                                  |                                (SyncIsOff)
-//          |                                                               |        (ScreenIsOn)
-//                   |                                                               (ScreenIsOff)
-//    */
-//    std::vector<std::unique_ptr<LogEvent>> events;
-//    events.push_back(std::move(crashEvent1));
-//    events.push_back(std::move(crashEvent2));
-//    events.push_back(std::move(crashEvent3));
-//    events.push_back(std::move(crashEvent4));
-//    events.push_back(std::move(crashEvent5));
-//    events.push_back(std::move(crashEvent6));
-//    events.push_back(std::move(crashEvent7));
-//    events.push_back(std::move(crashEvent8));
-//    events.push_back(std::move(crashEvent9));
-//    events.push_back(std::move(crashEvent10));
-//    events.push_back(std::move(screenTurnedOnEvent));
-//    events.push_back(std::move(screenTurnedOffEvent));
-//    events.push_back(std::move(screenTurnedOnEvent2));
-//    events.push_back(std::move(syncOnEvent1));
-//    events.push_back(std::move(syncOffEvent1));
-//    events.push_back(std::move(syncOnEvent2));
-//    events.push_back(std::move(moveToBackgroundEvent1));
-//    events.push_back(std::move(moveToForegroundEvent1));
-//    events.push_back(std::move(moveToBackgroundEvent2));
-//    events.push_back(std::move(moveToForegroundEvent2));
-//
-//    sortLogEventsByTimestamp(&events);
-//
-//    for (const auto& event : events) {
-//        processor->OnLogEvent(event.get());
-//    }
-//    ConfigMetricsReportList reports;
-//    vector<uint8_t> buffer;
-//    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true,
-//                            ADB_DUMP, FAST, &buffer);
-//    EXPECT_TRUE(buffer.size() > 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStringInReport(&reports);
-//    backfillStartEndTimestamp(&reports);
-//    EXPECT_EQ(reports.reports_size(), 1);
-//    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
-//    EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1);
-//    EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 1);
-//    EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1);
-//    auto data = reports.reports(0).metrics(0).count_metrics().data(0);
-//    // Validate dimension value.
-//    EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
-//    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
-//    // Uid field.
-//    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
-//    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid);
-//}
-//
-//TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks2) {
-//    auto config = CreateStatsdConfig();
-//    uint64_t bucketStartTimeNs = 10000000000;
-//    uint64_t bucketSizeNs =
-//            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
-//
-//    ConfigKey cfgKey;
-//    auto processor = CreateStatsLogProcessor(
-//            bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-//
-//    int appUid = 123;
-//    auto crashEvent1 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 1);
-//    auto crashEvent2 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 201);
-//    auto crashEvent3 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 101);
-//
-//    auto crashEvent4 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 51);
-//    auto crashEvent5 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 299);
-//    auto crashEvent6 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 2001);
-//
-//    auto crashEvent7 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 16);
-//    auto crashEvent8 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 249);
-//
-//    auto crashEvent9 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 351);
-//    auto crashEvent10 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 2);
-//
-//    auto screenTurnedOnEvent = CreateScreenStateChangedEvent(
-//            android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 2);
-//    auto screenTurnedOffEvent = CreateScreenStateChangedEvent(
-//            android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 200);
-//    auto screenTurnedOnEvent2 =
-//            CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
-//                                          bucketStartTimeNs + 2 * bucketSizeNs - 100);
-//
-//    std::vector<AttributionNodeInternal> attributions = {
-//            CreateAttribution(appUid, "App1"), CreateAttribution(appUid + 1, "GMSCoreModule1")};
-//    auto syncOnEvent1 = CreateSyncStartEvent(attributions, "ReadEmail", bucketStartTimeNs + 50);
-//    auto syncOffEvent1 =
-//            CreateSyncEndEvent(attributions, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300);
-//    auto syncOnEvent2 =
-//            CreateSyncStartEvent(attributions, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000);
-//
-//    auto moveToBackgroundEvent1 = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 15);
-//    auto moveToForegroundEvent1 =
-//            CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 250);
-//
-//    auto moveToBackgroundEvent2 =
-//            CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 350);
-//    auto moveToForegroundEvent2 =
-//            CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 1);
-//
-//    /*
-//                    bucket #1                               bucket #2
-//
-//
-//       |      |   |  |                      |   |          |        |   |   |     (crashEvents)
-//    |-------------------------------------|-----------------------------------|---------
-//
-//             |                                           |                        (MoveToBkground)
-//
-//                                             |                               |    (MoveToForeground)
-//
-//                |                                                 |                (SyncIsOn)
-//                                                  |                                (SyncIsOff)
-//          |                                                               |        (ScreenIsOn)
-//                   |                                                               (ScreenIsOff)
-//    */
-//    std::vector<std::unique_ptr<LogEvent>> events;
-//    events.push_back(std::move(crashEvent1));
-//    events.push_back(std::move(crashEvent2));
-//    events.push_back(std::move(crashEvent3));
-//    events.push_back(std::move(crashEvent4));
-//    events.push_back(std::move(crashEvent5));
-//    events.push_back(std::move(crashEvent6));
-//    events.push_back(std::move(crashEvent7));
-//    events.push_back(std::move(crashEvent8));
-//    events.push_back(std::move(crashEvent9));
-//    events.push_back(std::move(crashEvent10));
-//    events.push_back(std::move(screenTurnedOnEvent));
-//    events.push_back(std::move(screenTurnedOffEvent));
-//    events.push_back(std::move(screenTurnedOnEvent2));
-//    events.push_back(std::move(syncOnEvent1));
-//    events.push_back(std::move(syncOffEvent1));
-//    events.push_back(std::move(syncOnEvent2));
-//    events.push_back(std::move(moveToBackgroundEvent1));
-//    events.push_back(std::move(moveToForegroundEvent1));
-//    events.push_back(std::move(moveToBackgroundEvent2));
-//    events.push_back(std::move(moveToForegroundEvent2));
-//
-//    sortLogEventsByTimestamp(&events);
-//
-//    for (const auto& event : events) {
-//        processor->OnLogEvent(event.get());
-//    }
-//    ConfigMetricsReportList reports;
-//    vector<uint8_t> buffer;
-//
-//    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
-//                            ADB_DUMP, FAST, &buffer);
-//    EXPECT_TRUE(buffer.size() > 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStringInReport(&reports);
-//    backfillStartEndTimestamp(&reports);
-//    EXPECT_EQ(reports.reports_size(), 1);
-//    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
-//    EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1);
-//    EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 2);
-//    EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1);
-//    EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(1).count(), 3);
-//    auto data = reports.reports(0).metrics(0).count_metrics().data(0);
-//    // Validate dimension value.
-//    EXPECT_EQ(data.dimensions_in_what().field(),
-//              android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
-//    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
-//    // Uid field.
-//    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
-//    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid);
-//}
+// If we want to test multiple dump data, we must do it in separate tests, because in the e2e tests,
+// we should use the real API which will clear the data after dump data is called.
+TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks1) {
+    auto config = CreateStatsdConfig();
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+    int appUid = 123;
+    auto crashEvent1 = CreateAppCrashEvent(bucketStartTimeNs + 1, appUid);
+    auto crashEvent2 = CreateAppCrashEvent(bucketStartTimeNs + 201, appUid);
+    auto crashEvent3 = CreateAppCrashEvent(bucketStartTimeNs + 2 * bucketSizeNs - 101, appUid);
+
+    auto crashEvent4 = CreateAppCrashEvent(bucketStartTimeNs + 51, appUid);
+    auto crashEvent5 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 299, appUid);
+    auto crashEvent6 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 2001, appUid);
+
+    auto crashEvent7 = CreateAppCrashEvent(bucketStartTimeNs + 16, appUid);
+    auto crashEvent8 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 249, appUid);
+
+    auto crashEvent9 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 351, appUid);
+    auto crashEvent10 = CreateAppCrashEvent(bucketStartTimeNs + 2 * bucketSizeNs - 2, appUid);
+
+    auto screenTurnedOnEvent = CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 2, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+    auto screenTurnedOffEvent = CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 200, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
+    auto screenTurnedOnEvent2 =
+            CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs - 100,
+                                          android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+
+    std::vector<int> attributionUids = {appUid, appUid + 1};
+    std::vector<string> attributionTags = {"App1", "GMSCoreModule1"};
+
+    auto syncOnEvent1 = CreateSyncStartEvent(bucketStartTimeNs + 50, attributionUids,
+                                             attributionTags, "ReadEmail");
+    auto syncOffEvent1 = CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 300, attributionUids,
+                                            attributionTags, "ReadEmail");
+    auto syncOnEvent2 = CreateSyncStartEvent(bucketStartTimeNs + bucketSizeNs + 2000,
+                                             attributionUids, attributionTags, "ReadDoc");
+
+    auto moveToBackgroundEvent1 = CreateMoveToBackgroundEvent(bucketStartTimeNs + 15, appUid);
+    auto moveToForegroundEvent1 =
+            CreateMoveToForegroundEvent(bucketStartTimeNs + bucketSizeNs + 250, appUid);
+
+    auto moveToBackgroundEvent2 =
+            CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 350, appUid);
+    auto moveToForegroundEvent2 =
+            CreateMoveToForegroundEvent(bucketStartTimeNs + 2 * bucketSizeNs - 1, appUid);
+
+    /*
+                    bucket #1                               bucket #2
+
+
+       |      |   |  |                      |   |          |        |   |   |     (crashEvents)
+    |-------------------------------------|-----------------------------------|---------
+
+             |                                           |                        (MoveToBkground)
+
+                                             |                               |    (MoveToForeground)
+
+                |                                                 |                (SyncIsOn)
+                                                  |                                (SyncIsOff)
+          |                                                               |        (ScreenIsOn)
+                   |                                                               (ScreenIsOff)
+    */
+    std::vector<std::unique_ptr<LogEvent>> events;
+    events.push_back(std::move(crashEvent1));
+    events.push_back(std::move(crashEvent2));
+    events.push_back(std::move(crashEvent3));
+    events.push_back(std::move(crashEvent4));
+    events.push_back(std::move(crashEvent5));
+    events.push_back(std::move(crashEvent6));
+    events.push_back(std::move(crashEvent7));
+    events.push_back(std::move(crashEvent8));
+    events.push_back(std::move(crashEvent9));
+    events.push_back(std::move(crashEvent10));
+    events.push_back(std::move(screenTurnedOnEvent));
+    events.push_back(std::move(screenTurnedOffEvent));
+    events.push_back(std::move(screenTurnedOnEvent2));
+    events.push_back(std::move(syncOnEvent1));
+    events.push_back(std::move(syncOffEvent1));
+    events.push_back(std::move(syncOnEvent2));
+    events.push_back(std::move(moveToBackgroundEvent1));
+    events.push_back(std::move(moveToForegroundEvent1));
+    events.push_back(std::move(moveToBackgroundEvent2));
+    events.push_back(std::move(moveToForegroundEvent2));
+
+    sortLogEventsByTimestamp(&events);
+
+    for (const auto& event : events) {
+        processor->OnLogEvent(event.get());
+    }
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true, ADB_DUMP,
+                            FAST, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+    EXPECT_EQ(reports.reports_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1);
+    auto data = reports.reports(0).metrics(0).count_metrics().data(0);
+    // Validate dimension value.
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    // Uid field.
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid);
+}
+
+TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks2) {
+    auto config = CreateStatsdConfig();
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+    int appUid = 123;
+    auto crashEvent1 = CreateAppCrashEvent(bucketStartTimeNs + 1, appUid);
+    auto crashEvent2 = CreateAppCrashEvent(bucketStartTimeNs + 201, appUid);
+    auto crashEvent3 = CreateAppCrashEvent(bucketStartTimeNs + 2 * bucketSizeNs - 101, appUid);
+
+    auto crashEvent4 = CreateAppCrashEvent(bucketStartTimeNs + 51, appUid);
+    auto crashEvent5 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 299, appUid);
+    auto crashEvent6 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 2001, appUid);
+
+    auto crashEvent7 = CreateAppCrashEvent(bucketStartTimeNs + 16, appUid);
+    auto crashEvent8 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 249, appUid);
+
+    auto crashEvent9 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 351, appUid);
+    auto crashEvent10 = CreateAppCrashEvent(bucketStartTimeNs + 2 * bucketSizeNs - 2, appUid);
+
+    auto screenTurnedOnEvent = CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 2, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+    auto screenTurnedOffEvent = CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 200, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
+    auto screenTurnedOnEvent2 =
+            CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs - 100,
+                                          android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+
+    std::vector<int> attributionUids = {appUid, appUid + 1};
+    std::vector<string> attributionTags = {"App1", "GMSCoreModule1"};
+
+    auto syncOnEvent1 = CreateSyncStartEvent(bucketStartTimeNs + 50, attributionUids,
+                                             attributionTags, "ReadEmail");
+    auto syncOffEvent1 = CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 300, attributionUids,
+                                            attributionTags, "ReadEmail");
+    auto syncOnEvent2 = CreateSyncStartEvent(bucketStartTimeNs + bucketSizeNs + 2000,
+                                             attributionUids, attributionTags, "ReadDoc");
+
+    auto moveToBackgroundEvent1 = CreateMoveToBackgroundEvent(bucketStartTimeNs + 15, appUid);
+    auto moveToForegroundEvent1 =
+            CreateMoveToForegroundEvent(bucketStartTimeNs + bucketSizeNs + 250, appUid);
+
+    auto moveToBackgroundEvent2 =
+            CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 350, appUid);
+    auto moveToForegroundEvent2 =
+            CreateMoveToForegroundEvent(bucketStartTimeNs + 2 * bucketSizeNs - 1, appUid);
+
+    /*
+                    bucket #1                               bucket #2
+
+
+       |      |   |  |                      |   |          |        |   |   |     (crashEvents)
+    |-------------------------------------|-----------------------------------|---------
+
+             |                                           |                        (MoveToBkground)
+
+                                             |                               |    (MoveToForeground)
+
+                |                                                 |                (SyncIsOn)
+                                                  |                                (SyncIsOff)
+          |                                                               |        (ScreenIsOn)
+                   |                                                               (ScreenIsOff)
+    */
+    std::vector<std::unique_ptr<LogEvent>> events;
+    events.push_back(std::move(crashEvent1));
+    events.push_back(std::move(crashEvent2));
+    events.push_back(std::move(crashEvent3));
+    events.push_back(std::move(crashEvent4));
+    events.push_back(std::move(crashEvent5));
+    events.push_back(std::move(crashEvent6));
+    events.push_back(std::move(crashEvent7));
+    events.push_back(std::move(crashEvent8));
+    events.push_back(std::move(crashEvent9));
+    events.push_back(std::move(crashEvent10));
+    events.push_back(std::move(screenTurnedOnEvent));
+    events.push_back(std::move(screenTurnedOffEvent));
+    events.push_back(std::move(screenTurnedOnEvent2));
+    events.push_back(std::move(syncOnEvent1));
+    events.push_back(std::move(syncOffEvent1));
+    events.push_back(std::move(syncOnEvent2));
+    events.push_back(std::move(moveToBackgroundEvent1));
+    events.push_back(std::move(moveToForegroundEvent1));
+    events.push_back(std::move(moveToBackgroundEvent2));
+    events.push_back(std::move(moveToForegroundEvent2));
+
+    sortLogEventsByTimestamp(&events);
+
+    for (const auto& event : events) {
+        processor->OnLogEvent(event.get());
+    }
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true, ADB_DUMP,
+                            FAST, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+    EXPECT_EQ(reports.reports_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 2);
+    EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1);
+    EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(1).count(), 3);
+    auto data = reports.reports(0).metrics(0).count_metrics().data(0);
+    // Validate dimension value.
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    // Uid field.
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid);
+}
 
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
index 9ec831b..b975907 100644
--- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
@@ -113,96 +113,107 @@
 }
 }  // anonymous namespace
 
-// TODO(b/149590301): Update this test to use new socket schema.
-//TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit) {
-//    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
-//    SendConfig(service, MakeConfig());
-//    int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
-//                                             // initialized with.
-//
-//    service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
-//    service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 2).get());
-//
-//    ConfigMetricsReport report = GetReports(service->mProcessor, start + 3);
-//    // Expect no metrics since the bucket has not finished yet.
-//    EXPECT_EQ(1, report.metrics_size());
-//    EXPECT_EQ(0, report.metrics(0).count_metrics().data_size());
-//}
-//
-//TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) {
-//    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
-//    SendConfig(service, MakeConfig());
-//    int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
-//                                             // initialized with.
-//
-//    // Force the uidmap to update at timestamp 2.
-//    service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
-//    // This is a new installation, so there shouldn't be a split (should be same as the without
-//    // split case).
-//    service->mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
-//                               String16(""));
-//    // Goes into the second bucket.
-//    service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
-//
-//    ConfigMetricsReport report = GetReports(service->mProcessor, start + 4);
-//    EXPECT_EQ(1, report.metrics_size());
-//    EXPECT_EQ(0, report.metrics(0).count_metrics().data_size());
-//}
-//
-//TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) {
-//    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
-//    SendConfig(service, MakeConfig());
-//    int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
-//                                             // initialized with.
-//    service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
-//                                {String16("")});
-//
-//    // Force the uidmap to update at timestamp 2.
-//    service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
-//    service->mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
-//                               String16(""));
-//    // Goes into the second bucket.
-//    service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
-//
-//    ConfigMetricsReport report = GetReports(service->mProcessor, start + 4);
-//    backfillStartEndTimestamp(&report);
-//
-//    ASSERT_EQ(1, report.metrics_size());
-//    ASSERT_EQ(1, report.metrics(0).count_metrics().data_size());
-//    ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size());
-//    EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
-//                    has_start_bucket_elapsed_nanos());
-//    EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
-//                    has_end_bucket_elapsed_nanos());
-//    EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count());
-//}
-//
-//TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) {
-//    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
-//    SendConfig(service, MakeConfig());
-//    int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
-//                                             // initialized with.
-//    service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
-//                                {String16("")});
-//
-//    // Force the uidmap to update at timestamp 2.
-//    service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
-//    service->mUidMap->removeApp(start + 2, String16(kApp1.c_str()), 1);
-//    // Goes into the second bucket.
-//    service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
-//
-//    ConfigMetricsReport report = GetReports(service->mProcessor, start + 4);
-//    backfillStartEndTimestamp(&report);
-//
-//    ASSERT_EQ(1, report.metrics_size());
-//    ASSERT_EQ(1, report.metrics(0).count_metrics().data_size());
-//    ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size());
-//    EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
-//                    has_start_bucket_elapsed_nanos());
-//    EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
-//                    has_end_bucket_elapsed_nanos());
-//    EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count());
-//}
+TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit) {
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
+    SendConfig(service, MakeConfig());
+    int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
+                                             // initialized with.
+
+    service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 1, 100).get());
+    service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 2, 100).get());
+
+    ConfigMetricsReport report = GetReports(service->mProcessor, start + 3);
+    // Expect no metrics since the bucket has not finished yet.
+    EXPECT_EQ(1, report.metrics_size());
+    EXPECT_EQ(0, report.metrics(0).count_metrics().data_size());
+}
+
+TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) {
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
+    SendConfig(service, MakeConfig());
+    int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
+                                             // initialized with.
+
+    // Force the uidmap to update at timestamp 2.
+    service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 1, 100).get());
+    // This is a new installation, so there shouldn't be a split (should be same as the without
+    // split case).
+    service->mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
+                                String16(""));
+    // Goes into the second bucket.
+    service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3, 100).get());
+
+    ConfigMetricsReport report = GetReports(service->mProcessor, start + 4);
+    EXPECT_EQ(1, report.metrics_size());
+    EXPECT_EQ(0, report.metrics(0).count_metrics().data_size());
+}
+
+TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) {
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
+    SendConfig(service, MakeConfig());
+    int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
+                                             // initialized with.
+    service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
+                                {String16("")});
+
+    // Force the uidmap to update at timestamp 2.
+    service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 1, 100).get());
+    service->mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
+                                String16(""));
+    // Goes into the second bucket.
+    service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3, 100).get());
+
+    ConfigMetricsReport report = GetReports(service->mProcessor, start + 4);
+    backfillStartEndTimestamp(&report);
+
+    ASSERT_EQ(1, report.metrics_size());
+    ASSERT_EQ(1, report.metrics(0).count_metrics().data_size());
+    ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size());
+    EXPECT_TRUE(report.metrics(0)
+                        .count_metrics()
+                        .data(0)
+                        .bucket_info(0)
+                        .has_start_bucket_elapsed_nanos());
+    EXPECT_TRUE(report.metrics(0)
+                        .count_metrics()
+                        .data(0)
+                        .bucket_info(0)
+                        .has_end_bucket_elapsed_nanos());
+    EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count());
+}
+
+TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) {
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
+    SendConfig(service, MakeConfig());
+    int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
+                                             // initialized with.
+    service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
+                                {String16("")});
+
+    // Force the uidmap to update at timestamp 2.
+    service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 1, 100).get());
+    service->mUidMap->removeApp(start + 2, String16(kApp1.c_str()), 1);
+    // Goes into the second bucket.
+    service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3, 100).get());
+
+    ConfigMetricsReport report = GetReports(service->mProcessor, start + 4);
+    backfillStartEndTimestamp(&report);
+
+    ASSERT_EQ(1, report.metrics_size());
+    ASSERT_EQ(1, report.metrics(0).count_metrics().data_size());
+    ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size());
+    EXPECT_TRUE(report.metrics(0)
+                        .count_metrics()
+                        .data(0)
+                        .bucket_info(0)
+                        .has_start_bucket_elapsed_nanos());
+    EXPECT_TRUE(report.metrics(0)
+                        .count_metrics()
+                        .data(0)
+                        .bucket_info(0)
+                        .has_end_bucket_elapsed_nanos());
+    EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count());
+}
 
 TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) {
     shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
index 99dbaf1..a87bb71 100644
--- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
@@ -64,317 +64,313 @@
 
 }  // namespace
 
-// TODO(b/149590301): Update this test to use new socket schema.
-//TEST(ValueMetricE2eTest, TestPulledEvents) {
-//    auto config = CreateStatsdConfig();
-//    int64_t baseTimeNs = getElapsedRealtimeNs();
-//    int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
-//    int64_t bucketSizeNs =
-//        TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
-//
-//    ConfigKey cfgKey;
-//    auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
-//                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
-//                                             android::util::SUBSYSTEM_SLEEP_STATE);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-//    processor->mPullerManager->ForceClearPullerCache();
-//
-//    int startBucketNum = processor->mMetricsManagers.begin()->second->
-//            mAllMetricProducers[0]->getCurrentBucketNum();
-//    EXPECT_GT(startBucketNum, (int64_t)0);
-//
-//    // When creating the config, the value metric producer should register the alarm at the
-//    // end of the current bucket.
-//    EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
-//    EXPECT_EQ(bucketSizeNs,
-//              processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
-//    int64_t& expectedPullTimeNs =
-//            processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs);
-//
-//    auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-//                                                        configAddedTimeNs + 55);
-//    processor->OnLogEvent(screenOffEvent.get());
-//
-//    auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-//                                                       configAddedTimeNs + 65);
-//    processor->OnLogEvent(screenOnEvent.get());
-//
-//    screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-//                                                   configAddedTimeNs + 75);
-//    processor->OnLogEvent(screenOffEvent.get());
-//
-//    // Pulling alarm arrives on time and reset the sequential pulling alarm.
-//    processor->informPullAlarmFired(expectedPullTimeNs + 1);
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, expectedPullTimeNs);
-//
-//    processor->informPullAlarmFired(expectedPullTimeNs + 1);
-//
-//    screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-//                                                       configAddedTimeNs + 2 * bucketSizeNs + 15);
-//    processor->OnLogEvent(screenOnEvent.get());
-//
-//    processor->informPullAlarmFired(expectedPullTimeNs + 1);
-//
-//    processor->informPullAlarmFired(expectedPullTimeNs + 1);
-//
-//    screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-//                                                   configAddedTimeNs + 4 * bucketSizeNs + 11);
-//    processor->OnLogEvent(screenOffEvent.get());
-//
-//    processor->informPullAlarmFired(expectedPullTimeNs + 1);
-//
-//    processor->informPullAlarmFired(expectedPullTimeNs + 1);
-//
-//    ConfigMetricsReportList reports;
-//    vector<uint8_t> buffer;
-//    processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
-//                            ADB_DUMP, FAST, &buffer);
-//    EXPECT_TRUE(buffer.size() > 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStringInReport(&reports);
-//    backfillStartEndTimestamp(&reports);
-//    EXPECT_EQ(1, reports.reports_size());
-//    EXPECT_EQ(1, reports.reports(0).metrics_size());
-//    StatsLogReport::ValueMetricDataWrapper valueMetrics;
-//    sortMetricDataByDimensionsValue(
-//            reports.reports(0).metrics(0).value_metrics(), &valueMetrics);
-//    EXPECT_GT((int)valueMetrics.data_size(), 1);
-//
-//    auto data = valueMetrics.data(0);
-//    EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* subsystem name field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
-//    // We have 4 buckets, the first one was incomplete since the condition was unknown.
-//    EXPECT_EQ(4, data.bucket_info_size());
-//
-//    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-//    EXPECT_EQ(1, data.bucket_info(0).values_size());
-//
-//    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
-//    EXPECT_EQ(1, data.bucket_info(1).values_size());
-//
-//    EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
-//    EXPECT_EQ(1, data.bucket_info(2).values_size());
-//
-//    EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos());
-//    EXPECT_EQ(1, data.bucket_info(3).values_size());
-//}
-//
-//TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm) {
-//    auto config = CreateStatsdConfig();
-//    int64_t baseTimeNs = getElapsedRealtimeNs();
-//    // 10 mins == 2 bucket durations.
-//    int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
-//    int64_t bucketSizeNs =
-//        TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
-//
-//    ConfigKey cfgKey;
-//    auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
-//                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
-//                                             android::util::SUBSYSTEM_SLEEP_STATE);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-//    processor->mPullerManager->ForceClearPullerCache();
-//
-//    int startBucketNum = processor->mMetricsManagers.begin()->second->
-//            mAllMetricProducers[0]->getCurrentBucketNum();
-//    EXPECT_GT(startBucketNum, (int64_t)0);
-//
-//    // When creating the config, the value metric producer should register the alarm at the
-//    // end of the current bucket.
-//    EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
-//    EXPECT_EQ(bucketSizeNs,
-//              processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
-//    int64_t& expectedPullTimeNs =
-//            processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs);
-//
-//    // Screen off/on/off events.
-//    auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-//                                                        configAddedTimeNs + 55);
-//    processor->OnLogEvent(screenOffEvent.get());
-//
-//    auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-//                                                       configAddedTimeNs + 65);
-//    processor->OnLogEvent(screenOnEvent.get());
-//
-//    screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-//                                                   configAddedTimeNs + 75);
-//    processor->OnLogEvent(screenOffEvent.get());
-//
-//    // Pulling alarm arrives late by 2 buckets and 1 ns. 2 buckets late is too far away in the
-//    // future, data will be skipped.
-//    processor->informPullAlarmFired(expectedPullTimeNs + 2 * bucketSizeNs + 1);
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, expectedPullTimeNs);
-//
-//    // This screen state change will start a new bucket.
-//    screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-//                                                       configAddedTimeNs + 4 * bucketSizeNs + 65);
-//    processor->OnLogEvent(screenOnEvent.get());
-//
-//    // The alarm is delayed but we already created a bucket thanks to the screen state condition.
-//    // This bucket does not have to be skipped since the alarm arrives in time for the next bucket.
-//    processor->informPullAlarmFired(expectedPullTimeNs + bucketSizeNs + 21);
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, expectedPullTimeNs);
-//
-//    screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-//                                                   configAddedTimeNs + 6 * bucketSizeNs + 31);
-//    processor->OnLogEvent(screenOffEvent.get());
-//
-//    processor->informPullAlarmFired(expectedPullTimeNs + bucketSizeNs + 21);
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 8 * bucketSizeNs, expectedPullTimeNs);
-//
-//    processor->informPullAlarmFired(expectedPullTimeNs + 1);
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 9 * bucketSizeNs, expectedPullTimeNs);
-//
-//    ConfigMetricsReportList reports;
-//    vector<uint8_t> buffer;
-//    processor->onDumpReport(cfgKey, configAddedTimeNs + 9 * bucketSizeNs + 10, false, true,
-//                            ADB_DUMP, FAST, &buffer);
-//    EXPECT_TRUE(buffer.size() > 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStringInReport(&reports);
-//    backfillStartEndTimestamp(&reports);
-//    EXPECT_EQ(1, reports.reports_size());
-//    EXPECT_EQ(1, reports.reports(0).metrics_size());
-//    StatsLogReport::ValueMetricDataWrapper valueMetrics;
-//    sortMetricDataByDimensionsValue(
-//            reports.reports(0).metrics(0).value_metrics(), &valueMetrics);
-//    EXPECT_GT((int)valueMetrics.data_size(), 1);
-//
-//    auto data = valueMetrics.data(0);
-//    EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* subsystem name field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
-//    EXPECT_EQ(3, data.bucket_info_size());
-//
-//    EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-//    EXPECT_EQ(1, data.bucket_info(0).values_size());
-//
-//    EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
-//    EXPECT_EQ(1, data.bucket_info(1).values_size());
-//
-//    EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
-//    EXPECT_EQ(baseTimeNs + 10 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
-//    EXPECT_EQ(1, data.bucket_info(2).values_size());
-//}
-//
-//TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation) {
-//    auto config = CreateStatsdConfig(false);
-//    int64_t baseTimeNs = getElapsedRealtimeNs();
-//    int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
-//    int64_t bucketSizeNs =
-//        TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
-//
-//    auto batterySaverStartMatcher = CreateBatterySaverModeStartAtomMatcher();
-//    *config.add_atom_matcher() = batterySaverStartMatcher;
-//    const int64_t ttlNs = 2 * bucketSizeNs; // Two buckets.
-//    auto metric_activation = config.add_metric_activation();
-//    metric_activation->set_metric_id(metricId);
-//    metric_activation->set_activation_type(ACTIVATE_IMMEDIATELY);
-//    auto event_activation = metric_activation->add_event_activation();
-//    event_activation->set_atom_matcher_id(batterySaverStartMatcher.id());
-//    event_activation->set_ttl_seconds(ttlNs / 1000000000);
-//
-//    ConfigKey cfgKey;
-//    auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
-//                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
-//                                             android::util::SUBSYSTEM_SLEEP_STATE);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-//    processor->mPullerManager->ForceClearPullerCache();
-//
-//    int startBucketNum = processor->mMetricsManagers.begin()->second->
-//            mAllMetricProducers[0]->getCurrentBucketNum();
-//    EXPECT_GT(startBucketNum, (int64_t)0);
-//    EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
-//
-//    // When creating the config, the value metric producer should register the alarm at the
-//    // end of the current bucket.
-//    EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
-//    EXPECT_EQ(bucketSizeNs,
-//              processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
-//    int64_t& expectedPullTimeNs =
-//            processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs);
-//
-//    // Pulling alarm arrives on time and reset the sequential pulling alarm.
-//    processor->informPullAlarmFired(expectedPullTimeNs + 1); // 15 mins + 1 ns.
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, expectedPullTimeNs);
-//    EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
-//
-//    // Activate the metric. A pull occurs here
-//    const int64_t activationNs = configAddedTimeNs + bucketSizeNs + (2 * 1000 * 1000); // 2 millis.
-//    auto batterySaverOnEvent = CreateBatterySaverOnEvent(activationNs);
-//    processor->OnLogEvent(batterySaverOnEvent.get()); // 15 mins + 2 ms.
-//    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
-//
-//    processor->informPullAlarmFired(expectedPullTimeNs + 1); // 20 mins + 1 ns.
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, expectedPullTimeNs);
-//
-//    processor->informPullAlarmFired(expectedPullTimeNs + 2); // 25 mins + 2 ns.
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, expectedPullTimeNs);
-//
-//    // Create random event to deactivate metric.
-//    auto deactivationEvent = CreateScreenBrightnessChangedEvent(50, activationNs + ttlNs + 1);
-//    processor->OnLogEvent(deactivationEvent.get());
-//    EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
-//
-//    processor->informPullAlarmFired(expectedPullTimeNs + 3);
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, expectedPullTimeNs);
-//
-//    processor->informPullAlarmFired(expectedPullTimeNs + 4);
-//    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, expectedPullTimeNs);
-//
-//    ConfigMetricsReportList reports;
-//    vector<uint8_t> buffer;
-//    processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
-//                            ADB_DUMP, FAST, &buffer);
-//    EXPECT_TRUE(buffer.size() > 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStringInReport(&reports);
-//    backfillStartEndTimestamp(&reports);
-//    EXPECT_EQ(1, reports.reports_size());
-//    EXPECT_EQ(1, reports.reports(0).metrics_size());
-//    StatsLogReport::ValueMetricDataWrapper valueMetrics;
-//    sortMetricDataByDimensionsValue(
-//            reports.reports(0).metrics(0).value_metrics(), &valueMetrics);
-//    EXPECT_GT((int)valueMetrics.data_size(), 0);
-//
-//    auto data = valueMetrics.data(0);
-//    EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
-//    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
-//    EXPECT_EQ(1 /* subsystem name field */,
-//              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
-//    EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
-//    // We have 2 full buckets, the two surrounding the activation are dropped.
-//    EXPECT_EQ(2, data.bucket_info_size());
-//
-//    auto bucketInfo = data.bucket_info(0);
-//    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos());
-//    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
-//    EXPECT_EQ(1, bucketInfo.values_size());
-//
-//    bucketInfo = data.bucket_info(1);
-//    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos());
-//    EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
-//    EXPECT_EQ(1, bucketInfo.values_size());
-//}
+TEST(ValueMetricE2eTest, TestPulledEvents) {
+    auto config = CreateStatsdConfig();
+    int64_t baseTimeNs = getElapsedRealtimeNs();
+    int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
+                                             android::util::SUBSYSTEM_SLEEP_STATE);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    processor->mPullerManager->ForceClearPullerCache();
+
+    int startBucketNum = processor->mMetricsManagers.begin()
+                                 ->second->mAllMetricProducers[0]
+                                 ->getCurrentBucketNum();
+    EXPECT_GT(startBucketNum, (int64_t)0);
+
+    // When creating the config, the value metric producer should register the alarm at the
+    // end of the current bucket.
+    EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
+    EXPECT_EQ(bucketSizeNs,
+              processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
+    int64_t& expectedPullTimeNs =
+            processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs);
+
+    auto screenOffEvent =
+            CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    auto screenOnEvent =
+            CreateScreenStateChangedEvent(configAddedTimeNs + 65, android::view::DISPLAY_STATE_ON);
+    processor->OnLogEvent(screenOnEvent.get());
+
+    screenOffEvent =
+            CreateScreenStateChangedEvent(configAddedTimeNs + 75, android::view::DISPLAY_STATE_OFF);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    // Pulling alarm arrives on time and reset the sequential pulling alarm.
+    processor->informPullAlarmFired(expectedPullTimeNs + 1);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, expectedPullTimeNs);
+
+    processor->informPullAlarmFired(expectedPullTimeNs + 1);
+
+    screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 2 * bucketSizeNs + 15,
+                                                  android::view::DISPLAY_STATE_ON);
+    processor->OnLogEvent(screenOnEvent.get());
+
+    processor->informPullAlarmFired(expectedPullTimeNs + 1);
+
+    processor->informPullAlarmFired(expectedPullTimeNs + 1);
+
+    screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 4 * bucketSizeNs + 11,
+                                                   android::view::DISPLAY_STATE_OFF);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    processor->informPullAlarmFired(expectedPullTimeNs + 1);
+
+    processor->informPullAlarmFired(expectedPullTimeNs + 1);
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
+                            ADB_DUMP, FAST, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    StatsLogReport::ValueMetricDataWrapper valueMetrics;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(), &valueMetrics);
+    EXPECT_GT((int)valueMetrics.data_size(), 1);
+
+    auto data = valueMetrics.data(0);
+    EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* subsystem name field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
+    // We have 4 buckets, the first one was incomplete since the condition was unknown.
+    EXPECT_EQ(4, data.bucket_info_size());
+
+    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+    EXPECT_EQ(1, data.bucket_info(0).values_size());
+
+    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
+    EXPECT_EQ(1, data.bucket_info(1).values_size());
+
+    EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
+    EXPECT_EQ(1, data.bucket_info(2).values_size());
+
+    EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos());
+    EXPECT_EQ(1, data.bucket_info(3).values_size());
+}
+
+TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm) {
+    auto config = CreateStatsdConfig();
+    int64_t baseTimeNs = getElapsedRealtimeNs();
+    // 10 mins == 2 bucket durations.
+    int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
+                                             android::util::SUBSYSTEM_SLEEP_STATE);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    processor->mPullerManager->ForceClearPullerCache();
+
+    int startBucketNum = processor->mMetricsManagers.begin()
+                                 ->second->mAllMetricProducers[0]
+                                 ->getCurrentBucketNum();
+    EXPECT_GT(startBucketNum, (int64_t)0);
+
+    // When creating the config, the value metric producer should register the alarm at the
+    // end of the current bucket.
+    EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
+    EXPECT_EQ(bucketSizeNs,
+              processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
+    int64_t& expectedPullTimeNs =
+            processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs);
+
+    // Screen off/on/off events.
+    auto screenOffEvent =
+            CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    auto screenOnEvent =
+            CreateScreenStateChangedEvent(configAddedTimeNs + 65, android::view::DISPLAY_STATE_ON);
+    processor->OnLogEvent(screenOnEvent.get());
+
+    screenOffEvent =
+            CreateScreenStateChangedEvent(configAddedTimeNs + 75, android::view::DISPLAY_STATE_OFF);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    // Pulling alarm arrives late by 2 buckets and 1 ns. 2 buckets late is too far away in the
+    // future, data will be skipped.
+    processor->informPullAlarmFired(expectedPullTimeNs + 2 * bucketSizeNs + 1);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, expectedPullTimeNs);
+
+    // This screen state change will start a new bucket.
+    screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 4 * bucketSizeNs + 65,
+                                                  android::view::DISPLAY_STATE_ON);
+    processor->OnLogEvent(screenOnEvent.get());
+
+    // The alarm is delayed but we already created a bucket thanks to the screen state condition.
+    // This bucket does not have to be skipped since the alarm arrives in time for the next bucket.
+    processor->informPullAlarmFired(expectedPullTimeNs + bucketSizeNs + 21);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, expectedPullTimeNs);
+
+    screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 6 * bucketSizeNs + 31,
+                                                   android::view::DISPLAY_STATE_OFF);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    processor->informPullAlarmFired(expectedPullTimeNs + bucketSizeNs + 21);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 8 * bucketSizeNs, expectedPullTimeNs);
+
+    processor->informPullAlarmFired(expectedPullTimeNs + 1);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 9 * bucketSizeNs, expectedPullTimeNs);
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 9 * bucketSizeNs + 10, false, true,
+                            ADB_DUMP, FAST, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    StatsLogReport::ValueMetricDataWrapper valueMetrics;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(), &valueMetrics);
+    EXPECT_GT((int)valueMetrics.data_size(), 1);
+
+    auto data = valueMetrics.data(0);
+    EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* subsystem name field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
+    EXPECT_EQ(3, data.bucket_info_size());
+
+    EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+    EXPECT_EQ(1, data.bucket_info(0).values_size());
+
+    EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
+    EXPECT_EQ(1, data.bucket_info(1).values_size());
+
+    EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 10 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
+    EXPECT_EQ(1, data.bucket_info(2).values_size());
+}
+
+TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation) {
+    auto config = CreateStatsdConfig(false);
+    int64_t baseTimeNs = getElapsedRealtimeNs();
+    int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
+
+    auto batterySaverStartMatcher = CreateBatterySaverModeStartAtomMatcher();
+    *config.add_atom_matcher() = batterySaverStartMatcher;
+    const int64_t ttlNs = 2 * bucketSizeNs;  // Two buckets.
+    auto metric_activation = config.add_metric_activation();
+    metric_activation->set_metric_id(metricId);
+    metric_activation->set_activation_type(ACTIVATE_IMMEDIATELY);
+    auto event_activation = metric_activation->add_event_activation();
+    event_activation->set_atom_matcher_id(batterySaverStartMatcher.id());
+    event_activation->set_ttl_seconds(ttlNs / 1000000000);
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
+                                             android::util::SUBSYSTEM_SLEEP_STATE);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    processor->mPullerManager->ForceClearPullerCache();
+
+    int startBucketNum = processor->mMetricsManagers.begin()
+                                 ->second->mAllMetricProducers[0]
+                                 ->getCurrentBucketNum();
+    EXPECT_GT(startBucketNum, (int64_t)0);
+    EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
+
+    // When creating the config, the value metric producer should register the alarm at the
+    // end of the current bucket.
+    EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
+    EXPECT_EQ(bucketSizeNs,
+              processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
+    int64_t& expectedPullTimeNs =
+            processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs);
+
+    // Pulling alarm arrives on time and reset the sequential pulling alarm.
+    processor->informPullAlarmFired(expectedPullTimeNs + 1);  // 15 mins + 1 ns.
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, expectedPullTimeNs);
+    EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
+
+    // Activate the metric. A pull occurs here
+    const int64_t activationNs = configAddedTimeNs + bucketSizeNs + (2 * 1000 * 1000);  // 2 millis.
+    auto batterySaverOnEvent = CreateBatterySaverOnEvent(activationNs);
+    processor->OnLogEvent(batterySaverOnEvent.get());  // 15 mins + 2 ms.
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
+
+    processor->informPullAlarmFired(expectedPullTimeNs + 1);  // 20 mins + 1 ns.
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, expectedPullTimeNs);
+
+    processor->informPullAlarmFired(expectedPullTimeNs + 2);  // 25 mins + 2 ns.
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, expectedPullTimeNs);
+
+    // Create random event to deactivate metric.
+    auto deactivationEvent = CreateScreenBrightnessChangedEvent(activationNs + ttlNs + 1, 50);
+    processor->OnLogEvent(deactivationEvent.get());
+    EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
+
+    processor->informPullAlarmFired(expectedPullTimeNs + 3);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, expectedPullTimeNs);
+
+    processor->informPullAlarmFired(expectedPullTimeNs + 4);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, expectedPullTimeNs);
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
+                            ADB_DUMP, FAST, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    StatsLogReport::ValueMetricDataWrapper valueMetrics;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(), &valueMetrics);
+    EXPECT_GT((int)valueMetrics.data_size(), 0);
+
+    auto data = valueMetrics.data(0);
+    EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* subsystem name field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
+    // We have 2 full buckets, the two surrounding the activation are dropped.
+    EXPECT_EQ(2, data.bucket_info_size());
+
+    auto bucketInfo = data.bucket_info(0);
+    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
+    EXPECT_EQ(1, bucketInfo.values_size());
+
+    bucketInfo = data.bucket_info(1);
+    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
+    EXPECT_EQ(1, bucketInfo.values_size());
+}
 
 /**
  * Test initialization of a simple value metric that is sliced by a state.
diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
index 21092e2..ddd8f95 100644
--- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
@@ -61,292 +61,290 @@
     return config;
 }
 
-std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
-                                                      CreateAttribution(222, "GMSCoreModule1"),
-                                                      CreateAttribution(222, "GMSCoreModule2")};
+std::vector<int> attributionUids1 = {111, 222, 222};
+std::vector<string> attributionTags1 = {"App1", "GMSCoreModule1", "GMSCoreModule2"};
 
-std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(111, "App2"),
-                                                      CreateAttribution(222, "GMSCoreModule1"),
-                                                      CreateAttribution(222, "GMSCoreModule2")};
+std::vector<int> attributionUids2 = {111, 222, 222};
+std::vector<string> attributionTags2 = {"App2", "GMSCoreModule1", "GMSCoreModule2"};
 
-// TODO(b/149590301): Update this helper to use new socket schema.
-///*
-//Events:
-//Screen off is met from (200ns,1 min+500ns].
-//Acquire event for wl1 from 2ns to 1min+2ns
-//Acquire event for wl2 from 1min-10ns to 2min-15ns
-//*/
-//void FeedEvents(StatsdConfig config, sp<StatsLogProcessor> processor) {
-//    uint64_t bucketStartTimeNs = 10000000000;
-//    uint64_t bucketSizeNs =
-//            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-//
-//    auto screenTurnedOnEvent = CreateScreenStateChangedEvent(
-//            android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 1);
-//    auto screenTurnedOffEvent = CreateScreenStateChangedEvent(
-//            android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 200);
-//    auto screenTurnedOnEvent2 =
-//            CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
-//                                          bucketStartTimeNs + bucketSizeNs + 500);
-//
-//    auto acquireEvent1 = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 2);
-//    auto releaseEvent1 =
-//            CreateReleaseWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 2);
-//    auto acquireEvent2 =
-//            CreateAcquireWakelockEvent(attributions2, "wl2", bucketStartTimeNs + bucketSizeNs - 10);
-//    auto releaseEvent2 = CreateReleaseWakelockEvent(attributions2, "wl2",
-//                                                    bucketStartTimeNs + 2 * bucketSizeNs - 15);
-//
-//    std::vector<std::unique_ptr<LogEvent>> events;
-//
-//    events.push_back(std::move(screenTurnedOnEvent));
-//    events.push_back(std::move(screenTurnedOffEvent));
-//    events.push_back(std::move(screenTurnedOnEvent2));
-//    events.push_back(std::move(acquireEvent1));
-//    events.push_back(std::move(acquireEvent2));
-//    events.push_back(std::move(releaseEvent1));
-//    events.push_back(std::move(releaseEvent2));
-//
-//    sortLogEventsByTimestamp(&events);
-//
-//    for (const auto& event : events) {
-//        processor->OnLogEvent(event.get());
-//    }
-//}
+/*
+Events:
+Screen off is met from (200ns,1 min+500ns].
+Acquire event for wl1 from 2ns to 1min+2ns
+Acquire event for wl2 from 1min-10ns to 2min-15ns
+*/
+void FeedEvents(StatsdConfig config, sp<StatsLogProcessor> processor) {
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+
+    auto screenTurnedOnEvent = CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+    auto screenTurnedOffEvent = CreateScreenStateChangedEvent(
+            bucketStartTimeNs + 200, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
+    auto screenTurnedOnEvent2 =
+            CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 500,
+                                          android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+
+    auto acquireEvent1 = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
+                                                    attributionTags1, "wl1");
+    auto releaseEvent1 = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2,
+                                                    attributionUids1, attributionTags1, "wl1");
+    auto acquireEvent2 = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 10,
+                                                    attributionUids2, attributionTags2, "wl2");
+    auto releaseEvent2 = CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs - 15,
+                                                    attributionUids2, attributionTags2, "wl2");
+
+    std::vector<std::unique_ptr<LogEvent>> events;
+
+    events.push_back(std::move(screenTurnedOnEvent));
+    events.push_back(std::move(screenTurnedOffEvent));
+    events.push_back(std::move(screenTurnedOnEvent2));
+    events.push_back(std::move(acquireEvent1));
+    events.push_back(std::move(acquireEvent2));
+    events.push_back(std::move(releaseEvent1));
+    events.push_back(std::move(releaseEvent2));
+
+    sortLogEventsByTimestamp(&events);
+
+    for (const auto& event : events) {
+        processor->OnLogEvent(event.get());
+    }
+}
 
 }  // namespace
 
-// TODO(b/149590301): Update these tests to use new socket schema.
-//TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1) {
-//    ConfigKey cfgKey;
-//    auto config = CreateStatsdConfig(DurationMetric::SUM);
-//    uint64_t bucketStartTimeNs = 10000000000;
-//    uint64_t bucketSizeNs =
-//            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-//    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-//    FeedEvents(config, processor);
-//    vector<uint8_t> buffer;
-//    ConfigMetricsReportList reports;
-//    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true,
-//                            ADB_DUMP, FAST, &buffer);
-//    EXPECT_TRUE(buffer.size() > 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStringInReport(&reports);
-//    backfillStartEndTimestamp(&reports);
-//
-//    EXPECT_EQ(reports.reports_size(), 1);
-//    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
-//    // Only 1 dimension output. The tag dimension in the predicate has been aggregated.
-//    EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
-//
-//    auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
-//    // Validate dimension value.
-//    ValidateAttributionUidDimension(data.dimensions_in_what(),
-//                                    android::util::WAKELOCK_STATE_CHANGED, 111);
-//    // Validate bucket info.
-//    EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1);
-//    data = reports.reports(0).metrics(0).duration_metrics().data(0);
-//    // The wakelock holding interval starts from the screen off event and to the end of the 1st
-//    // bucket.
-//    EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs - 200);
-//}
-//
-//TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2) {
-//    ConfigKey cfgKey;
-//    auto config = CreateStatsdConfig(DurationMetric::SUM);
-//    uint64_t bucketStartTimeNs = 10000000000;
-//    uint64_t bucketSizeNs =
-//            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-//    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-//    FeedEvents(config, processor);
-//    vector<uint8_t> buffer;
-//    ConfigMetricsReportList reports;
-//    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
-//                            ADB_DUMP, FAST, &buffer);
-//    EXPECT_TRUE(buffer.size() > 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStringInReport(&reports);
-//    backfillStartEndTimestamp(&reports);
-//    EXPECT_EQ(reports.reports_size(), 1);
-//    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
-//    EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
-//    // Dump the report after the end of 2nd bucket.
-//    EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2);
-//    auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
-//    // Validate dimension value.
-//    ValidateAttributionUidDimension(data.dimensions_in_what(),
-//                                    android::util::WAKELOCK_STATE_CHANGED, 111);
-//    // Two output buckets.
-//    // The wakelock holding interval in the 1st bucket starts from the screen off event and to
-//    // the end of the 1st bucket.
-//    EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(),
-//              bucketStartTimeNs + bucketSizeNs - (bucketStartTimeNs + 200));
-//    // The wakelock holding interval in the 2nd bucket starts at the beginning of the bucket and
-//    // ends at the second screen on event.
-//    EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 500UL);
-//}
-//TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration3) {
-//    ConfigKey cfgKey;
-//    auto config = CreateStatsdConfig(DurationMetric::SUM);
-//    uint64_t bucketStartTimeNs = 10000000000;
-//    uint64_t bucketSizeNs =
-//            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-//    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-//    FeedEvents(config, processor);
-//    vector<uint8_t> buffer;
-//    ConfigMetricsReportList reports;
-//
-//    std::vector<std::unique_ptr<LogEvent>> events;
-//    events.push_back(
-//            CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
-//                                          bucketStartTimeNs + 2 * bucketSizeNs + 90));
-//    events.push_back(CreateAcquireWakelockEvent(attributions1, "wl3",
-//                                                bucketStartTimeNs + 2 * bucketSizeNs + 100));
-//    events.push_back(CreateReleaseWakelockEvent(attributions1, "wl3",
-//                                                bucketStartTimeNs + 5 * bucketSizeNs + 100));
-//    sortLogEventsByTimestamp(&events);
-//    for (const auto& event : events) {
-//        processor->OnLogEvent(event.get());
-//    }
-//
-//    processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true,
-//                            ADB_DUMP, FAST, &buffer);
-//    EXPECT_TRUE(buffer.size() > 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStringInReport(&reports);
-//    backfillStartEndTimestamp(&reports);
-//    EXPECT_EQ(reports.reports_size(), 1);
-//    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
-//    EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
-//    EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 6);
-//    auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
-//    ValidateAttributionUidDimension(data.dimensions_in_what(),
-//                                    android::util::WAKELOCK_STATE_CHANGED, 111);
-//    // The last wakelock holding spans 4 buckets.
-//    EXPECT_EQ((unsigned long long)data.bucket_info(2).duration_nanos(), bucketSizeNs - 100);
-//    EXPECT_EQ((unsigned long long)data.bucket_info(3).duration_nanos(), bucketSizeNs);
-//    EXPECT_EQ((unsigned long long)data.bucket_info(4).duration_nanos(), bucketSizeNs);
-//    EXPECT_EQ((unsigned long long)data.bucket_info(5).duration_nanos(), 100UL);
-//}
-//
-//TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration1) {
-//    ConfigKey cfgKey;
-//    auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE);
-//    uint64_t bucketStartTimeNs = 10000000000;
-//    uint64_t bucketSizeNs =
-//            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-//    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-//    FeedEvents(config, processor);
-//    ConfigMetricsReportList reports;
-//    vector<uint8_t> buffer;
-//    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true,
-//                            ADB_DUMP, FAST, &buffer);
-//    EXPECT_TRUE(buffer.size() > 0);
-//
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStringInReport(&reports);
-//    backfillStartEndTimestamp(&reports);
-//
-//    EXPECT_EQ(reports.reports_size(), 1);
-//
-//    // When using ProtoOutputStream, if nothing written to a sub msg, it won't be treated as
-//    // one. It was previsouly 1 because we had a fake onDumpReport which calls add_metric() by
-//    // itself.
-//    EXPECT_EQ(1, reports.reports(0).metrics_size());
-//    EXPECT_EQ(0, reports.reports(0).metrics(0).duration_metrics().data_size());
-//}
-//
-//TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration2) {
-//    ConfigKey cfgKey;
-//    auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE);
-//    uint64_t bucketStartTimeNs = 10000000000;
-//    uint64_t bucketSizeNs =
-//            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-//    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-//    FeedEvents(config, processor);
-//    ConfigMetricsReportList reports;
-//    vector<uint8_t> buffer;
-//    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
-//                            ADB_DUMP, FAST, &buffer);
-//    EXPECT_TRUE(buffer.size() > 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStringInReport(&reports);
-//    backfillStartEndTimestamp(&reports);
-//    EXPECT_EQ(reports.reports_size(), 1);
-//    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
-//    EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
-//    // Dump the report after the end of 2nd bucket. One dimension with one bucket.
-//    EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1);
-//    auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
-//    // Validate dimension value.
-//    ValidateAttributionUidDimension(data.dimensions_in_what(),
-//                                    android::util::WAKELOCK_STATE_CHANGED, 111);
-//    // The max is acquire event for wl1 to screen off start.
-//    EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs + 2 - 200);
-//}
-//
-//TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration3) {
-//    ConfigKey cfgKey;
-//    auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE);
-//    uint64_t bucketStartTimeNs = 10000000000;
-//    uint64_t bucketSizeNs =
-//            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-//    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-//    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-//    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-//    FeedEvents(config, processor);
-//    ConfigMetricsReportList reports;
-//    vector<uint8_t> buffer;
-//
-//    std::vector<std::unique_ptr<LogEvent>> events;
-//    events.push_back(
-//            CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
-//                                          bucketStartTimeNs + 2 * bucketSizeNs + 90));
-//    events.push_back(CreateAcquireWakelockEvent(attributions1, "wl3",
-//                                                bucketStartTimeNs + 2 * bucketSizeNs + 100));
-//    events.push_back(CreateReleaseWakelockEvent(attributions1, "wl3",
-//                                                bucketStartTimeNs + 5 * bucketSizeNs + 100));
-//    sortLogEventsByTimestamp(&events);
-//    for (const auto& event : events) {
-//        processor->OnLogEvent(event.get());
-//    }
-//
-//    processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true,
-//                            ADB_DUMP, FAST, &buffer);
-//    EXPECT_TRUE(buffer.size() > 0);
-//    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-//    backfillDimensionPath(&reports);
-//    backfillStringInReport(&reports);
-//    backfillStartEndTimestamp(&reports);
-//    EXPECT_EQ(reports.reports_size(), 1);
-//    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
-//    EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
-//    EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2);
-//    auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
-//    ValidateAttributionUidDimension(data.dimensions_in_what(),
-//                                    android::util::WAKELOCK_STATE_CHANGED, 111);
-//    // The last wakelock holding spans 4 buckets.
-//    EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 3 * bucketSizeNs);
-//    EXPECT_EQ((unsigned long long)data.bucket_info(1).start_bucket_elapsed_nanos(),
-//              bucketStartTimeNs + 5 * bucketSizeNs);
-//    EXPECT_EQ((unsigned long long)data.bucket_info(1).end_bucket_elapsed_nanos(),
-//              bucketStartTimeNs + 6 * bucketSizeNs);
-//}
+TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1) {
+    ConfigKey cfgKey;
+    auto config = CreateStatsdConfig(DurationMetric::SUM);
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    FeedEvents(config, processor);
+    vector<uint8_t> buffer;
+    ConfigMetricsReportList reports;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true, ADB_DUMP,
+                            FAST, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+
+    EXPECT_EQ(reports.reports_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+    // Only 1 dimension output. The tag dimension in the predicate has been aggregated.
+    EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
+
+    auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
+    // Validate dimension value.
+    ValidateAttributionUidDimension(data.dimensions_in_what(),
+                                    android::util::WAKELOCK_STATE_CHANGED, 111);
+    // Validate bucket info.
+    EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1);
+    data = reports.reports(0).metrics(0).duration_metrics().data(0);
+    // The wakelock holding interval starts from the screen off event and to the end of the 1st
+    // bucket.
+    EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs - 200);
+}
+
+TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2) {
+    ConfigKey cfgKey;
+    auto config = CreateStatsdConfig(DurationMetric::SUM);
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    FeedEvents(config, processor);
+    vector<uint8_t> buffer;
+    ConfigMetricsReportList reports;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true, ADB_DUMP,
+                            FAST, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+    EXPECT_EQ(reports.reports_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
+    // Dump the report after the end of 2nd bucket.
+    EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2);
+    auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
+    // Validate dimension value.
+    ValidateAttributionUidDimension(data.dimensions_in_what(),
+                                    android::util::WAKELOCK_STATE_CHANGED, 111);
+    // Two output buckets.
+    // The wakelock holding interval in the 1st bucket starts from the screen off event and to
+    // the end of the 1st bucket.
+    EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(),
+              bucketStartTimeNs + bucketSizeNs - (bucketStartTimeNs + 200));
+    // The wakelock holding interval in the 2nd bucket starts at the beginning of the bucket and
+    // ends at the second screen on event.
+    EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 500UL);
+}
+
+TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration3) {
+    ConfigKey cfgKey;
+    auto config = CreateStatsdConfig(DurationMetric::SUM);
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    FeedEvents(config, processor);
+    vector<uint8_t> buffer;
+    ConfigMetricsReportList reports;
+
+    std::vector<std::unique_ptr<LogEvent>> events;
+    events.push_back(
+            CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 90,
+                                          android::view::DisplayStateEnum::DISPLAY_STATE_OFF));
+    events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100,
+                                                attributionUids1, attributionTags1, "wl3"));
+    events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs + 100,
+                                                attributionUids1, attributionTags1, "wl3"));
+    sortLogEventsByTimestamp(&events);
+    for (const auto& event : events) {
+        processor->OnLogEvent(event.get());
+    }
+
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true, ADB_DUMP,
+                            FAST, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+    EXPECT_EQ(reports.reports_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 6);
+    auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
+    ValidateAttributionUidDimension(data.dimensions_in_what(),
+                                    android::util::WAKELOCK_STATE_CHANGED, 111);
+    // The last wakelock holding spans 4 buckets.
+    EXPECT_EQ((unsigned long long)data.bucket_info(2).duration_nanos(), bucketSizeNs - 100);
+    EXPECT_EQ((unsigned long long)data.bucket_info(3).duration_nanos(), bucketSizeNs);
+    EXPECT_EQ((unsigned long long)data.bucket_info(4).duration_nanos(), bucketSizeNs);
+    EXPECT_EQ((unsigned long long)data.bucket_info(5).duration_nanos(), 100UL);
+}
+
+TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration1) {
+    ConfigKey cfgKey;
+    auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE);
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    FeedEvents(config, processor);
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true, ADB_DUMP,
+                            FAST, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+
+    EXPECT_EQ(reports.reports_size(), 1);
+
+    // When using ProtoOutputStream, if nothing written to a sub msg, it won't be treated as
+    // one. It was previsouly 1 because we had a fake onDumpReport which calls add_metric() by
+    // itself.
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    EXPECT_EQ(0, reports.reports(0).metrics(0).duration_metrics().data_size());
+}
+
+TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration2) {
+    ConfigKey cfgKey;
+    auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE);
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    FeedEvents(config, processor);
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true, ADB_DUMP,
+                            FAST, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+    EXPECT_EQ(reports.reports_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
+    // Dump the report after the end of 2nd bucket. One dimension with one bucket.
+    EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1);
+    auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
+    // Validate dimension value.
+    ValidateAttributionUidDimension(data.dimensions_in_what(),
+                                    android::util::WAKELOCK_STATE_CHANGED, 111);
+    // The max is acquire event for wl1 to screen off start.
+    EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs + 2 - 200);
+}
+
+TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration3) {
+    ConfigKey cfgKey;
+    auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE);
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    FeedEvents(config, processor);
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+
+    std::vector<std::unique_ptr<LogEvent>> events;
+    events.push_back(
+            CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 90,
+                                          android::view::DisplayStateEnum::DISPLAY_STATE_OFF));
+    events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100,
+                                                attributionUids1, attributionTags1, "wl3"));
+    events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs + 100,
+                                                attributionUids1, attributionTags1, "wl3"));
+    sortLogEventsByTimestamp(&events);
+    for (const auto& event : events) {
+        processor->OnLogEvent(event.get());
+    }
+
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true, ADB_DUMP,
+                            FAST, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+    EXPECT_EQ(reports.reports_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2);
+    auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
+    ValidateAttributionUidDimension(data.dimensions_in_what(),
+                                    android::util::WAKELOCK_STATE_CHANGED, 111);
+    // The last wakelock holding spans 4 buckets.
+    EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 3 * bucketSizeNs);
+    EXPECT_EQ((unsigned long long)data.bucket_info(1).start_bucket_elapsed_nanos(),
+              bucketStartTimeNs + 5 * bucketSizeNs);
+    EXPECT_EQ((unsigned long long)data.bucket_info(1).end_bucket_elapsed_nanos(),
+              bucketStartTimeNs + 6 * bucketSizeNs);
+}
 
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
diff --git a/cmds/statsd/tests/external/StatsPuller_test.cpp b/cmds/statsd/tests/external/StatsPuller_test.cpp
index c0b4f43..e8200d5 100644
--- a/cmds/statsd/tests/external/StatsPuller_test.cpp
+++ b/cmds/statsd/tests/external/StatsPuller_test.cpp
@@ -15,11 +15,14 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <stdio.h>
+
 #include <chrono>
 #include <thread>
 #include <vector>
+
 #include "../metrics/metrics_test_helper.h"
 #include "src/stats_log_util.h"
+#include "stats_event.h"
 #include "tests/statsd_test_util.h"
 
 #ifdef __ANDROID__
@@ -57,13 +60,22 @@
 
 FakePuller puller;
 
-// TODO(b/149590301): Update this helper to use new socket schema.
-//shared_ptr<LogEvent> createSimpleEvent(int64_t eventTimeNs, int64_t value) {
-//    shared_ptr<LogEvent> event = make_shared<LogEvent>(pullTagId, eventTimeNs);
-//    event->write(value);
-//    event->init();
-//    return event;
-//}
+std::unique_ptr<LogEvent> createSimpleEvent(int64_t eventTimeNs, int64_t value) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, pullTagId);
+    AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+    AStatsEvent_writeInt64(statsEvent, value);
+    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;
+}
 
 class StatsPullerTest : public ::testing::Test {
 public:
@@ -80,149 +92,148 @@
 
 }  // Anonymous namespace.
 
-// TODO(b/149590301): Update these tests to use new socket schema.
-//TEST_F(StatsPullerTest, PullSuccess) {
-//    pullData.push_back(createSimpleEvent(1111L, 33));
-//
-//    pullSuccess = true;
-//
-//    vector<std::shared_ptr<LogEvent>> dataHolder;
-//    EXPECT_TRUE(puller.Pull(&dataHolder));
-//    EXPECT_EQ(1, dataHolder.size());
-//    EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
-//    EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
-//    EXPECT_EQ(1, dataHolder[0]->size());
-//    EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
-//
-//    sleep_for(std::chrono::seconds(1));
-//
-//    pullData.clear();
-//    pullData.push_back(createSimpleEvent(2222L, 44));
-//
-//    pullSuccess = true;
-//
-//    EXPECT_TRUE(puller.Pull(&dataHolder));
-//    EXPECT_EQ(1, dataHolder.size());
-//    EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
-//    EXPECT_EQ(2222L, dataHolder[0]->GetElapsedTimestampNs());
-//    EXPECT_EQ(1, dataHolder[0]->size());
-//    EXPECT_EQ(44, dataHolder[0]->getValues()[0].mValue.int_value);
-//}
-//
-//TEST_F(StatsPullerTest, PullFailAfterSuccess) {
-//    pullData.push_back(createSimpleEvent(1111L, 33));
-//
-//    pullSuccess = true;
-//
-//    vector<std::shared_ptr<LogEvent>> dataHolder;
-//    EXPECT_TRUE(puller.Pull(&dataHolder));
-//    EXPECT_EQ(1, dataHolder.size());
-//    EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
-//    EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
-//    EXPECT_EQ(1, dataHolder[0]->size());
-//    EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
-//
-//    sleep_for(std::chrono::seconds(1));
-//
-//    pullData.clear();
-//    pullData.push_back(createSimpleEvent(2222L, 44));
-//
-//    pullSuccess = false;
-//    dataHolder.clear();
-//    EXPECT_FALSE(puller.Pull(&dataHolder));
-//    EXPECT_EQ(0, dataHolder.size());
-//
-//    pullSuccess = true;
-//    dataHolder.clear();
-//    EXPECT_FALSE(puller.Pull(&dataHolder));
-//    EXPECT_EQ(0, dataHolder.size());
-//}
-//
-//// Test pull takes longer than timeout, 2nd pull happens shorter than cooldown
-//TEST_F(StatsPullerTest, PullTakeTooLongAndPullFast) {
-//    pullData.push_back(createSimpleEvent(1111L, 33));
-//    pullSuccess = true;
-//    // timeout is 0.5
-//    pullDelayNs = (long)(0.8 * NS_PER_SEC);
-//
-//    vector<std::shared_ptr<LogEvent>> dataHolder;
-//    EXPECT_FALSE(puller.Pull(&dataHolder));
-//    EXPECT_EQ(0, dataHolder.size());
-//
-//    pullData.clear();
-//    pullData.push_back(createSimpleEvent(2222L, 44));
-//
-//    pullSuccess = true;
-//    dataHolder.clear();
-//    EXPECT_FALSE(puller.Pull(&dataHolder));
-//    EXPECT_EQ(0, dataHolder.size());
-//}
-//
-//TEST_F(StatsPullerTest, PullFail) {
-//    pullData.push_back(createSimpleEvent(1111L, 33));
-//
-//    pullSuccess = false;
-//
-//    vector<std::shared_ptr<LogEvent>> dataHolder;
-//    EXPECT_FALSE(puller.Pull(&dataHolder));
-//    EXPECT_EQ(0, dataHolder.size());
-//}
-//
-//TEST_F(StatsPullerTest, PullTakeTooLong) {
-//    pullData.push_back(createSimpleEvent(1111L, 33));
-//
-//    pullSuccess = true;
-//    pullDelayNs = NS_PER_SEC;
-//
-//    vector<std::shared_ptr<LogEvent>> dataHolder;
-//    EXPECT_FALSE(puller.Pull(&dataHolder));
-//    EXPECT_EQ(0, dataHolder.size());
-//}
-//
-//TEST_F(StatsPullerTest, PullTooFast) {
-//    pullData.push_back(createSimpleEvent(1111L, 33));
-//
-//    pullSuccess = true;
-//
-//    vector<std::shared_ptr<LogEvent>> dataHolder;
-//    EXPECT_TRUE(puller.Pull(&dataHolder));
-//    EXPECT_EQ(1, dataHolder.size());
-//    EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
-//    EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
-//    EXPECT_EQ(1, dataHolder[0]->size());
-//    EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
-//
-//    pullData.clear();
-//    pullData.push_back(createSimpleEvent(2222L, 44));
-//
-//    pullSuccess = true;
-//
-//    dataHolder.clear();
-//    EXPECT_TRUE(puller.Pull(&dataHolder));
-//    EXPECT_EQ(1, dataHolder.size());
-//    EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
-//    EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
-//    EXPECT_EQ(1, dataHolder[0]->size());
-//    EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
-//}
-//
-//TEST_F(StatsPullerTest, PullFailsAndTooFast) {
-//    pullData.push_back(createSimpleEvent(1111L, 33));
-//
-//    pullSuccess = false;
-//
-//    vector<std::shared_ptr<LogEvent>> dataHolder;
-//    EXPECT_FALSE(puller.Pull(&dataHolder));
-//    EXPECT_EQ(0, dataHolder.size());
-//
-//    pullData.clear();
-//    pullData.push_back(createSimpleEvent(2222L, 44));
-//
-//    pullSuccess = true;
-//
-//    EXPECT_FALSE(puller.Pull(&dataHolder));
-//    EXPECT_EQ(0, dataHolder.size());
-//}
+TEST_F(StatsPullerTest, PullSuccess) {
+    pullData.push_back(createSimpleEvent(1111L, 33));
+
+    pullSuccess = true;
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    EXPECT_TRUE(puller.Pull(&dataHolder));
+    EXPECT_EQ(1, dataHolder.size());
+    EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+    EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
+    EXPECT_EQ(1, dataHolder[0]->size());
+    EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
+
+    sleep_for(std::chrono::seconds(1));
+
+    pullData.clear();
+    pullData.push_back(createSimpleEvent(2222L, 44));
+
+    pullSuccess = true;
+
+    EXPECT_TRUE(puller.Pull(&dataHolder));
+    EXPECT_EQ(1, dataHolder.size());
+    EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+    EXPECT_EQ(2222L, dataHolder[0]->GetElapsedTimestampNs());
+    EXPECT_EQ(1, dataHolder[0]->size());
+    EXPECT_EQ(44, dataHolder[0]->getValues()[0].mValue.int_value);
+}
+
+TEST_F(StatsPullerTest, PullFailAfterSuccess) {
+    pullData.push_back(createSimpleEvent(1111L, 33));
+
+    pullSuccess = true;
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    EXPECT_TRUE(puller.Pull(&dataHolder));
+    EXPECT_EQ(1, dataHolder.size());
+    EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+    EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
+    EXPECT_EQ(1, dataHolder[0]->size());
+    EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
+
+    sleep_for(std::chrono::seconds(1));
+
+    pullData.clear();
+    pullData.push_back(createSimpleEvent(2222L, 44));
+
+    pullSuccess = false;
+    dataHolder.clear();
+    EXPECT_FALSE(puller.Pull(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+
+    pullSuccess = true;
+    dataHolder.clear();
+    EXPECT_FALSE(puller.Pull(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+}
+
+// Test pull takes longer than timeout, 2nd pull happens shorter than cooldown
+TEST_F(StatsPullerTest, PullTakeTooLongAndPullFast) {
+    pullData.push_back(createSimpleEvent(1111L, 33));
+    pullSuccess = true;
+    // timeout is 0.5
+    pullDelayNs = (long)(0.8 * NS_PER_SEC);
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    EXPECT_FALSE(puller.Pull(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+
+    pullData.clear();
+    pullData.push_back(createSimpleEvent(2222L, 44));
+
+    pullSuccess = true;
+    dataHolder.clear();
+    EXPECT_FALSE(puller.Pull(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+}
+
+TEST_F(StatsPullerTest, PullFail) {
+    pullData.push_back(createSimpleEvent(1111L, 33));
+
+    pullSuccess = false;
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    EXPECT_FALSE(puller.Pull(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+}
+
+TEST_F(StatsPullerTest, PullTakeTooLong) {
+    pullData.push_back(createSimpleEvent(1111L, 33));
+
+    pullSuccess = true;
+    pullDelayNs = NS_PER_SEC;
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    EXPECT_FALSE(puller.Pull(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+}
+
+TEST_F(StatsPullerTest, PullTooFast) {
+    pullData.push_back(createSimpleEvent(1111L, 33));
+
+    pullSuccess = true;
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    EXPECT_TRUE(puller.Pull(&dataHolder));
+    EXPECT_EQ(1, dataHolder.size());
+    EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+    EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
+    EXPECT_EQ(1, dataHolder[0]->size());
+    EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
+
+    pullData.clear();
+    pullData.push_back(createSimpleEvent(2222L, 44));
+
+    pullSuccess = true;
+
+    dataHolder.clear();
+    EXPECT_TRUE(puller.Pull(&dataHolder));
+    EXPECT_EQ(1, dataHolder.size());
+    EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+    EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
+    EXPECT_EQ(1, dataHolder[0]->size());
+    EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
+}
+
+TEST_F(StatsPullerTest, PullFailsAndTooFast) {
+    pullData.push_back(createSimpleEvent(1111L, 33));
+
+    pullSuccess = false;
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    EXPECT_FALSE(puller.Pull(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+
+    pullData.clear();
+    pullData.push_back(createSimpleEvent(2222L, 44));
+
+    pullSuccess = true;
+
+    EXPECT_FALSE(puller.Pull(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+}
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/tests/external/puller_util_test.cpp b/cmds/statsd/tests/external/puller_util_test.cpp
index 81590a2..f21954f2 100644
--- a/cmds/statsd/tests/external/puller_util_test.cpp
+++ b/cmds/statsd/tests/external/puller_util_test.cpp
@@ -13,12 +13,16 @@
 // limitations under the License.
 
 #include "external/puller_util.h"
+
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <stdio.h>
+
 #include <vector>
-#include "statslog.h"
+
 #include "../metrics/metrics_test_helper.h"
+#include "stats_event.h"
+#include "statslog.h"
 
 #ifdef __ANDROID__
 
@@ -58,212 +62,187 @@
     ret.push_back(vec);
   }
 }
+
+std::shared_ptr<LogEvent> makeUidLogEvent(uint64_t timestampNs, int uid, int data1, int data2) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, uidAtomTagId);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+    AStatsEvent_writeInt32(statsEvent, uid);
+    AStatsEvent_writeInt32(statsEvent, data1);
+    AStatsEvent_writeInt32(statsEvent, data2);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+
+    std::shared_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+    return logEvent;
+}
+
+std::shared_ptr<LogEvent> makeNonUidAtomLogEvent(uint64_t timestampNs, int data1) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, nonUidAtomTagId);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+    AStatsEvent_writeInt32(statsEvent, data1);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+
+    std::shared_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+    return logEvent;
+}
+
 }  // anonymous namespace
 
-// TODO(b/149590301): Update these tests to use new socket schema.
-//TEST(puller_util, MergeNoDimension) {
-//  vector<shared_ptr<LogEvent>> inputData;
-//  shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
-//  // 30->22->31
-//  event->write(isolatedUid);
-//  event->write(hostNonAdditiveData);
-//  event->write(isolatedAdditiveData);
-//  event->init();
-//  inputData.push_back(event);
-//
-//  // 20->22->21
-//  event = make_shared<LogEvent>(uidAtomTagId, timestamp);
-//  event->write(hostUid);
-//  event->write(hostNonAdditiveData);
-//  event->write(hostAdditiveData);
-//  event->init();
-//  inputData.push_back(event);
-//
-//  sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
-//  EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid))
-//      .WillRepeatedly(Return(hostUid));
-//  EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
-//      .WillRepeatedly(ReturnArg<0>());
-//  mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields);
-//
-//  vector<vector<int>> actual;
-//  extractIntoVector(inputData, actual);
-//  vector<int> expectedV1 = {20, 22, 52};
-//  EXPECT_EQ(1, (int)actual.size());
-//  EXPECT_THAT(actual, Contains(expectedV1));
-//}
-//
-//TEST(puller_util, MergeWithDimension) {
-//  vector<shared_ptr<LogEvent>> inputData;
-//  shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
-//  // 30->32->31
-//  event->write(isolatedUid);
-//  event->write(isolatedNonAdditiveData);
-//  event->write(isolatedAdditiveData);
-//  event->init();
-//  inputData.push_back(event);
-//
-//  // 20->32->21
-//  event = make_shared<LogEvent>(uidAtomTagId, timestamp);
-//  event->write(hostUid);
-//  event->write(isolatedNonAdditiveData);
-//  event->write(hostAdditiveData);
-//  event->init();
-//  inputData.push_back(event);
-//
-//  // 20->22->21
-//  event = make_shared<LogEvent>(uidAtomTagId, timestamp);
-//  event->write(hostUid);
-//  event->write(hostNonAdditiveData);
-//  event->write(hostAdditiveData);
-//  event->init();
-//  inputData.push_back(event);
-//
-//  sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
-//  EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid))
-//      .WillRepeatedly(Return(hostUid));
-//  EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
-//      .WillRepeatedly(ReturnArg<0>());
-//  mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields);
-//
-//  vector<vector<int>> actual;
-//  extractIntoVector(inputData, actual);
-//  vector<int> expectedV1 = {20, 22, 21};
-//  vector<int> expectedV2 = {20, 32, 52};
-//  EXPECT_EQ(2, (int)actual.size());
-//  EXPECT_THAT(actual, Contains(expectedV1));
-//  EXPECT_THAT(actual, Contains(expectedV2));
-//}
-//
-//TEST(puller_util, NoMergeHostUidOnly) {
-//  vector<shared_ptr<LogEvent>> inputData;
-//  shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
-//  // 20->32->31
-//  event->write(hostUid);
-//  event->write(isolatedNonAdditiveData);
-//  event->write(isolatedAdditiveData);
-//  event->init();
-//  inputData.push_back(event);
-//
-//  // 20->22->21
-//  event = make_shared<LogEvent>(uidAtomTagId, timestamp);
-//  event->write(hostUid);
-//  event->write(hostNonAdditiveData);
-//  event->write(hostAdditiveData);
-//  event->init();
-//  inputData.push_back(event);
-//
-//  sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
-//  EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid))
-//      .WillRepeatedly(Return(hostUid));
-//  EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
-//      .WillRepeatedly(ReturnArg<0>());
-//  mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields);
-//
-//  // 20->32->31
-//  // 20->22->21
-//  vector<vector<int>> actual;
-//  extractIntoVector(inputData, actual);
-//  vector<int> expectedV1 = {20, 32, 31};
-//  vector<int> expectedV2 = {20, 22, 21};
-//  EXPECT_EQ(2, (int)actual.size());
-//  EXPECT_THAT(actual, Contains(expectedV1));
-//  EXPECT_THAT(actual, Contains(expectedV2));
-//}
-//
-//TEST(puller_util, IsolatedUidOnly) {
-//  vector<shared_ptr<LogEvent>> inputData;
-//  shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
-//  // 30->32->31
-//  event->write(hostUid);
-//  event->write(isolatedNonAdditiveData);
-//  event->write(isolatedAdditiveData);
-//  event->init();
-//  inputData.push_back(event);
-//
-//  // 30->22->21
-//  event = make_shared<LogEvent>(uidAtomTagId, timestamp);
-//  event->write(hostUid);
-//  event->write(hostNonAdditiveData);
-//  event->write(hostAdditiveData);
-//  event->init();
-//  inputData.push_back(event);
-//
-//  sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
-//  EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid))
-//      .WillRepeatedly(Return(hostUid));
-//  EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
-//      .WillRepeatedly(ReturnArg<0>());
-//  mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields);
-//
-//  // 20->32->31
-//  // 20->22->21
-//  vector<vector<int>> actual;
-//  extractIntoVector(inputData, actual);
-//  vector<int> expectedV1 = {20, 32, 31};
-//  vector<int> expectedV2 = {20, 22, 21};
-//  EXPECT_EQ(2, (int)actual.size());
-//  EXPECT_THAT(actual, Contains(expectedV1));
-//  EXPECT_THAT(actual, Contains(expectedV2));
-//}
-//
-//TEST(puller_util, MultipleIsolatedUidToOneHostUid) {
-//  vector<shared_ptr<LogEvent>> inputData;
-//  shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
-//  // 30->32->31
-//  event->write(isolatedUid);
-//  event->write(isolatedNonAdditiveData);
-//  event->write(isolatedAdditiveData);
-//  event->init();
-//  inputData.push_back(event);
-//
-//  // 31->32->21
-//  event = make_shared<LogEvent>(uidAtomTagId, timestamp);
-//  event->write(isolatedUid + 1);
-//  event->write(isolatedNonAdditiveData);
-//  event->write(hostAdditiveData);
-//  event->init();
-//  inputData.push_back(event);
-//
-//  // 20->32->21
-//  event = make_shared<LogEvent>(uidAtomTagId, timestamp);
-//  event->write(hostUid);
-//  event->write(isolatedNonAdditiveData);
-//  event->write(hostAdditiveData);
-//  event->init();
-//  inputData.push_back(event);
-//
-//  sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
-//  EXPECT_CALL(*uidMap, getHostUidOrSelf(_)).WillRepeatedly(Return(hostUid));
-//  mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields);
-//
-//  vector<vector<int>> actual;
-//  extractIntoVector(inputData, actual);
-//  vector<int> expectedV1 = {20, 32, 73};
-//  EXPECT_EQ(1, (int)actual.size());
-//  EXPECT_THAT(actual, Contains(expectedV1));
-//}
-//
-//TEST(puller_util, NoNeedToMerge) {
-//  vector<shared_ptr<LogEvent>> inputData;
-//  shared_ptr<LogEvent> event =
-//      make_shared<LogEvent>(nonUidAtomTagId, timestamp);
-//  // 32
-//  event->write(isolatedNonAdditiveData);
-//  event->init();
-//  inputData.push_back(event);
-//
-//  event = make_shared<LogEvent>(nonUidAtomTagId, timestamp);
-//  // 22
-//  event->write(hostNonAdditiveData);
-//  event->init();
-//  inputData.push_back(event);
-//
-//  sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
-//  mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, nonUidAtomTagId, {} /*no additive fields*/);
-//
-//  EXPECT_EQ(2, (int)inputData.size());
-//}
+TEST(puller_util, MergeNoDimension) {
+    vector<shared_ptr<LogEvent>> inputData;
+
+    // 30->22->31
+    inputData.push_back(
+            makeUidLogEvent(timestamp, isolatedUid, hostNonAdditiveData, isolatedAdditiveData));
+
+    // 20->22->21
+    inputData.push_back(makeUidLogEvent(timestamp, hostUid, hostNonAdditiveData, hostAdditiveData));
+
+    sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+    EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid));
+    EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))).WillRepeatedly(ReturnArg<0>());
+    mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields);
+
+    vector<vector<int>> actual;
+    extractIntoVector(inputData, actual);
+    vector<int> expectedV1 = {20, 22, 52};
+    EXPECT_EQ(1, (int)actual.size());
+    EXPECT_THAT(actual, Contains(expectedV1));
+}
+
+TEST(puller_util, MergeWithDimension) {
+    vector<shared_ptr<LogEvent>> inputData;
+
+    // 30->32->31
+    inputData.push_back(
+            makeUidLogEvent(timestamp, isolatedUid, isolatedNonAdditiveData, isolatedAdditiveData));
+
+    // 20->32->21
+    inputData.push_back(
+            makeUidLogEvent(timestamp, hostUid, isolatedNonAdditiveData, hostAdditiveData));
+
+    // 20->22->21
+    inputData.push_back(makeUidLogEvent(timestamp, hostUid, hostNonAdditiveData, hostAdditiveData));
+
+    sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+    EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid));
+    EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))).WillRepeatedly(ReturnArg<0>());
+    mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields);
+
+    vector<vector<int>> actual;
+    extractIntoVector(inputData, actual);
+    vector<int> expectedV1 = {20, 22, 21};
+    vector<int> expectedV2 = {20, 32, 52};
+    EXPECT_EQ(2, (int)actual.size());
+    EXPECT_THAT(actual, Contains(expectedV1));
+    EXPECT_THAT(actual, Contains(expectedV2));
+}
+
+TEST(puller_util, NoMergeHostUidOnly) {
+    vector<shared_ptr<LogEvent>> inputData;
+
+    // 20->32->31
+    inputData.push_back(
+            makeUidLogEvent(timestamp, hostUid, isolatedNonAdditiveData, isolatedAdditiveData));
+
+    // 20->22->21
+    inputData.push_back(makeUidLogEvent(timestamp, hostUid, hostNonAdditiveData, hostAdditiveData));
+
+    sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+    EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid));
+    EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))).WillRepeatedly(ReturnArg<0>());
+    mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields);
+
+    // 20->32->31
+    // 20->22->21
+    vector<vector<int>> actual;
+    extractIntoVector(inputData, actual);
+    vector<int> expectedV1 = {20, 32, 31};
+    vector<int> expectedV2 = {20, 22, 21};
+    EXPECT_EQ(2, (int)actual.size());
+    EXPECT_THAT(actual, Contains(expectedV1));
+    EXPECT_THAT(actual, Contains(expectedV2));
+}
+
+TEST(puller_util, IsolatedUidOnly) {
+    vector<shared_ptr<LogEvent>> inputData;
+
+    // 30->32->31
+    inputData.push_back(
+            makeUidLogEvent(timestamp, hostUid, isolatedNonAdditiveData, isolatedAdditiveData));
+
+    // 30->22->21
+    inputData.push_back(makeUidLogEvent(timestamp, hostUid, hostNonAdditiveData, hostAdditiveData));
+
+    sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+    EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid));
+    EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))).WillRepeatedly(ReturnArg<0>());
+    mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields);
+
+    // 20->32->31
+    // 20->22->21
+    vector<vector<int>> actual;
+    extractIntoVector(inputData, actual);
+    vector<int> expectedV1 = {20, 32, 31};
+    vector<int> expectedV2 = {20, 22, 21};
+    EXPECT_EQ(2, (int)actual.size());
+    EXPECT_THAT(actual, Contains(expectedV1));
+    EXPECT_THAT(actual, Contains(expectedV2));
+}
+
+TEST(puller_util, MultipleIsolatedUidToOneHostUid) {
+    vector<shared_ptr<LogEvent>> inputData;
+
+    // 30->32->31
+    inputData.push_back(
+            makeUidLogEvent(timestamp, isolatedUid, isolatedNonAdditiveData, isolatedAdditiveData));
+
+    // 31->32->21
+    inputData.push_back(
+            makeUidLogEvent(timestamp, isolatedUid + 1, isolatedNonAdditiveData, hostAdditiveData));
+
+    // 20->32->21
+    inputData.push_back(
+            makeUidLogEvent(timestamp, hostUid, isolatedNonAdditiveData, hostAdditiveData));
+
+    sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+    EXPECT_CALL(*uidMap, getHostUidOrSelf(_)).WillRepeatedly(Return(hostUid));
+    mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields);
+
+    vector<vector<int>> actual;
+    extractIntoVector(inputData, actual);
+    vector<int> expectedV1 = {20, 32, 73};
+    EXPECT_EQ(1, (int)actual.size());
+    EXPECT_THAT(actual, Contains(expectedV1));
+}
+
+TEST(puller_util, NoNeedToMerge) {
+    vector<shared_ptr<LogEvent>> inputData;
+
+    // 32
+    inputData.push_back(makeNonUidAtomLogEvent(timestamp, isolatedNonAdditiveData));
+
+    // 22
+    inputData.push_back(makeNonUidAtomLogEvent(timestamp, hostNonAdditiveData));
+
+    sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+    mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, nonUidAtomTagId, {} /*no additive fields*/);
+
+    EXPECT_EQ(2, (int)inputData.size());
+}
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/tests/log_event/LogEventQueue_test.cpp b/cmds/statsd/tests/log_event/LogEventQueue_test.cpp
index c4407f4..6dc041f 100644
--- a/cmds/statsd/tests/log_event/LogEventQueue_test.cpp
+++ b/cmds/statsd/tests/log_event/LogEventQueue_test.cpp
@@ -42,7 +42,7 @@
     size_t size;
     uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
 
-    std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/-1, /*pid=*/-1);
+    std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
     logEvent->parseBuffer(buf, size);
     AStatsEvent_release(statsEvent);
     return logEvent;
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index b882678..d55996c 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -13,16 +13,19 @@
 // limitations under the License.
 
 #include "src/metrics/CountMetricProducer.h"
-#include "src/stats_log_util.h"
-#include "metrics_test_helper.h"
-#include "tests/statsd_test_util.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <math.h>
 #include <stdio.h>
+
 #include <vector>
 
+#include "metrics_test_helper.h"
+#include "src/stats_log_util.h"
+#include "stats_event.h"
+#include "tests/statsd_test_util.h"
+
 using namespace testing;
 using android::sp;
 using std::set;
@@ -37,366 +40,392 @@
 
 const ConfigKey kConfigKey(0, 12345);
 
-// TODO(b/149590301): Update these tests to use new socket schema.
-//TEST(CountMetricProducerTest, TestFirstBucket) {
-//    CountMetric metric;
-//    metric.set_id(1);
-//    metric.set_bucket(ONE_MINUTE);
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//
-//    CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 5,
-//                                      600 * NS_PER_SEC + NS_PER_SEC / 2);
-//    EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs);
-//    EXPECT_EQ(10, countProducer.mCurrentBucketNum);
-//    EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs());
-//}
-//
-//TEST(CountMetricProducerTest, TestNonDimensionalEvents) {
-//    int64_t bucketStartTimeNs = 10000000000;
-//    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
-//    int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
-//    int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
-//    int tagId = 1;
-//
-//    CountMetric metric;
-//    metric.set_id(1);
-//    metric.set_bucket(ONE_MINUTE);
-//
-//    LogEvent event1(tagId, bucketStartTimeNs + 1);
-//    event1.init();
-//    LogEvent event2(tagId, bucketStartTimeNs + 2);
-//    event2.init();
-//
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//
-//    CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-//                                      bucketStartTimeNs, bucketStartTimeNs);
-//
-//    // 2 events in bucket 1.
-//    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
-//    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
-//
-//    // Flushes at event #2.
-//    countProducer.flushIfNeededLocked(bucketStartTimeNs + 2);
-//    EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
-//
-//    // Flushes.
-//    countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
-//    EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
-//    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
-//                countProducer.mPastBuckets.end());
-//    const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
-//    EXPECT_EQ(1UL, buckets.size());
-//    EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
-//    EXPECT_EQ(2LL, buckets[0].mCount);
-//
-//    // 1 matched event happens in bucket 2.
-//    LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 2);
-//    event3.init();
-//
-//    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
-//    countProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
-//    EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
-//    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
-//                countProducer.mPastBuckets.end());
-//    EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-//    const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1];
-//    EXPECT_EQ(bucket2StartTimeNs, bucketInfo2.mBucketStartNs);
-//    EXPECT_EQ(bucket2StartTimeNs + bucketSizeNs, bucketInfo2.mBucketEndNs);
-//    EXPECT_EQ(1LL, bucketInfo2.mCount);
-//
-//    // nothing happens in bucket 3. we should not record anything for bucket 3.
-//    countProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1);
-//    EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
-//    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
-//                countProducer.mPastBuckets.end());
-//    const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
-//    EXPECT_EQ(2UL, buckets3.size());
-//}
-//
-//TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) {
-//    int64_t bucketStartTimeNs = 10000000000;
-//    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
-//
-//    CountMetric metric;
-//    metric.set_id(1);
-//    metric.set_bucket(ONE_MINUTE);
-//    metric.set_condition(StringToId("SCREEN_ON"));
-//
-//    LogEvent event1(1, bucketStartTimeNs + 1);
-//    event1.init();
-//
-//    LogEvent event2(1, bucketStartTimeNs + 10);
-//    event2.init();
-//
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//
-//    CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs,
-//                                      bucketStartTimeNs);
-//
-//    countProducer.onConditionChanged(true, bucketStartTimeNs);
-//    countProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
-//    EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
-//
-//    countProducer.onConditionChanged(false /*new condition*/, bucketStartTimeNs + 2);
-//    // Upon this match event, the matched event1 is flushed.
-//    countProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
-//    EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
-//
-//    countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
-//    EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
-//    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
-//                countProducer.mPastBuckets.end());
-//    {
-//        const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
-//        EXPECT_EQ(1UL, buckets.size());
-//        const auto& bucketInfo = buckets[0];
-//        EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
-//        EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
-//        EXPECT_EQ(1LL, bucketInfo.mCount);
-//    }
-//}
-//
-//TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
-//    int64_t bucketStartTimeNs = 10000000000;
-//    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
-//
-//    int tagId = 1;
-//    int conditionTagId = 2;
-//
-//    CountMetric metric;
-//    metric.set_id(1);
-//    metric.set_bucket(ONE_MINUTE);
-//    metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON"));
-//    MetricConditionLink* link = metric.add_links();
-//    link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID"));
-//    buildSimpleAtomFieldMatcher(tagId, 1, link->mutable_fields_in_what());
-//    buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition());
-//
-//    LogEvent event1(tagId, bucketStartTimeNs + 1);
-//    event1.write("111");  // uid
-//    event1.init();
-//    ConditionKey key1;
-//    key1[StringToId("APP_IN_BACKGROUND_PER_UID")] =
-//        {getMockedDimensionKey(conditionTagId, 2, "111")};
-//
-//    LogEvent event2(tagId, bucketStartTimeNs + 10);
-//    event2.write("222");  // uid
-//    event2.init();
-//    ConditionKey key2;
-//    key2[StringToId("APP_IN_BACKGROUND_PER_UID")] =
-//        {getMockedDimensionKey(conditionTagId, 2, "222")};
-//
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//    EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse));
-//
-//    EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue));
-//
-//    CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard,
-//                                      bucketStartTimeNs, bucketStartTimeNs);
-//
-//    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
-//    countProducer.flushIfNeededLocked(bucketStartTimeNs + 1);
-//    EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
-//
-//    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
-//    countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
-//    EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
-//    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
-//                countProducer.mPastBuckets.end());
-//    const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
-//    EXPECT_EQ(1UL, buckets.size());
-//    const auto& bucketInfo = buckets[0];
-//    EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
-//    EXPECT_EQ(1LL, bucketInfo.mCount);
-//}
-//
-//TEST(CountMetricProducerTest, TestEventWithAppUpgrade) {
-//    sp<AlarmMonitor> alarmMonitor;
-//    int64_t bucketStartTimeNs = 10000000000;
-//    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
-//    int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
-//
-//    int tagId = 1;
-//    int conditionTagId = 2;
-//
-//    CountMetric metric;
-//    metric.set_id(1);
-//    metric.set_bucket(ONE_MINUTE);
-//    Alert alert;
-//    alert.set_num_buckets(3);
-//    alert.set_trigger_if_sum_gt(2);
-//    LogEvent event1(tagId, bucketStartTimeNs + 1);
-//    event1.write("111");  // uid
-//    event1.init();
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//    CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard,
-//                                      bucketStartTimeNs, bucketStartTimeNs);
-//
-//    sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
-//    EXPECT_TRUE(anomalyTracker != nullptr);
-//
-//    // Bucket is flushed yet.
-//    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
-//    EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
-//    EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
-//
-//    // App upgrade forces bucket flush.
-//    // Check that there's a past bucket and the bucket end is not adjusted.
-//    countProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
-//    EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-//    EXPECT_EQ((long long)bucketStartTimeNs,
-//              countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
-//    EXPECT_EQ((long long)eventUpgradeTimeNs,
-//              countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
-//    EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs);
-//    // Anomaly tracker only contains full buckets.
-//    EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
-//
-//    int64_t lastEndTimeNs = countProducer.getCurrentBucketEndTimeNs();
-//    // Next event occurs in same bucket as partial bucket created.
-//    LogEvent event2(tagId, bucketStartTimeNs + 59 * NS_PER_SEC + 10);
-//    event2.write("222");  // uid
-//    event2.init();
-//    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
-//    EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-//    EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs);
-//    EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
-//
-//    // Third event in following bucket.
-//    LogEvent event3(tagId, bucketStartTimeNs + 62 * NS_PER_SEC + 10);
-//    event3.write("333");  // uid
-//    event3.init();
-//    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
-//    EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-//    EXPECT_EQ(lastEndTimeNs, countProducer.mCurrentBucketStartTimeNs);
-//    EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
-//}
-//
-//TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket) {
-//    int64_t bucketStartTimeNs = 10000000000;
-//    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
-//    int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
-//
-//    int tagId = 1;
-//    int conditionTagId = 2;
-//
-//    CountMetric metric;
-//    metric.set_id(1);
-//    metric.set_bucket(ONE_MINUTE);
-//    LogEvent event1(tagId, bucketStartTimeNs + 1);
-//    event1.write("111");  // uid
-//    event1.init();
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//    CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard,
-//                                      bucketStartTimeNs, bucketStartTimeNs);
-//
-//    // Bucket is flushed yet.
-//    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
-//    EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
-//
-//    // App upgrade forces bucket flush.
-//    // Check that there's a past bucket and the bucket end is not adjusted.
-//    countProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
-//    EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-//    EXPECT_EQ((int64_t)bucketStartTimeNs,
-//              countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
-//              countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
-//    EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs);
-//
-//    // Next event occurs in same bucket as partial bucket created.
-//    LogEvent event2(tagId, bucketStartTimeNs + 70 * NS_PER_SEC + 10);
-//    event2.write("222");  // uid
-//    event2.init();
-//    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
-//    EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-//
-//    // Third event in following bucket.
-//    LogEvent event3(tagId, bucketStartTimeNs + 121 * NS_PER_SEC + 10);
-//    event3.write("333");  // uid
-//    event3.init();
-//    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
-//    EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-//    EXPECT_EQ((int64_t)eventUpgradeTimeNs,
-//              countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketStartNs);
-//    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
-//              countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketEndNs);
-//}
-//
-//TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) {
-//    sp<AlarmMonitor> alarmMonitor;
-//    Alert alert;
-//    alert.set_id(11);
-//    alert.set_metric_id(1);
-//    alert.set_trigger_if_sum_gt(2);
-//    alert.set_num_buckets(2);
-//    const int32_t refPeriodSec = 1;
-//    alert.set_refractory_period_secs(refPeriodSec);
-//
-//    int64_t bucketStartTimeNs = 10000000000;
-//    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
-//    int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
-//    int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
-//
-//    CountMetric metric;
-//    metric.set_id(1);
-//    metric.set_bucket(ONE_MINUTE);
-//
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//    CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-//                                      bucketStartTimeNs, bucketStartTimeNs);
-//
-//    sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
-//
-//    int tagId = 1;
-//    LogEvent event1(tagId, bucketStartTimeNs + 1);
-//    event1.init();
-//    LogEvent event2(tagId, bucketStartTimeNs + 2);
-//    event2.init();
-//    LogEvent event3(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 1);
-//    event3.init();
-//    LogEvent event4(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 1);
-//    event4.init();
-//    LogEvent event5(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2);
-//    event5.init();
-//    LogEvent event6(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 3);
-//    event6.init();
-//    LogEvent event7(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC);
-//    event7.init();
-//
-//    // Two events in bucket #0.
-//    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
-//    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
-//
-//    EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
-//    EXPECT_EQ(2L, countProducer.mCurrentSlicedCounter->begin()->second);
-//    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
-//
-//    // One event in bucket #2. No alarm as bucket #0 is trashed out.
-//    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
-//    EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
-//    EXPECT_EQ(1L, countProducer.mCurrentSlicedCounter->begin()->second);
-//    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
-//
-//    // Two events in bucket #3.
-//    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
-//    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event5);
-//    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event6);
-//    EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
-//    EXPECT_EQ(3L, countProducer.mCurrentSlicedCounter->begin()->second);
-//    // Anomaly at event 6 is within refractory period. The alarm is at event 5 timestamp not event 6
-//    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
-//            std::ceil(1.0 * event5.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
-//
-//    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7);
-//    EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
-//    EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second);
-//    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
-//            std::ceil(1.0 * event7.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
-//}
+namespace {
+
+void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+}
+
+void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId, string uid) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+    AStatsEvent_writeString(statsEvent, uid.c_str());
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+}
+
+}  // namespace
+
+TEST(CountMetricProducerTest, TestFirstBucket) {
+    CountMetric metric;
+    metric.set_id(1);
+    metric.set_bucket(ONE_MINUTE);
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 5,
+                                      600 * NS_PER_SEC + NS_PER_SEC / 2);
+    EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs);
+    EXPECT_EQ(10, countProducer.mCurrentBucketNum);
+    EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs());
+}
+
+TEST(CountMetricProducerTest, TestNonDimensionalEvents) {
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
+    int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+    int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
+    int tagId = 1;
+
+    CountMetric metric;
+    metric.set_id(1);
+    metric.set_bucket(ONE_MINUTE);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      bucketStartTimeNs, bucketStartTimeNs);
+
+    // 2 events in bucket 1.
+    LogEvent event1(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event1, bucketStartTimeNs + 1, tagId);
+    LogEvent event2(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event2, bucketStartTimeNs + 2, tagId);
+
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
+
+    // Flushes at event #2.
+    countProducer.flushIfNeededLocked(bucketStartTimeNs + 2);
+    EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
+
+    // Flushes.
+    countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
+    EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
+    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
+                countProducer.mPastBuckets.end());
+    const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
+    EXPECT_EQ(1UL, buckets.size());
+    EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
+    EXPECT_EQ(2LL, buckets[0].mCount);
+
+    // 1 matched event happens in bucket 2.
+    LogEvent event3(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event3, bucketStartTimeNs + bucketSizeNs + 2, tagId);
+
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
+
+    countProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
+    EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
+    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
+                countProducer.mPastBuckets.end());
+    EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+    const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1];
+    EXPECT_EQ(bucket2StartTimeNs, bucketInfo2.mBucketStartNs);
+    EXPECT_EQ(bucket2StartTimeNs + bucketSizeNs, bucketInfo2.mBucketEndNs);
+    EXPECT_EQ(1LL, bucketInfo2.mCount);
+
+    // nothing happens in bucket 3. we should not record anything for bucket 3.
+    countProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1);
+    EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
+    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
+                countProducer.mPastBuckets.end());
+    const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
+    EXPECT_EQ(2UL, buckets3.size());
+}
+
+TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) {
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
+
+    CountMetric metric;
+    metric.set_id(1);
+    metric.set_bucket(ONE_MINUTE);
+    metric.set_condition(StringToId("SCREEN_ON"));
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs,
+                                      bucketStartTimeNs);
+
+    countProducer.onConditionChanged(true, bucketStartTimeNs);
+
+    LogEvent event1(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event1, bucketStartTimeNs + 1, /*atomId=*/1);
+    countProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
+
+    EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
+
+    countProducer.onConditionChanged(false /*new condition*/, bucketStartTimeNs + 2);
+
+    // Upon this match event, the matched event1 is flushed.
+    LogEvent event2(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event2, bucketStartTimeNs + 10, /*atomId=*/1);
+    countProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
+    EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
+
+    countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
+    EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
+    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
+                countProducer.mPastBuckets.end());
+
+    const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
+    EXPECT_EQ(1UL, buckets.size());
+    const auto& bucketInfo = buckets[0];
+    EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
+    EXPECT_EQ(1LL, bucketInfo.mCount);
+}
+
+TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
+
+    int tagId = 1;
+    int conditionTagId = 2;
+
+    CountMetric metric;
+    metric.set_id(1);
+    metric.set_bucket(ONE_MINUTE);
+    metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON"));
+    MetricConditionLink* link = metric.add_links();
+    link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID"));
+    buildSimpleAtomFieldMatcher(tagId, 1, link->mutable_fields_in_what());
+    buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition());
+
+    LogEvent event1(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111");
+
+    LogEvent event2(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event2, bucketStartTimeNs + 10, tagId, /*uid=*/"222");
+
+    ConditionKey key1;
+    key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = {
+            getMockedDimensionKey(conditionTagId, 2, "111")};
+
+    ConditionKey key2;
+    key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = {
+            getMockedDimensionKey(conditionTagId, 2, "222")};
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse));
+
+    EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue));
+
+    CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard,
+                                      bucketStartTimeNs, bucketStartTimeNs);
+
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+    countProducer.flushIfNeededLocked(bucketStartTimeNs + 1);
+    EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
+
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
+    countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
+    EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
+    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
+                countProducer.mPastBuckets.end());
+    const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
+    EXPECT_EQ(1UL, buckets.size());
+    const auto& bucketInfo = buckets[0];
+    EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
+    EXPECT_EQ(1LL, bucketInfo.mCount);
+}
+
+TEST(CountMetricProducerTest, TestEventWithAppUpgrade) {
+    sp<AlarmMonitor> alarmMonitor;
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
+    int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
+
+    int tagId = 1;
+    int conditionTagId = 2;
+
+    CountMetric metric;
+    metric.set_id(1);
+    metric.set_bucket(ONE_MINUTE);
+    Alert alert;
+    alert.set_num_buckets(3);
+    alert.set_trigger_if_sum_gt(2);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard,
+                                      bucketStartTimeNs, bucketStartTimeNs);
+
+    sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
+    EXPECT_TRUE(anomalyTracker != nullptr);
+
+    // Bucket is flushed yet.
+    LogEvent event1(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111");
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+    EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
+    EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
+
+    // App upgrade forces bucket flush.
+    // Check that there's a past bucket and the bucket end is not adjusted.
+    countProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+    EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+    EXPECT_EQ((long long)bucketStartTimeNs,
+              countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
+    EXPECT_EQ((long long)eventUpgradeTimeNs,
+              countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
+    EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs);
+    // Anomaly tracker only contains full buckets.
+    EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
+
+    int64_t lastEndTimeNs = countProducer.getCurrentBucketEndTimeNs();
+    // Next event occurs in same bucket as partial bucket created.
+    LogEvent event2(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event2, bucketStartTimeNs + 59 * NS_PER_SEC + 10, tagId, /*uid=*/"222");
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
+    EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+    EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs);
+    EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
+
+    // Third event in following bucket.
+    LogEvent event3(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event3, bucketStartTimeNs + 62 * NS_PER_SEC + 10, tagId, /*uid=*/"333");
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
+    EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+    EXPECT_EQ(lastEndTimeNs, countProducer.mCurrentBucketStartTimeNs);
+    EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
+}
+
+TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket) {
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
+    int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
+
+    int tagId = 1;
+    int conditionTagId = 2;
+
+    CountMetric metric;
+    metric.set_id(1);
+    metric.set_bucket(ONE_MINUTE);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard,
+                                      bucketStartTimeNs, bucketStartTimeNs);
+
+    // Bucket is flushed yet.
+    LogEvent event1(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111");
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+    EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
+
+    // App upgrade forces bucket flush.
+    // Check that there's a past bucket and the bucket end is not adjusted.
+    countProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+    EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+    EXPECT_EQ((int64_t)bucketStartTimeNs,
+              countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
+              countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
+    EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs);
+
+    // Next event occurs in same bucket as partial bucket created.
+    LogEvent event2(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event2, bucketStartTimeNs + 70 * NS_PER_SEC + 10, tagId, /*uid=*/"222");
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
+    EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+
+    // Third event in following bucket.
+    LogEvent event3(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event3, bucketStartTimeNs + 121 * NS_PER_SEC + 10, tagId, /*uid=*/"333");
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
+    EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+    EXPECT_EQ((int64_t)eventUpgradeTimeNs,
+              countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketStartNs);
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+              countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketEndNs);
+}
+
+TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) {
+    sp<AlarmMonitor> alarmMonitor;
+    Alert alert;
+    alert.set_id(11);
+    alert.set_metric_id(1);
+    alert.set_trigger_if_sum_gt(2);
+    alert.set_num_buckets(2);
+    const int32_t refPeriodSec = 1;
+    alert.set_refractory_period_secs(refPeriodSec);
+
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
+    int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+    int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
+
+    CountMetric metric;
+    metric.set_id(1);
+    metric.set_bucket(ONE_MINUTE);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      bucketStartTimeNs, bucketStartTimeNs);
+
+    sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
+
+    int tagId = 1;
+    LogEvent event1(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event1, bucketStartTimeNs + 1, tagId);
+    LogEvent event2(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event2, bucketStartTimeNs + 2, tagId);
+    LogEvent event3(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event3, bucketStartTimeNs + 2 * bucketSizeNs + 1, tagId);
+    LogEvent event4(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event4, bucketStartTimeNs + 3 * bucketSizeNs + 1, tagId);
+    LogEvent event5(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event5, bucketStartTimeNs + 3 * bucketSizeNs + 2, tagId);
+    LogEvent event6(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event6, bucketStartTimeNs + 3 * bucketSizeNs + 3, tagId);
+    LogEvent event7(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event7, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC, tagId);
+
+    // Two events in bucket #0.
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
+
+    EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
+    EXPECT_EQ(2L, countProducer.mCurrentSlicedCounter->begin()->second);
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
+
+    // One event in bucket #2. No alarm as bucket #0 is trashed out.
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
+    EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
+    EXPECT_EQ(1L, countProducer.mCurrentSlicedCounter->begin()->second);
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
+
+    // Two events in bucket #3.
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event5);
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event6);
+    EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
+    EXPECT_EQ(3L, countProducer.mCurrentSlicedCounter->begin()->second);
+    // Anomaly at event 6 is within refractory period. The alarm is at event 5 timestamp not event 6
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
+              std::ceil(1.0 * event5.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
+
+    countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7);
+    EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
+    EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second);
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
+              std::ceil(1.0 * event7.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
+}
 
 TEST(CountMetricProducerTest, TestOneWeekTimeUnit) {
     CountMetric metric;
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
index 6661374..6143dc0 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -13,17 +13,20 @@
 // limitations under the License.
 
 #include "src/metrics/DurationMetricProducer.h"
-#include "src/stats_log_util.h"
-#include "metrics_test_helper.h"
-#include "src/condition/ConditionWizard.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <stdio.h>
+
 #include <set>
 #include <unordered_map>
 #include <vector>
 
+#include "metrics_test_helper.h"
+#include "src/condition/ConditionWizard.h"
+#include "src/stats_log_util.h"
+#include "stats_event.h"
+
 using namespace android::os::statsd;
 using namespace testing;
 using android::sp;
@@ -39,6 +42,22 @@
 
 const ConfigKey kConfigKey(0, 12345);
 
+namespace {
+
+void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+}
+
+}  // namespace
+
 TEST(DurationMetricTrackerTest, TestFirstBucket) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     DurationMetric metric;
@@ -56,383 +75,386 @@
     EXPECT_EQ(660000000005, durationProducer.getCurrentBucketEndTimeNs());
 }
 
-// TODO(b/149590301): Update these to use new socket schema.
-//TEST(DurationMetricTrackerTest, TestNoCondition) {
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//    int64_t bucketStartTimeNs = 10000000000;
-//    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
-//
-//    DurationMetric metric;
-//    metric.set_id(1);
-//    metric.set_bucket(ONE_MINUTE);
-//    metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
-//
-//    int tagId = 1;
-//    LogEvent event1(tagId, bucketStartTimeNs + 1);
-//    event1.init();
-//    LogEvent event2(tagId, bucketStartTimeNs + bucketSizeNs + 2);
-//    event2.init();
-//
-//    FieldMatcher dimensions;
-//    DurationMetricProducer durationProducer(
-//            kConfigKey, metric, -1 /*no condition*/, 1 /* start index */, 2 /* stop index */,
-//            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
-//
-//    durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
-//    durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
-//    durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
-//    EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
-//    EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
-//                durationProducer.mPastBuckets.end());
-//    const auto& buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
-//    EXPECT_EQ(2UL, buckets.size());
-//    EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
-//    EXPECT_EQ(bucketSizeNs - 1LL, buckets[0].mDuration);
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs);
-//    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[1].mBucketEndNs);
-//    EXPECT_EQ(2LL, buckets[1].mDuration);
-//}
-//
-//TEST(DurationMetricTrackerTest, TestNonSlicedCondition) {
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//    int64_t bucketStartTimeNs = 10000000000;
-//    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
-//
-//    DurationMetric metric;
-//    metric.set_id(1);
-//    metric.set_bucket(ONE_MINUTE);
-//    metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
-//
-//    int tagId = 1;
-//    LogEvent event1(tagId, bucketStartTimeNs + 1);
-//    event1.init();
-//    LogEvent event2(tagId, bucketStartTimeNs + 2);
-//    event2.init();
-//    LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 1);
-//    event3.init();
-//    LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3);
-//    event4.init();
-//
-//    FieldMatcher dimensions;
-//    DurationMetricProducer durationProducer(
-//            kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */,
-//            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
-//    durationProducer.mCondition = ConditionState::kFalse;
-//
-//    EXPECT_FALSE(durationProducer.mCondition);
-//    EXPECT_FALSE(durationProducer.isConditionSliced());
-//
-//    durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
-//    durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
-//    durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
-//    EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
-//
-//    durationProducer.onMatchedLogEvent(1 /* start index*/, event3);
-//    durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2);
-//    durationProducer.onMatchedLogEvent(2 /* stop index*/, event4);
-//    durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
-//    EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
-//    EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
-//                durationProducer.mPastBuckets.end());
-//    const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
-//    EXPECT_EQ(1UL, buckets2.size());
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs);
-//    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs);
-//    EXPECT_EQ(1LL, buckets2[0].mDuration);
-//}
-//
-//TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) {
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//    int64_t bucketStartTimeNs = 10000000000;
-//    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
-//
-//    DurationMetric metric;
-//    metric.set_id(1);
-//    metric.set_bucket(ONE_MINUTE);
-//    metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
-//
-//    int tagId = 1;
-//    LogEvent event1(tagId, bucketStartTimeNs + 1);
-//    event1.init();
-//    LogEvent event2(tagId, bucketStartTimeNs + 2);
-//    event2.init();
-//    LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 1);
-//    event3.init();
-//    LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3);
-//    event4.init();
-//
-//    FieldMatcher dimensions;
-//    DurationMetricProducer durationProducer(
-//            kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */,
-//            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
-//
-//    EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition);
-//    EXPECT_FALSE(durationProducer.isConditionSliced());
-//
-//    durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
-//    durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
-//    durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
-//    EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
-//
-//    durationProducer.onMatchedLogEvent(1 /* start index*/, event3);
-//    durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2);
-//    durationProducer.onMatchedLogEvent(2 /* stop index*/, event4);
-//    durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
-//    EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
-//    const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
-//    EXPECT_EQ(1UL, buckets2.size());
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs);
-//    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs);
-//    EXPECT_EQ(1LL, buckets2[0].mDuration);
-//}
-//
-//TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) {
-//    /**
-//     * The duration starts from the first bucket, through the two partial buckets (10-70sec),
-//     * another bucket, and ends at the beginning of the next full bucket.
-//     * Expected buckets:
-//     *  - [10,25]: 14 secs
-//     *  - [25,70]: All 45 secs
-//     *  - [70,130]: All 60 secs
-//     *  - [130, 210]: Only 5 secs (event ended at 135sec)
-//     */
-//    int64_t bucketStartTimeNs = 10000000000;
-//    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
-//    int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
-//    int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
-//    int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
-//
-//    int tagId = 1;
-//
-//    DurationMetric metric;
-//    metric.set_id(1);
-//    metric.set_bucket(ONE_MINUTE);
-//    metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//    FieldMatcher dimensions;
-//    DurationMetricProducer durationProducer(
-//            kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
-//            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
-//
-//    LogEvent start_event(tagId, startTimeNs);
-//    start_event.init();
-//    durationProducer.onMatchedLogEvent(1 /* start index*/, start_event);
-//    EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
-//    EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
-//
-//    durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
-//    EXPECT_EQ(1UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-//    std::vector<DurationBucket> buckets =
-//            durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
-//    EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
-//    EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketEndNs);
-//    EXPECT_EQ(eventUpgradeTimeNs - startTimeNs, buckets[0].mDuration);
-//    EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
-//
-//    // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
-//    LogEvent end_event(tagId, endTimeNs);
-//    end_event.init();
-//    durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event);
-//    buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
-//    EXPECT_EQ(3UL, buckets.size());
-//    EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketStartNs);
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketEndNs);
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - eventUpgradeTimeNs, buckets[1].mDuration);
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[2].mBucketStartNs);
-//    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs);
-//    EXPECT_EQ(bucketSizeNs, buckets[2].mDuration);
-//}
-//
-//TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) {
-//    /**
-//     * Expected buckets (start at 11s, upgrade at 75s, end at 135s):
-//     *  - [10,70]: 59 secs
-//     *  - [70,75]: 5 sec
-//     *  - [75,130]: 55 secs
-//     */
-//    int64_t bucketStartTimeNs = 10000000000;
-//    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
-//    int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
-//    int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
-//    int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
-//
-//    int tagId = 1;
-//
-//    DurationMetric metric;
-//    metric.set_id(1);
-//    metric.set_bucket(ONE_MINUTE);
-//    metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//    FieldMatcher dimensions;
-//    DurationMetricProducer durationProducer(
-//            kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
-//            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
-//
-//    LogEvent start_event(tagId, startTimeNs);
-//    start_event.init();
-//    durationProducer.onMatchedLogEvent(1 /* start index*/, start_event);
-//    EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
-//    EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
-//
-//    durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
-//    EXPECT_EQ(2UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-//    std::vector<DurationBucket> buckets =
-//            durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
-//    EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, buckets[0].mDuration);
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs);
-//    EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketEndNs);
-//    EXPECT_EQ(eventUpgradeTimeNs - (bucketStartTimeNs + bucketSizeNs), buckets[1].mDuration);
-//    EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
-//
-//    // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
-//    LogEvent end_event(tagId, endTimeNs);
-//    end_event.init();
-//    durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event);
-//    buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
-//    EXPECT_EQ(3UL, buckets.size());
-//    EXPECT_EQ(eventUpgradeTimeNs, buckets[2].mBucketStartNs);
-//    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs);
-//    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs - eventUpgradeTimeNs, buckets[2].mDuration);
-//}
-//
-//TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) {
-//    sp<AlarmMonitor> alarmMonitor;
-//    int64_t bucketStartTimeNs = 10000000000;
-//    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
-//    int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
-//    int64_t startTimeNs = bucketStartTimeNs + 1;
-//    int64_t endTimeNs = startTimeNs + 65 * NS_PER_SEC;
-//
-//    int tagId = 1;
-//
-//    // Setup metric with alert.
-//    DurationMetric metric;
-//    metric.set_id(1);
-//    metric.set_bucket(ONE_MINUTE);
-//    metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
-//    Alert alert;
-//    alert.set_num_buckets(3);
-//    alert.set_trigger_if_sum_gt(2);
-//
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//    FieldMatcher dimensions;
-//    DurationMetricProducer durationProducer(
-//            kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
-//            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
-//
-//    sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor);
-//    EXPECT_TRUE(anomalyTracker != nullptr);
-//
-//    LogEvent start_event(tagId, startTimeNs);
-//    start_event.init();
-//    durationProducer.onMatchedLogEvent(1 /* start index*/, start_event);
-//    durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
-//    // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
-//    LogEvent end_event(tagId, endTimeNs);
-//    end_event.init();
-//    durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event);
-//
-//    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs,
-//              anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
-//}
-//
-//TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade) {
-//    int64_t bucketStartTimeNs = 10000000000;
-//    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
-//    int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
-//    int64_t startTimeNs = bucketStartTimeNs + 1;
-//    int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
-//
-//    int tagId = 1;
-//
-//    DurationMetric metric;
-//    metric.set_id(1);
-//    metric.set_bucket(ONE_MINUTE);
-//    metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
-//    LogEvent event1(tagId, startTimeNs);
-//    event1.write("111");  // uid
-//    event1.init();
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//    FieldMatcher dimensions;
-//    DurationMetricProducer durationProducer(
-//            kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
-//            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
-//
-//    LogEvent start_event(tagId, startTimeNs);
-//    start_event.init();
-//    durationProducer.onMatchedLogEvent(1 /* start index*/, start_event);
-//    EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
-//    EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
-//
-//    durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
-//    EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-//    EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
-//
-//    // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
-//    LogEvent end_event(tagId, endTimeNs);
-//    end_event.init();
-//    durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event);
-//    EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-//
-//    durationProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1);
-//    std::vector<DurationBucket> buckets =
-//            durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
-//    EXPECT_EQ(1UL, buckets.size());
-//    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketStartNs);
-//    EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, buckets[0].mBucketEndNs);
-//    EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration);
-//}
-//
-//TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket) {
-//    int64_t bucketStartTimeNs = 10000000000;
-//    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
-//    int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
-//    int64_t startTimeNs = bucketStartTimeNs + 1;
-//    int64_t endTimeNs = startTimeNs + 115 * NS_PER_SEC;
-//
-//    int tagId = 1;
-//
-//    DurationMetric metric;
-//    metric.set_id(1);
-//    metric.set_bucket(ONE_MINUTE);
-//    metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
-//    LogEvent event1(tagId, startTimeNs);
-//    event1.write("111");  // uid
-//    event1.init();
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//    FieldMatcher dimensions;
-//    DurationMetricProducer durationProducer(
-//            kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
-//            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
-//
-//    LogEvent start_event(tagId, startTimeNs);
-//    start_event.init();
-//    durationProducer.onMatchedLogEvent(1 /* start index*/, start_event);
-//    EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
-//    EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
-//
-//    durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
-//    EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-//    EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
-//
-//    // Stop occurs in the same partial bucket as created for the app upgrade.
-//    LogEvent end_event(tagId, endTimeNs);
-//    end_event.init();
-//    durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event);
-//    EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-//    EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
-//
-//    durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
-//    std::vector<DurationBucket> buckets =
-//            durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
-//    EXPECT_EQ(1UL, buckets.size());
-//    EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketStartNs);
-//    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketEndNs);
-//    EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration);
-//}
+TEST(DurationMetricTrackerTest, TestNoCondition) {
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
+
+    DurationMetric metric;
+    metric.set_id(1);
+    metric.set_bucket(ONE_MINUTE);
+    metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
+
+    int tagId = 1;
+    LogEvent event1(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event1, bucketStartTimeNs + 1, tagId);
+    LogEvent event2(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event2, bucketStartTimeNs + bucketSizeNs + 2, tagId);
+
+    FieldMatcher dimensions;
+    DurationMetricProducer durationProducer(kConfigKey, metric, -1 /*no condition*/,
+                                            1 /* start index */, 2 /* stop index */,
+                                            3 /* stop_all index */, false /*nesting*/, wizard,
+                                            dimensions, bucketStartTimeNs, bucketStartTimeNs);
+
+    durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
+    durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
+    durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
+    EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
+    EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
+                durationProducer.mPastBuckets.end());
+    const auto& buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
+    EXPECT_EQ(2UL, buckets.size());
+    EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
+    EXPECT_EQ(bucketSizeNs - 1LL, buckets[0].mDuration);
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs);
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[1].mBucketEndNs);
+    EXPECT_EQ(2LL, buckets[1].mDuration);
+}
+
+TEST(DurationMetricTrackerTest, TestNonSlicedCondition) {
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
+
+    DurationMetric metric;
+    metric.set_id(1);
+    metric.set_bucket(ONE_MINUTE);
+    metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
+
+    int tagId = 1;
+    LogEvent event1(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event1, bucketStartTimeNs + 1, tagId);
+    LogEvent event2(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event2, bucketStartTimeNs + 2, tagId);
+    LogEvent event3(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event3, bucketStartTimeNs + bucketSizeNs + 1, tagId);
+    LogEvent event4(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event4, bucketStartTimeNs + bucketSizeNs + 3, tagId);
+
+    FieldMatcher dimensions;
+    DurationMetricProducer durationProducer(kConfigKey, metric, 0 /* condition index */,
+                                            1 /* start index */, 2 /* stop index */,
+                                            3 /* stop_all index */, false /*nesting*/, wizard,
+                                            dimensions, bucketStartTimeNs, bucketStartTimeNs);
+    durationProducer.mCondition = ConditionState::kFalse;
+
+    EXPECT_FALSE(durationProducer.mCondition);
+    EXPECT_FALSE(durationProducer.isConditionSliced());
+
+    durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
+    durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
+    durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
+    EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
+
+    durationProducer.onMatchedLogEvent(1 /* start index*/, event3);
+    durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2);
+    durationProducer.onMatchedLogEvent(2 /* stop index*/, event4);
+    durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
+    EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
+    EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
+                durationProducer.mPastBuckets.end());
+    const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
+    EXPECT_EQ(1UL, buckets2.size());
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs);
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs);
+    EXPECT_EQ(1LL, buckets2[0].mDuration);
+}
+
+TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) {
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
+
+    DurationMetric metric;
+    metric.set_id(1);
+    metric.set_bucket(ONE_MINUTE);
+    metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
+
+    int tagId = 1;
+    LogEvent event1(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event1, bucketStartTimeNs + 1, tagId);
+    LogEvent event2(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event2, bucketStartTimeNs + 2, tagId);
+    LogEvent event3(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event3, bucketStartTimeNs + bucketSizeNs + 1, tagId);
+    LogEvent event4(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event4, bucketStartTimeNs + bucketSizeNs + 3, tagId);
+
+    FieldMatcher dimensions;
+    DurationMetricProducer durationProducer(kConfigKey, metric, 0 /* condition index */,
+                                            1 /* start index */, 2 /* stop index */,
+                                            3 /* stop_all index */, false /*nesting*/, wizard,
+                                            dimensions, bucketStartTimeNs, bucketStartTimeNs);
+
+    EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition);
+    EXPECT_FALSE(durationProducer.isConditionSliced());
+
+    durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
+    durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
+    durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
+    EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
+
+    durationProducer.onMatchedLogEvent(1 /* start index*/, event3);
+    durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2);
+    durationProducer.onMatchedLogEvent(2 /* stop index*/, event4);
+    durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
+    EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
+    const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
+    EXPECT_EQ(1UL, buckets2.size());
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs);
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs);
+    EXPECT_EQ(1LL, buckets2[0].mDuration);
+}
+
+TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) {
+    /**
+     * The duration starts from the first bucket, through the two partial buckets (10-70sec),
+     * another bucket, and ends at the beginning of the next full bucket.
+     * Expected buckets:
+     *  - [10,25]: 14 secs
+     *  - [25,70]: All 45 secs
+     *  - [70,130]: All 60 secs
+     *  - [130, 210]: Only 5 secs (event ended at 135sec)
+     */
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
+    int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
+    int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
+    int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
+
+    int tagId = 1;
+    LogEvent event1(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event1, startTimeNs, tagId);
+    LogEvent event2(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event2, endTimeNs, tagId);
+
+    DurationMetric metric;
+    metric.set_id(1);
+    metric.set_bucket(ONE_MINUTE);
+    metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    FieldMatcher dimensions;
+    DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */,
+                                            1 /* start index */, 2 /* stop index */,
+                                            3 /* stop_all index */, false /*nesting*/, wizard,
+                                            dimensions, bucketStartTimeNs, bucketStartTimeNs);
+
+    durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
+    EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
+    EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+
+    durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+    EXPECT_EQ(1UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+    std::vector<DurationBucket> buckets =
+            durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
+    EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
+    EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketEndNs);
+    EXPECT_EQ(eventUpgradeTimeNs - startTimeNs, buckets[0].mDuration);
+    EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+
+    // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
+    durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
+    buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
+    EXPECT_EQ(3UL, buckets.size());
+    EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketStartNs);
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketEndNs);
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - eventUpgradeTimeNs, buckets[1].mDuration);
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[2].mBucketStartNs);
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs);
+    EXPECT_EQ(bucketSizeNs, buckets[2].mDuration);
+}
+
+TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) {
+    /**
+     * Expected buckets (start at 11s, upgrade at 75s, end at 135s):
+     *  - [10,70]: 59 secs
+     *  - [70,75]: 5 sec
+     *  - [75,130]: 55 secs
+     */
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
+    int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
+    int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
+    int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
+
+    int tagId = 1;
+    LogEvent event1(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event1, startTimeNs, tagId);
+    LogEvent event2(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event2, endTimeNs, tagId);
+
+    DurationMetric metric;
+    metric.set_id(1);
+    metric.set_bucket(ONE_MINUTE);
+    metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    FieldMatcher dimensions;
+    DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */,
+                                            1 /* start index */, 2 /* stop index */,
+                                            3 /* stop_all index */, false /*nesting*/, wizard,
+                                            dimensions, bucketStartTimeNs, bucketStartTimeNs);
+
+    durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
+    EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
+    EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+
+    durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+    EXPECT_EQ(2UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+    std::vector<DurationBucket> buckets =
+            durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
+    EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, buckets[0].mDuration);
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs);
+    EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketEndNs);
+    EXPECT_EQ(eventUpgradeTimeNs - (bucketStartTimeNs + bucketSizeNs), buckets[1].mDuration);
+    EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+
+    // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
+    durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
+    buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
+    EXPECT_EQ(3UL, buckets.size());
+    EXPECT_EQ(eventUpgradeTimeNs, buckets[2].mBucketStartNs);
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs);
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs - eventUpgradeTimeNs, buckets[2].mDuration);
+}
+
+TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) {
+    sp<AlarmMonitor> alarmMonitor;
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
+    int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
+    int64_t startTimeNs = bucketStartTimeNs + 1;
+    int64_t endTimeNs = startTimeNs + 65 * NS_PER_SEC;
+
+    int tagId = 1;
+    LogEvent event1(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event1, startTimeNs, tagId);
+    LogEvent event2(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event2, endTimeNs, tagId);
+
+    // Setup metric with alert.
+    DurationMetric metric;
+    metric.set_id(1);
+    metric.set_bucket(ONE_MINUTE);
+    metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
+    Alert alert;
+    alert.set_num_buckets(3);
+    alert.set_trigger_if_sum_gt(2);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    FieldMatcher dimensions;
+    DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */,
+                                            1 /* start index */, 2 /* stop index */,
+                                            3 /* stop_all index */, false /*nesting*/, wizard,
+                                            dimensions, bucketStartTimeNs, bucketStartTimeNs);
+
+    sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor);
+    EXPECT_TRUE(anomalyTracker != nullptr);
+
+    durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
+    durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+
+    // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
+    durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs,
+              anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
+}
+
+TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade) {
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
+    int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
+    int64_t startTimeNs = bucketStartTimeNs + 1;
+    int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
+
+    int tagId = 1;
+    LogEvent event1(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event1, startTimeNs, tagId);
+    LogEvent event2(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event2, endTimeNs, tagId);
+
+    DurationMetric metric;
+    metric.set_id(1);
+    metric.set_bucket(ONE_MINUTE);
+    metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    FieldMatcher dimensions;
+    DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */,
+                                            1 /* start index */, 2 /* stop index */,
+                                            3 /* stop_all index */, false /*nesting*/, wizard,
+                                            dimensions, bucketStartTimeNs, bucketStartTimeNs);
+
+    durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
+    EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
+    EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+
+    durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+    EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+    EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+
+    // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
+    durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
+    EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+
+    durationProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1);
+    std::vector<DurationBucket> buckets =
+            durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
+    EXPECT_EQ(1UL, buckets.size());
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketStartNs);
+    EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, buckets[0].mBucketEndNs);
+    EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration);
+}
+
+TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket) {
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
+    int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
+    int64_t startTimeNs = bucketStartTimeNs + 1;
+    int64_t endTimeNs = startTimeNs + 115 * NS_PER_SEC;
+
+    int tagId = 1;
+    LogEvent event1(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event1, startTimeNs, tagId);
+    LogEvent event2(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event2, endTimeNs, tagId);
+
+    DurationMetric metric;
+    metric.set_id(1);
+    metric.set_bucket(ONE_MINUTE);
+    metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    FieldMatcher dimensions;
+    DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */,
+                                            1 /* start index */, 2 /* stop index */,
+                                            3 /* stop_all index */, false /*nesting*/, wizard,
+                                            dimensions, bucketStartTimeNs, bucketStartTimeNs);
+
+    durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
+    EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
+    EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+
+    durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+    EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+    EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+
+    // Stop occurs in the same partial bucket as created for the app upgrade.
+    durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
+    EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+    EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+
+    durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
+    std::vector<DurationBucket> buckets =
+            durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
+    EXPECT_EQ(1UL, buckets.size());
+    EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketStartNs);
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketEndNs);
+    EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration);
+}
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index d416f13..e2eee03 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -410,40 +410,75 @@
     return dimensions;
 }
 
-// TODO(b/149590301): Update these helpers to use new socket schema.
-//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);
-//    EXPECT_TRUE(event->write(state));
-//    event->init();
-//    return event;
-//}
-//
-//std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs) {
-//    auto event = std::make_unique<LogEvent>(
-//        android::util::BATTERY_SAVER_MODE_STATE_CHANGED, timestampNs);
-//    EXPECT_TRUE(event->write(BatterySaverModeStateChanged::ON));
-//    event->init();
-//    return event;
-//}
-//
-//std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs) {
-//    auto event = std::make_unique<LogEvent>(
-//        android::util::BATTERY_SAVER_MODE_STATE_CHANGED, timestampNs);
-//    EXPECT_TRUE(event->write(BatterySaverModeStateChanged::OFF));
-//    event->init();
-//    return event;
-//}
-//
-//std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(
-//    int level, uint64_t timestampNs) {
-//    auto event = std::make_unique<LogEvent>(android::util::SCREEN_BRIGHTNESS_CHANGED, timestampNs);
-//    EXPECT_TRUE(event->write(level));
-//    event->init();
-//    return event;
-//
-//}
-//
+std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
+        uint64_t timestampNs, const android::view::DisplayStateEnum state) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, android::util::SCREEN_STATE_CHANGED);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+    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> CreateBatterySaverOnEvent(uint64_t timestampNs) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, android::util::BATTERY_SAVER_MODE_STATE_CHANGED);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+    AStatsEvent_writeInt32(statsEvent, BatterySaverModeStateChanged::ON);
+    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> CreateBatterySaverOffEvent(uint64_t timestampNs) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, android::util::BATTERY_SAVER_MODE_STATE_CHANGED);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+    AStatsEvent_writeInt32(statsEvent, BatterySaverModeStateChanged::OFF);
+    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> CreateScreenBrightnessChangedEvent(uint64_t timestampNs, int level) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, android::util::SCREEN_BRIGHTNESS_CHANGED);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+    AStatsEvent_writeInt32(statsEvent, level);
+    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) {
@@ -470,121 +505,212 @@
 //            attributions, name, 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> CreateAcquireWakelockEvent(
-//        const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
-//        uint64_t timestampNs) {
-//    return CreateWakelockStateChangedEvent(
-//        attributions, wakelockName, WakelockStateChanged::ACQUIRE, timestampNs);
-//}
-//
-//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> CreateProcessLifeCycleStateChangedEvent(
-//    const int uid, const ProcessLifeCycleStateChanged::State state, uint64_t timestampNs) {
-//    auto logEvent = std::make_unique<LogEvent>(
-//        android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, timestampNs);
-//    logEvent->write(uid);
-//    logEvent->write("");
-//    logEvent->write(state);
-//    logEvent->init();
-//    return logEvent;
-//}
-//
-//std::unique_ptr<LogEvent> CreateAppCrashEvent(const int uid, uint64_t timestampNs) {
-//    return CreateProcessLifeCycleStateChangedEvent(
-//        uid, ProcessLifeCycleStateChanged::CRASHED, timestampNs);
-//}
-//
-//std::unique_ptr<LogEvent> CreateAppCrashOccurredEvent(const int uid, uint64_t timestampNs) {
-//    auto event = std::make_unique<LogEvent>(android::util::APP_CRASH_OCCURRED, timestampNs);
-//    event->write(uid);
-//    event->write("eventType");
-//    event->write("processName");
-//    event->init();
-//    return event;
-//}
-//
-//std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent(
-//    int isolatedUid, int hostUid, bool is_create, uint64_t timestampNs) {
-//    auto logEvent = std::make_unique<LogEvent>(
-//        android::util::ISOLATED_UID_CHANGED, timestampNs);
-//    logEvent->write(hostUid);
-//    logEvent->write(isolatedUid);
-//    logEvent->write(is_create);
-//    logEvent->init();
-//    return logEvent;
-//}
-//
-//std::unique_ptr<LogEvent> CreateUidProcessStateChangedEvent(
-//        int uid, const android::app::ProcessStateEnum state, uint64_t timestampNs) {
-//    auto event = std::make_unique<LogEvent>(android::util::UID_PROCESS_STATE_CHANGED, timestampNs);
-//    event->write(uid);
-//    event->write(state);
-//    event->init();
-//    return event;
-//}
+std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(uint64_t timestampNs,
+                                                          const vector<int>& attributionUids,
+                                                          const vector<string>& attributionTags,
+                                                          const string& wakelockName,
+                                                          const WakelockStateChanged::State state) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, android::util::WAKELOCK_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, android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK);
+    AStatsEvent_writeString(statsEvent, wakelockName.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(uint64_t timestampNs,
+                                                     const vector<int>& attributionUids,
+                                                     const vector<string>& attributionTags,
+                                                     const string& wakelockName) {
+    return CreateWakelockStateChangedEvent(timestampNs, attributionUids, attributionTags,
+                                           wakelockName, WakelockStateChanged::ACQUIRE);
+}
+
+std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(uint64_t timestampNs,
+                                                     const vector<int>& attributionUids,
+                                                     const vector<string>& attributionTags,
+                                                     const string& wakelockName) {
+    return CreateWakelockStateChangedEvent(timestampNs, attributionUids, attributionTags,
+                                           wakelockName, WakelockStateChanged::RELEASE);
+}
+
+std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent(
+        uint64_t timestampNs, const int uid, const ActivityForegroundStateChanged::State state) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+    AStatsEvent_writeInt32(statsEvent, uid);
+    AStatsEvent_writeString(statsEvent, "pkg_name");
+    AStatsEvent_writeString(statsEvent, "class_name");
+    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> CreateMoveToBackgroundEvent(uint64_t timestampNs, const int uid) {
+    return CreateActivityForegroundStateChangedEvent(timestampNs, uid,
+                                                     ActivityForegroundStateChanged::BACKGROUND);
+}
+
+std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(uint64_t timestampNs, const int uid) {
+    return CreateActivityForegroundStateChangedEvent(timestampNs, uid,
+                                                     ActivityForegroundStateChanged::FOREGROUND);
+}
+
+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, android::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> 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> CreateSyncEndEvent(uint64_t timestampNs,
+                                             const vector<int>& attributionUids,
+                                             const vector<string>& attributionTags,
+                                             const string& name) {
+    return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name,
+                                       SyncStateChanged::OFF);
+}
+
+std::unique_ptr<LogEvent> CreateProcessLifeCycleStateChangedEvent(
+        uint64_t timestampNs, const int uid, const ProcessLifeCycleStateChanged::State state) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+    AStatsEvent_writeInt32(statsEvent, uid);
+    AStatsEvent_writeString(statsEvent, "");
+    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> CreateAppCrashEvent(uint64_t timestampNs, const int uid) {
+    return CreateProcessLifeCycleStateChangedEvent(timestampNs, uid,
+                                                   ProcessLifeCycleStateChanged::CRASHED);
+}
+
+std::unique_ptr<LogEvent> CreateAppCrashOccurredEvent(uint64_t timestampNs, const int uid) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, android::util::APP_CRASH_OCCURRED);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+    AStatsEvent_writeInt32(statsEvent, uid);
+    AStatsEvent_writeString(statsEvent, "eventType");
+    AStatsEvent_writeString(statsEvent, "processName");
+    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> CreateIsolatedUidChangedEvent(uint64_t timestampNs, int hostUid,
+                                                        int isolatedUid, bool is_create) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, android::util::ISOLATED_UID_CHANGED);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+    AStatsEvent_writeInt32(statsEvent, hostUid);
+    AStatsEvent_writeInt32(statsEvent, isolatedUid);
+    AStatsEvent_writeInt32(statsEvent, is_create);
+    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> CreateUidProcessStateChangedEvent(
+        uint64_t timestampNs, int uid, const android::app::ProcessStateEnum state) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, android::util::UID_PROCESS_STATE_CHANGED);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+    AStatsEvent_writeInt32(statsEvent, uid);
+    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,
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index c8326ee..4371015 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -166,11 +166,10 @@
 
 // Create log event for screen state changed.
 std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
-    const android::view::DisplayStateEnum state, uint64_t timestampNs);
+        uint64_t timestampNs, const android::view::DisplayStateEnum state);
 
 // Create log event for screen brightness state changed.
-std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(
-   int level, uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(uint64_t timestampNs, int level);
 
 // Create log event when scheduled job starts.
 std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(
@@ -188,45 +187,42 @@
 std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs);
 
 // Create log event for app moving to background.
-std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(uint64_t timestampNs, const int uid);
 
 // Create log event for app moving to foreground.
-std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(uint64_t timestampNs, const int uid);
 
 // 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>& uids,
+                                               const vector<string>& tags, 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);
+std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs, const vector<int>& uids,
+                                             const vector<string>& tags, const string& name);
 
 // Create log event when the app sync ends.
-std::unique_ptr<LogEvent> CreateAppCrashEvent(
-    const int uid, uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateAppCrashEvent(uint64_t timestampNs, const int uid);
 
 // Create log event for an app crash.
-std::unique_ptr<LogEvent> CreateAppCrashOccurredEvent(const int uid, uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateAppCrashOccurredEvent(uint64_t timestampNs, const int uid);
 
 // Create log event for acquiring wakelock.
-std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(
-        const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
-        uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(uint64_t timestampNs, const vector<int>& uids,
+                                                     const vector<string>& tags,
+                                                     const string& wakelockName);
 
 // Create log event for releasing wakelock.
-std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(
-        const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
-        uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(uint64_t timestampNs, const vector<int>& uids,
+                                                     const vector<string>& tags,
+                                                     const string& wakelockName);
 
 // 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> CreateIsolatedUidChangedEvent(uint64_t timestampNs, int hostUid,
+                                                        int isolatedUid, bool is_create);
 
 // Create log event for uid process state change.
 std::unique_ptr<LogEvent> CreateUidProcessStateChangedEvent(
-        int uid, const android::app::ProcessStateEnum state, uint64_t timestampNs);
+        uint64_t timestampNs, int uid, const android::app::ProcessStateEnum state);
 
 // Helper function to create an AttributionNodeInternal proto.
 AttributionNodeInternal CreateAttribution(const int& uid, const string& tag);
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 3a3eea9..e7036bb 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -48,6 +48,7 @@
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.KeyEvent;
+import android.view.SurfaceControl;
 import android.view.SurfaceView;
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
@@ -1937,8 +1938,8 @@
      * to declare the capability to take screenshot by setting the
      * {@link android.R.styleable#AccessibilityService_canTakeScreenshot}
      * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
-     * Besides, This API is only supported for default display now
-     * {@link Display#DEFAULT_DISPLAY}.
+     * This API only will support {@link Display#DEFAULT_DISPLAY} until {@link SurfaceControl}
+     * supports non-default displays.
      * </p>
      *
      * @param displayId The logic display id, must be {@link Display#DEFAULT_DISPLAY} for
@@ -1948,11 +1949,17 @@
      *
      * @return {@code true} if the taking screenshot accepted, {@code false} if too little time
      * has elapsed since the last screenshot, invalid display or internal errors.
+     * @throws IllegalArgumentException if displayId is not {@link Display#DEFAULT_DISPLAY}.
      */
     public boolean takeScreenshot(int displayId, @NonNull @CallbackExecutor Executor executor,
             @NonNull Consumer<ScreenshotResult> callback) {
         Preconditions.checkNotNull(executor, "executor cannot be null");
         Preconditions.checkNotNull(callback, "callback cannot be null");
+
+        if (displayId != Display.DEFAULT_DISPLAY) {
+            throw new IllegalArgumentException("DisplayId isn't the default display");
+        }
+
         final IAccessibilityServiceConnection connection =
                 AccessibilityInteractionClient.getInstance().getConnection(
                         mConnectionId);
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index ca37e9b..2c41e8d 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -18,6 +18,7 @@
 
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
@@ -269,6 +270,11 @@
     private float mDurationScale = -1f;
 
     /**
+     * Animation handler used to schedule updates for this animation.
+     */
+    private AnimationHandler mAnimationHandler;
+
+    /**
      * Public constants
      */
 
@@ -1684,6 +1690,15 @@
      * @hide
      */
     public AnimationHandler getAnimationHandler() {
-        return AnimationHandler.getInstance();
+        return mAnimationHandler != null ? mAnimationHandler : AnimationHandler.getInstance();
+    }
+
+    /**
+     * Sets the animation handler used to schedule updates for this animator or {@code null} to use
+     * the default handler.
+     * @hide
+     */
+    public void setAnimationHandler(@Nullable AnimationHandler animationHandler) {
+        mAnimationHandler = animationHandler;
     }
 }
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 8d6bc72..6480a6a 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5829,7 +5829,7 @@
                 intent.prepareToLeaveProcess(this);
                 result = ActivityTaskManager.getService()
                     .startActivity(mMainThread.getApplicationThread(), getBasePackageName(),
-                            getFeatureId(), intent,
+                            getAttributionTag(), intent,
                             intent.resolveTypeIfNeeded(getContentResolver()), mToken, mEmbeddedID,
                             requestCode, ActivityManager.START_FLAG_ONLY_IF_NEEDED, null, options);
             } catch (RemoteException e) {
@@ -6624,12 +6624,10 @@
         String packageName = getPackageName();
         try {
             data.prepareToLeaveProcess(this);
-            IIntentSender target =
-                ActivityManager.getService().getIntentSenderWithFeature(
-                        ActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName, getFeatureId(),
-                        mParent == null ? mToken : mParent.mToken,
-                        mEmbeddedID, requestCode, new Intent[] { data }, null, flags, null,
-                        getUserId());
+            IIntentSender target = ActivityManager.getService().getIntentSenderWithFeature(
+                    ActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName, getAttributionTag(),
+                    mParent == null ? mToken : mParent.mToken, mEmbeddedID, requestCode,
+                    new Intent[]{data}, null, flags, null, getUserId());
             return target != null ? new PendingIntent(target) : null;
         } catch (RemoteException e) {
             // Empty
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index dd4788e..1a92b75 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4243,6 +4243,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION)
     public boolean updateMccMncConfiguration(@NonNull String mcc, @NonNull String mnc) {
         if (mcc == null || mnc == null) {
diff --git a/core/java/android/app/AppOps.md b/core/java/android/app/AppOps.md
new file mode 100644
index 0000000..bee701a
--- /dev/null
+++ b/core/java/android/app/AppOps.md
@@ -0,0 +1,212 @@
+<!--
+  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
+  -->
+
+# App-ops
+
+App-ops are used for two purposes: Access control and tracking.
+
+App-ops cover a wide variety of functionality from helping with runtime permissions to battery
+consumption tracking.
+
+App-ops are defined in `AppOpsManager` as `OP_...` and need to be continuously numbered. The
+integer values of the app-ops are not exposed. For app-ops visible to 3rd party apps,
+the name of the app-op might be exposed as `OPSTR_`. As the integers are not part of the API, they
+might (and have) changed between platform versions and OEM implementations.
+`AppOpsManager.opToPublicName` and `AppOpsManager.strOpToOp` allow for conversion between integer
+and string identifier for the op.
+
+## App-ops as access restrictions
+
+App-ops can either be controlled for each [uid](../os/Users.md#int-uid) or for each package. Which
+one is used depends on the API provider maintaining this app-op.
+
+For any security or privacy related app-ops the provider needs to control the app-op per uid
+as all security and privacy is based on uid in Android.
+
+App-op used for non-security related tasks are usually controlled per package to provide finer
+granularity.
+
+### Setting the app-op mode
+
+To control access the app-op can be set to:
+
+`MODE_DEFAULT`
+: Default behavior, might differ from app-op to app-op
+
+`MODE_ALLOWED`
+: Allow the access
+
+`MODE_FOREGROUND`
+: Allow the access but only if the app is currently in the [foreground](#foreground)
+
+`MODE_IGNORED`
+: Don't allow the access, i.e. don't perform the requested action or return dummy data
+
+`MODE_ERRORED`
+: Throw a `SecurityException` on access. This can be suppressed by using a `...noThrow` method to
+check the mode
+
+The initial state of an app-op is defined in `AppOpsManager.sOpDefaultMode`. Confusingly the
+initial state is often not `MODE_DEFAULT`
+
+Per-package modes can be set using `AppOpsManager.setMode` and per-uid modes can be set using
+`AppOpsManager.setUidMode`.
+
+**Warning**: Do not use `setMode` and `setUidMode` for the same app-op. Due to the way the
+internal storage for the mode works this can lead to very confusing behavior. If this ever happened
+by accident this needs to be cleaned up for any affected user as the app-op mode is retained over
+reboot.
+
+App-ops can also be set via the shell using the `appops set` command. The target package/uid can be
+defined via parameters to this command.
+
+The current state of the app-op can be read via the `appops get` command or via `dumpsys appops`.
+If the app-op is not mentioned in the output the app-op is in it's initial state.
+
+For example `dumpsys appops`:
+```
+[...]
+  Uid 2000:
+    [...]
+      COARSE_LOCATION: mode=foreground
+      START_FOREGROUND: mode=foreground
+      LEGACY_STORAGE: mode=ignore
+    [...]
+```
+
+### Guarding access based on app-ops
+
+API providers need to check the mode returned by `AppOpsManager.noteOp` if they are are allowing
+access to operations gated by the app-op. `AppOpsManager.unsafeCheckOp` should be used to check the
+mode if no access is granted. E.g. this can be for displaying app-op state in the UI or when
+checking the state before later calling `noteOp` anyway.
+
+If an operation refers to a time span (e.g. a audio-recording session) the API provider should
+use `AppOpsManager.startOp` and `AppOpsManager.finishOp` instead of `noteOp`.
+
+`noteOp` and `startOp` take a `packageName` and `featureId` parameter. These need to be read from
+the calling apps context as `Context.getOpPackageName` and `Context.getFeatureId`, then send to
+the data provider and then passed on the `noteOp`/`startOp` method.
+
+#### App-ops and permissions
+
+Access guarding is often done in combination with permissions using [runtime permissions
+](../permission/Permissions.md#runtime-permissions-and-app-ops) or [app-op permissions
+](../permission/Permissions.md#app-op-permissions). This is preferred over just using an app-op
+ as permissions a concept more familiar to app developers.
+
+### Foreground
+
+The `AppOpsService` tracks the apps' proc state (== foreground-ness) by following the
+`ActivityManagerService`'s proc state. It reduces the possible proc states to only those needed
+for app-ops. It also delays the changes by a _settle time_. This delay is needed as the proc state
+can fluctuate when switching apps. By delaying the change the appops service is not affected by
+those.
+
+The proc state is used for two use cases: Firstly, Tracking remembers the proc state for each
+tracked event. Secondly, `noteOp`/`checkOp` calls for app-op that are set to `MODE_FOREGROUND` are
+translated using the `AppOpsService.UidState.evalMode` method into `MODE_ALLOWED` when the app is
+counted as foreground and `MODE_IGNORED` when the app is counted as background. `checkOpRaw`
+calls are not affected.
+
+The current proc state for an app can be read from `dumpsys appops`. The tracking information can
+be read from `dumpsys appops`
+
+```
+Uid u0a118:
+  state=fg
+  capability=6
+```
+
+## App-ops for tracking
+
+App-ops track many important events, including all accesses to runtime permission protected
+APIs. This is done by tracking when an app-op was noted or started. The tracked data can only be
+read by system components.
+
+**Note:** Only `noteOp`/`startOp` calls are tracked; `unsafeCheckOp` is not tracked. Hence it is
+important to eventually call `noteOp` or `startOp` when providing access to protected operations
+or data.
+
+Some apps are forwarding access to other apps. E.g. an app might get the location from the
+system's location provider and then send the location further to a 3rd app. In this case the
+app passing on the data needs to call `AppOpsManager.noteProxyOp` to signal the access proxying.
+This might also make sense inside of a single app if the access is forwarded between two features of
+the app. In this case an app-op is noted for the forwarding app (proxy) and the app that received
+the data (proxied). As any app can do it is important to track how much the system trusts this
+proxy-access-tracking. For more details see `AppOpService.noteProxyOperation`.
+
+The tracking information can be read from `dumpsys appops` split by feature, proc state and
+proxying information with the syntax
+
+```
+Package THE_PACKAGE_NAME:
+  AN_APP_OP (CURRENT_MODE):
+    FEATURE_ID (or null for default feature)=[
+      ACCESS_OR_REJECT: [PROC_STATE-PROXYING_TAG] TIME proxy[INFO_ABOUT_PROXY IF_PROXY_ACCESS]
+```
+
+Example:
+
+```
+Package com.google.android.gms:
+  READ_CONTACTS (allow):
+    null=[
+      Access: [fgsvc-s] 2020-02-14 14:24:10.559 (-3d23h15m43s642ms)
+      Access: [fgsvc-tp] 2020-02-14 14:23:58.189 (-3d23h15m56s12ms)
+    ]
+    apkappcontext=[
+      Access: [fg-tp] 2020-02-17 14:24:54.721 (-23h14m59s480ms)
+    ]
+    com.google.android.gms.icing=[
+      Access: [fgsvc-tpd] 2020-02-14 14:26:27.018 (-3d23h13m27s183ms) proxy[uid=10070, pkg=com.android.providers.contacts, feature=null]
+      Access: [fg-tpd] 2020-02-18 02:26:08.711 (-11h13m45s490ms) proxy[uid=10070, pkg=com.android.providers.contacts, feature=null]
+      Access: [bg-tpd] 2020-02-14 14:34:55.310 (-3d23h4m58s891ms) proxy[uid=10070, pkg=com.android.providers.contacts, feature=null]
+    ]
+  MANAGE_EXTERNAL_STORAGE (default):
+    null=[
+      Reject: [fg-s]2020-02-18 08:00:04.444 (-5h39m49s757ms)
+      Reject: [bg-s]2020-02-18 08:00:04.427 (-5h39m49s774ms)
+    ]
+```
+
+### Tracking an app's own private data accesses
+
+An app can register an `AppOpsManager.OnOpNotedCallback` to get informed about what accesses the
+system is tracking for it. As each runtime permission has an associated app-op this API is
+particularly useful for an app that want to find unexpected private data accesses.
+
+## Listening to app-op events
+
+System apps (with the appropriate permissions) can listen to most app-op events, such as
+
+`noteOp`
+: `startWatchingNoted`
+
+`startOp`/`finishOp`
+: `startWatchingActive`
+
+mode changes
+: `startWatchingMode`
+
+[foreground](#foreground)-ness changes
+: `startWatchingMode` using the `WATCH_FOREGROUND_CHANGES` flag
+
+Watching such events is only ever as good as the tracked events. E.g. if the audio provider does
+not call `startOp` for a audio-session, the app's activeness for the record-audio app-op is not
+changed. Further there were cases where app-ops were noted even though no data was accessed or
+operation was performed. Hence before relying on the data from app-ops, double check if the data
+is actually reliable.
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index eeb5d41..f6a79cd 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -27,6 +27,7 @@
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.app.usage.UsageStatsManager;
+import android.compat.Compatibility;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -159,8 +160,8 @@
  * <p>Some apps are forwarding access to other apps. E.g. an app might get the location from the
  * system's location provider and then send the location further to a 3rd app. In this case the
  * app passing on the data needs to call {@link #noteProxyOp} to signal the access proxying. This
- * might also make sense inside of a single app if the access is forwarded between two features of
- * the app.
+ * might also make sense inside of a single app if the access is forwarded between two parts of
+ * the tagged with different attribution tags.
  *
  * <p>An app can register an {@link OnOpNotedCallback} to get informed about what accesses the
  * system is tracking for it. As each runtime permission has an associated app-op this API is
@@ -385,6 +386,13 @@
      */
     public static final int WATCH_FOREGROUND_CHANGES = 1 << 0;
 
+    /**
+     * Flag for {@link #startWatchingMode} that causes the callback to happen on the switch-op
+     * instead the op the callback was registered. (This simulates pre-R behavior).
+     *
+     * @hide
+     */
+    public static final int CALL_BACK_ON_SWITCHED_OP = 1 << 1;
 
     /**
      * Flag to determine whether we should log noteOp/startOp calls to make sure they
@@ -1988,108 +1996,108 @@
     };
 
     /**
-     * This specifies whether each option should allow the system
-     * (and system ui) to bypass the user restriction when active.
+     * In which cases should an app be allowed to bypass the {@link #setUserRestriction user
+     * restriction} for a certain app-op.
      */
-    private static boolean[] sOpAllowSystemRestrictionBypass = new boolean[] {
-            true, //COARSE_LOCATION
-            true, //FINE_LOCATION
-            false, //GPS
-            false, //VIBRATE
-            false, //READ_CONTACTS
-            false, //WRITE_CONTACTS
-            false, //READ_CALL_LOG
-            false, //WRITE_CALL_LOG
-            false, //READ_CALENDAR
-            false, //WRITE_CALENDAR
-            true, //WIFI_SCAN
-            false, //POST_NOTIFICATION
-            false, //NEIGHBORING_CELLS
-            false, //CALL_PHONE
-            false, //READ_SMS
-            false, //WRITE_SMS
-            false, //RECEIVE_SMS
-            false, //RECEIVE_EMERGECY_SMS
-            false, //RECEIVE_MMS
-            false, //RECEIVE_WAP_PUSH
-            false, //SEND_SMS
-            false, //READ_ICC_SMS
-            false, //WRITE_ICC_SMS
-            false, //WRITE_SETTINGS
-            true, //SYSTEM_ALERT_WINDOW
-            false, //ACCESS_NOTIFICATIONS
-            false, //CAMERA
-            false, //RECORD_AUDIO
-            false, //PLAY_AUDIO
-            false, //READ_CLIPBOARD
-            false, //WRITE_CLIPBOARD
-            false, //TAKE_MEDIA_BUTTONS
-            false, //TAKE_AUDIO_FOCUS
-            false, //AUDIO_MASTER_VOLUME
-            false, //AUDIO_VOICE_VOLUME
-            false, //AUDIO_RING_VOLUME
-            false, //AUDIO_MEDIA_VOLUME
-            false, //AUDIO_ALARM_VOLUME
-            false, //AUDIO_NOTIFICATION_VOLUME
-            false, //AUDIO_BLUETOOTH_VOLUME
-            false, //WAKE_LOCK
-            false, //MONITOR_LOCATION
-            false, //MONITOR_HIGH_POWER_LOCATION
-            false, //GET_USAGE_STATS
-            false, //MUTE_MICROPHONE
-            true, //TOAST_WINDOW
-            false, //PROJECT_MEDIA
-            false, //ACTIVATE_VPN
-            false, //WALLPAPER
-            false, //ASSIST_STRUCTURE
-            false, //ASSIST_SCREENSHOT
-            false, //READ_PHONE_STATE
-            false, //ADD_VOICEMAIL
-            false, // USE_SIP
-            false, // PROCESS_OUTGOING_CALLS
-            false, // USE_FINGERPRINT
-            false, // BODY_SENSORS
-            false, // READ_CELL_BROADCASTS
-            false, // MOCK_LOCATION
-            false, // READ_EXTERNAL_STORAGE
-            false, // WRITE_EXTERNAL_STORAGE
-            false, // TURN_ON_SCREEN
-            false, // GET_ACCOUNTS
-            false, // RUN_IN_BACKGROUND
-            false, // AUDIO_ACCESSIBILITY_VOLUME
-            false, // READ_PHONE_NUMBERS
-            false, // REQUEST_INSTALL_PACKAGES
-            false, // ENTER_PICTURE_IN_PICTURE_ON_HIDE
-            false, // INSTANT_APP_START_FOREGROUND
-            false, // ANSWER_PHONE_CALLS
-            false, // OP_RUN_ANY_IN_BACKGROUND
-            false, // OP_CHANGE_WIFI_STATE
-            false, // OP_REQUEST_DELETE_PACKAGES
-            false, // OP_BIND_ACCESSIBILITY_SERVICE
-            false, // ACCEPT_HANDOVER
-            false, // MANAGE_IPSEC_HANDOVERS
-            false, // START_FOREGROUND
-            true, // BLUETOOTH_SCAN
-            false, // USE_BIOMETRIC
-            false, // ACTIVITY_RECOGNITION
-            false, // SMS_FINANCIAL_TRANSACTIONS
-            false, // READ_MEDIA_AUDIO
-            false, // WRITE_MEDIA_AUDIO
-            false, // READ_MEDIA_VIDEO
-            false, // WRITE_MEDIA_VIDEO
-            false, // READ_MEDIA_IMAGES
-            false, // WRITE_MEDIA_IMAGES
-            false, // LEGACY_STORAGE
-            false, // ACCESS_ACCESSIBILITY
-            false, // READ_DEVICE_IDENTIFIERS
-            false, // ACCESS_MEDIA_LOCATION
-            false, // QUERY_ALL_PACKAGES
-            false, // MANAGE_EXTERNAL_STORAGE
-            false, // INTERACT_ACROSS_PROFILES
-            false, // ACTIVATE_PLATFORM_VPN
-            false, // LOADER_USAGE_STATS
-            false, // ACCESS_CALL_AUDIO
-            false, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
+    private static RestrictionBypass[] sOpAllowSystemRestrictionBypass = new RestrictionBypass[] {
+            new RestrictionBypass(true, false), //COARSE_LOCATION
+            new RestrictionBypass(true, false), //FINE_LOCATION
+            null, //GPS
+            null, //VIBRATE
+            null, //READ_CONTACTS
+            null, //WRITE_CONTACTS
+            null, //READ_CALL_LOG
+            null, //WRITE_CALL_LOG
+            null, //READ_CALENDAR
+            null, //WRITE_CALENDAR
+            new RestrictionBypass(true, false), //WIFI_SCAN
+            null, //POST_NOTIFICATION
+            null, //NEIGHBORING_CELLS
+            null, //CALL_PHONE
+            null, //READ_SMS
+            null, //WRITE_SMS
+            null, //RECEIVE_SMS
+            null, //RECEIVE_EMERGECY_SMS
+            null, //RECEIVE_MMS
+            null, //RECEIVE_WAP_PUSH
+            null, //SEND_SMS
+            null, //READ_ICC_SMS
+            null, //WRITE_ICC_SMS
+            null, //WRITE_SETTINGS
+            new RestrictionBypass(true, false), //SYSTEM_ALERT_WINDOW
+            null, //ACCESS_NOTIFICATIONS
+            null, //CAMERA
+            new RestrictionBypass(false, true), //RECORD_AUDIO
+            null, //PLAY_AUDIO
+            null, //READ_CLIPBOARD
+            null, //WRITE_CLIPBOARD
+            null, //TAKE_MEDIA_BUTTONS
+            null, //TAKE_AUDIO_FOCUS
+            null, //AUDIO_MASTER_VOLUME
+            null, //AUDIO_VOICE_VOLUME
+            null, //AUDIO_RING_VOLUME
+            null, //AUDIO_MEDIA_VOLUME
+            null, //AUDIO_ALARM_VOLUME
+            null, //AUDIO_NOTIFICATION_VOLUME
+            null, //AUDIO_BLUETOOTH_VOLUME
+            null, //WAKE_LOCK
+            null, //MONITOR_LOCATION
+            null, //MONITOR_HIGH_POWER_LOCATION
+            null, //GET_USAGE_STATS
+            null, //MUTE_MICROPHONE
+            new RestrictionBypass(true, false), //TOAST_WINDOW
+            null, //PROJECT_MEDIA
+            null, //ACTIVATE_VPN
+            null, //WALLPAPER
+            null, //ASSIST_STRUCTURE
+            null, //ASSIST_SCREENSHOT
+            null, //READ_PHONE_STATE
+            null, //ADD_VOICEMAIL
+            null, // USE_SIP
+            null, // PROCESS_OUTGOING_CALLS
+            null, // USE_FINGERPRINT
+            null, // BODY_SENSORS
+            null, // READ_CELL_BROADCASTS
+            null, // MOCK_LOCATION
+            null, // READ_EXTERNAL_STORAGE
+            null, // WRITE_EXTERNAL_STORAGE
+            null, // TURN_ON_SCREEN
+            null, // GET_ACCOUNTS
+            null, // RUN_IN_BACKGROUND
+            null, // AUDIO_ACCESSIBILITY_VOLUME
+            null, // READ_PHONE_NUMBERS
+            null, // REQUEST_INSTALL_PACKAGES
+            null, // ENTER_PICTURE_IN_PICTURE_ON_HIDE
+            null, // INSTANT_APP_START_FOREGROUND
+            null, // ANSWER_PHONE_CALLS
+            null, // OP_RUN_ANY_IN_BACKGROUND
+            null, // OP_CHANGE_WIFI_STATE
+            null, // OP_REQUEST_DELETE_PACKAGES
+            null, // OP_BIND_ACCESSIBILITY_SERVICE
+            null, // ACCEPT_HANDOVER
+            null, // MANAGE_IPSEC_HANDOVERS
+            null, // START_FOREGROUND
+            new RestrictionBypass(true, false), // BLUETOOTH_SCAN
+            null, // USE_BIOMETRIC
+            null, // ACTIVITY_RECOGNITION
+            null, // SMS_FINANCIAL_TRANSACTIONS
+            null, // READ_MEDIA_AUDIO
+            null, // WRITE_MEDIA_AUDIO
+            null, // READ_MEDIA_VIDEO
+            null, // WRITE_MEDIA_VIDEO
+            null, // READ_MEDIA_IMAGES
+            null, // WRITE_MEDIA_IMAGES
+            null, // LEGACY_STORAGE
+            null, // ACCESS_ACCESSIBILITY
+            null, // READ_DEVICE_IDENTIFIERS
+            null, // ACCESS_MEDIA_LOCATION
+            null, // QUERY_ALL_PACKAGES
+            null, // MANAGE_EXTERNAL_STORAGE
+            null, // INTERACT_ACROSS_PROFILES
+            null, // ACTIVATE_PLATFORM_VPN
+            null, // LOADER_USAGE_STATS
+            null, // ACCESS_CALL_AUDIO
+            null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
     };
 
     /**
@@ -2485,11 +2493,11 @@
     }
 
     /**
-     * Retrieve whether the op allows the system (and system ui) to
-     * bypass the user restriction.
+     * Retrieve whether the op allows to bypass the user restriction.
+     *
      * @hide
      */
-    public static boolean opAllowSystemBypassRestriction(int op) {
+    public static RestrictionBypass opAllowSystemBypassRestriction(int op) {
         return sOpAllowSystemRestrictionBypass[op];
     }
 
@@ -2536,6 +2544,29 @@
     }
 
     /**
+     * When to not enforce {@link #setUserRestriction restrictions}.
+     *
+     * @hide
+     */
+    public static class RestrictionBypass {
+        /** Does the app need to be privileged to bypass the restriction */
+        public boolean isPrivileged;
+
+        /**
+         * Does the app need to have the EXEMPT_FROM_AUDIO_RESTRICTIONS permission to bypass the
+         * restriction
+         */
+        public boolean isRecordAudioRestrictionExcept;
+
+        public RestrictionBypass(boolean isPrivileged, boolean isRecordAudioRestrictionExcept) {
+            this.isPrivileged = isPrivileged;
+            this.isRecordAudioRestrictionExcept = isRecordAudioRestrictionExcept;
+        }
+
+        public static RestrictionBypass UNRESTRICTED = new RestrictionBypass(true, true);
+    }
+
+    /**
      * Class holding all of the operation information associated with an app.
      * @hide
      */
@@ -2627,23 +2658,23 @@
         private @IntRange(from = 0) int mUid;
         /** Package of the proxy that noted the op */
         private @Nullable String mPackageName;
-        /** ID of the feature of the proxy that noted the op */
-        private @Nullable String mFeatureId;
+        /** Attribution tag of the proxy that noted the op */
+        private @Nullable String mAttributionTag;
 
         /**
          * Reinit existing object with new state.
          *
          * @param uid UID of the proxy app that noted the op
          * @param packageName Package of the proxy that noted the op
-         * @param featureId ID of the feature of the proxy that noted the op
+         * @param attributionTag attribution tag of the proxy that noted the op
          *
          * @hide
          */
         public void reinit(@IntRange(from = 0) int uid, @Nullable String packageName,
-                @Nullable String featureId) {
+                @Nullable String attributionTag) {
             mUid = Preconditions.checkArgumentNonnegative(uid);
             mPackageName = packageName;
-            mFeatureId = featureId;
+            mAttributionTag = attributionTag;
         }
 
 
@@ -2668,21 +2699,21 @@
          *   UID of the proxy app that noted the op
          * @param packageName
          *   Package of the proxy that noted the op
-         * @param featureId
-         *   ID of the feature of the proxy that noted the op
+         * @param attributionTag
+         *   Attribution tag of the proxy that noted the op
          * @hide
          */
         @DataClass.Generated.Member
         public OpEventProxyInfo(
                 @IntRange(from = 0) int uid,
                 @Nullable String packageName,
-                @Nullable String featureId) {
+                @Nullable String attributionTag) {
             this.mUid = uid;
             com.android.internal.util.AnnotationValidations.validate(
                     IntRange.class, null, mUid,
                     "from", 0);
             this.mPackageName = packageName;
-            this.mFeatureId = featureId;
+            this.mAttributionTag = attributionTag;
 
             // onConstructed(); // You can define this method to get a callback
         }
@@ -2696,7 +2727,7 @@
         public OpEventProxyInfo(@NonNull OpEventProxyInfo orig) {
             mUid = orig.mUid;
             mPackageName = orig.mPackageName;
-            mFeatureId = orig.mFeatureId;
+            mAttributionTag = orig.mAttributionTag;
         }
 
         /**
@@ -2716,11 +2747,11 @@
         }
 
         /**
-         * ID of the feature of the proxy that noted the op
+         * Attribution tag of the proxy that noted the op
          */
         @DataClass.Generated.Member
-        public @Nullable String getFeatureId() {
-            return mFeatureId;
+        public @Nullable String getAttributionTag() {
+            return mAttributionTag;
         }
 
         @Override
@@ -2731,11 +2762,11 @@
 
             byte flg = 0;
             if (mPackageName != null) flg |= 0x2;
-            if (mFeatureId != null) flg |= 0x4;
+            if (mAttributionTag != null) flg |= 0x4;
             dest.writeByte(flg);
             dest.writeInt(mUid);
             if (mPackageName != null) dest.writeString(mPackageName);
-            if (mFeatureId != null) dest.writeString(mFeatureId);
+            if (mAttributionTag != null) dest.writeString(mAttributionTag);
         }
 
         @Override
@@ -2752,14 +2783,14 @@
             byte flg = in.readByte();
             int uid = in.readInt();
             String packageName = (flg & 0x2) == 0 ? null : in.readString();
-            String featureId = (flg & 0x4) == 0 ? null : in.readString();
+            String attributionTag = (flg & 0x4) == 0 ? null : in.readString();
 
             this.mUid = uid;
             com.android.internal.util.AnnotationValidations.validate(
                     IntRange.class, null, mUid,
                     "from", 0);
             this.mPackageName = packageName;
-            this.mFeatureId = featureId;
+            this.mAttributionTag = attributionTag;
 
             // onConstructed(); // You can define this method to get a callback
         }
@@ -2783,7 +2814,7 @@
                 time = 1576814974615L,
                 codegenVersion = "1.0.14",
                 sourceFile = "frameworks/base/core/java/android/app/AppOpsManager.java",
-                inputSignatures = "private @android.annotation.IntRange(from=0L) int mUid\nprivate @android.annotation.Nullable java.lang.String mPackageName\nprivate @android.annotation.Nullable java.lang.String mFeatureId\npublic  void reinit(int,java.lang.String,java.lang.String)\nclass OpEventProxyInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genHiddenCopyConstructor=true)")
+                inputSignatures = "private @android.annotation.IntRange(from=0L) int mUid\nprivate @android.annotation.Nullable java.lang.String mPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\npublic  void reinit(int,java.lang.String,java.lang.String)\nclass OpEventProxyInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genHiddenCopyConstructor=true)")
         @Deprecated
         private void __metadata() {}
         */
@@ -2982,7 +3013,7 @@
 
     /**
      * Last {@link #noteOp} and {@link #startOp} events performed for a single op and a specific
-     * {@link Context#createFeatureContext(String) feature} for all uidModes and opFlags.
+     * {@link Context#createAttributionContext(String) attribution} for all uidModes and opFlags.
      *
      * @hide
      */
@@ -2991,7 +3022,7 @@
     @Immutable
     // @DataClass(genHiddenConstructor = true) codegen verifier is broken
     @DataClass.Suppress({"getAccessEvents", "getRejectEvents", "getOp"})
-    public static final class OpFeatureEntry implements Parcelable {
+    public static final class AttributedOpEntry implements Parcelable {
         /** The code of the op */
         private final @IntRange(from = 0, to = _NUM_OP - 1) int mOp;
         /** Whether the op is running */
@@ -3290,8 +3321,8 @@
         }
 
         /**
-         * Gets the proxy info of the app that performed the last access on behalf of this feature
-         * and as a result blamed the op on this app.
+         * Gets the proxy info of the app that performed the last access on behalf of this
+         * attribution and as a result blamed the op on this attribution.
          *
          * @param flags The op flags
          *
@@ -3308,7 +3339,7 @@
 
         /**
          * Gets the proxy info of the app that performed the last foreground access on behalf of
-         * this feature and as a result blamed the op on this app.
+         * this attribution and as a result blamed the op on this attribution.
          *
          * @param flags The op flags
          *
@@ -3326,7 +3357,7 @@
 
         /**
          * Gets the proxy info of the app that performed the last background access on behalf of
-         * this feature and as a result blamed the op on this app.
+         * this attribution and as a result blamed the op on this attribution.
          *
          * @param flags The op flags
          *
@@ -3343,8 +3374,8 @@
         }
 
         /**
-         * Gets the proxy info of the app that performed the last access on behalf of this feature
-         * and as a result blamed the op on this app.
+         * Gets the proxy info of the app that performed the last access on behalf of this
+         * attribution and as a result blamed the op on this attribution.
          *
          * @param fromUidState The lowest UID state for which to query
          * @param toUidState The highest UID state for which to query (inclusive)
@@ -3419,7 +3450,7 @@
 
 
         /**
-         * Creates a new OpFeatureEntry.
+         * Creates a new OpAttributionEntry.
          *
          * @param op
          *   The code of the op
@@ -3432,7 +3463,7 @@
          * @hide
          */
         @DataClass.Generated.Member
-        public OpFeatureEntry(
+        public AttributedOpEntry(
                 @IntRange(from = 0, to = _NUM_OP - 1) int op,
                 boolean running,
                 @Nullable LongSparseArray<NoteOpEvent> accessEvents,
@@ -3502,7 +3533,7 @@
         /** @hide */
         @SuppressWarnings({"unchecked", "RedundantCast"})
         @DataClass.Generated.Member
-        /* package-private */ OpFeatureEntry(@NonNull Parcel in) {
+        /* package-private */ AttributedOpEntry(@NonNull Parcel in) {
             // You can override field unparcelling by defining methods like:
             // static FieldType unparcelFieldName(Parcel in) { ... }
 
@@ -3525,16 +3556,16 @@
         }
 
         @DataClass.Generated.Member
-        public static final @NonNull Parcelable.Creator<OpFeatureEntry> CREATOR
-                = new Parcelable.Creator<OpFeatureEntry>() {
+        public static final @NonNull Parcelable.Creator<AttributedOpEntry> CREATOR
+                = new Parcelable.Creator<AttributedOpEntry>() {
             @Override
-            public OpFeatureEntry[] newArray(int size) {
-                return new OpFeatureEntry[size];
+            public AttributedOpEntry[] newArray(int size) {
+                return new AttributedOpEntry[size];
             }
 
             @Override
-            public OpFeatureEntry createFromParcel(@NonNull Parcel in) {
-                return new OpFeatureEntry(in);
+            public AttributedOpEntry createFromParcel(@NonNull Parcel in) {
+                return new AttributedOpEntry(in);
             }
         };
 
@@ -3543,7 +3574,7 @@
                 time = 1574809856239L,
                 codegenVersion = "1.0.14",
                 sourceFile = "frameworks/base/core/java/android/app/AppOpsManager.java",
-                inputSignatures = "private final @android.annotation.IntRange(from=0L, to=_NUM_OP - 1) int mOp\nprivate final  boolean mRunning\nprivate final @com.android.internal.util.DataClass.ParcelWith(android.app.OpFeatureEntry.LongSparseArrayParceling.class) @android.annotation.Nullable android.util.LongSparseArray<android.app.NoteOpEvent> mAccessEvents\nprivate final @com.android.internal.util.DataClass.ParcelWith(android.app.OpFeatureEntry.LongSparseArrayParceling.class) @android.annotation.Nullable android.util.LongSparseArray<android.app.NoteOpEvent> mRejectEvents\npublic @android.annotation.NonNull android.util.ArraySet<java.lang.Long> collectKeys()\npublic @android.app.UidState int getLastAccessUidState(int)\npublic @android.app.UidState int getLastForegroundAccessUidState(int)\npublic @android.app.UidState int getLastBackgroundAccessUidState(int)\npublic @android.app.UidState int getLastRejectUidState(int)\npublic @android.app.UidState int getLastForegroundRejectUidState(int)\npublic @android.app.UidState int getLastBackgroundRejectUidState(int)\npublic  long getAccessTime(int,int)\npublic  long getRejectTime(int,int)\npublic  long getDuration(int,int)\npublic  int getProxyUid(int,int)\npublic @android.annotation.Nullable java.lang.String getProxyPackageName(int,int)\npublic @android.annotation.Nullable java.lang.String getProxyFeatureId(int,int)\nclass OpFeatureEntry extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
+                inputSignatures = "private final @android.annotation.IntRange(from=0L, to=_NUM_OP - 1) int mOp\nprivate final  boolean mRunning\nprivate final @com.android.internal.util.DataClass.ParcelWith(android.app.OpAttributionEntry.LongSparseArrayParceling.class) @android.annotation.Nullable android.util.LongSparseArray<android.app.NoteOpEvent> mAccessEvents\nprivate final @com.android.internal.util.DataClass.ParcelWith(android.app.OpAttributionEntry.LongSparseArrayParceling.class) @android.annotation.Nullable android.util.LongSparseArray<android.app.NoteOpEvent> mRejectEvents\npublic @android.annotation.NonNull android.util.ArraySet<java.lang.Long> collectKeys()\npublic @android.app.UidState int getLastAccessUidState(int)\npublic @android.app.UidState int getLastForegroundAccessUidState(int)\npublic @android.app.UidState int getLastBackgroundAccessUidState(int)\npublic @android.app.UidState int getLastRejectUidState(int)\npublic @android.app.UidState int getLastForegroundRejectUidState(int)\npublic @android.app.UidState int getLastBackgroundRejectUidState(int)\npublic  long getAccessTime(int,int)\npublic  long getRejectTime(int,int)\npublic  long getDuration(int,int)\npublic  int getProxyUid(int,int)\npublic @android.annotation.Nullable java.lang.String getProxyPackageName(int,int)\npublic @android.annotation.Nullable java.lang.String getProxyAttributionTag(int,int)\nclass OpAttributionEntry extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
         @Deprecated
         private void __metadata() {}
          */
@@ -3569,8 +3600,8 @@
         private final @IntRange(from = 0, to = _NUM_OP - 1) int mOp;
         /** The mode of the op */
         private final @Mode int mMode;
-        /** The features that have been used when checking the op */
-        private final @NonNull Map<String, OpFeatureEntry> mFeatures;
+        /** The attributed entries by attribution tag */
+        private final @NonNull Map<String, AttributedOpEntry> mAttributedOpEntries;
 
         /**
          * @hide
@@ -3611,7 +3642,7 @@
          * @see #getLastAccessForegroundTime(int)
          * @see #getLastAccessBackgroundTime(int)
          * @see #getLastAccessTime(int, int, int)
-         * @see OpFeatureEntry#getLastAccessTime(int)
+         * @see AttributedOpEntry#getLastAccessTime(int)
          */
         public long getLastAccessTime(@OpFlags int flags) {
             return getLastAccessTime(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags);
@@ -3628,7 +3659,7 @@
          * @see #getLastAccessTime(int)
          * @see #getLastAccessBackgroundTime(int)
          * @see #getLastAccessTime(int, int, int)
-         * @see OpFeatureEntry#getLastAccessForegroundTime(int)
+         * @see AttributedOpEntry#getLastAccessForegroundTime(int)
          */
         public long getLastAccessForegroundTime(@OpFlags int flags) {
             return getLastAccessTime(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp),
@@ -3646,7 +3677,7 @@
          * @see #getLastAccessTime(int)
          * @see #getLastAccessForegroundTime(int)
          * @see #getLastAccessTime(int, int, int)
-         * @see OpFeatureEntry#getLastAccessBackgroundTime(int)
+         * @see AttributedOpEntry#getLastAccessBackgroundTime(int)
          */
         public long getLastAccessBackgroundTime(@OpFlags int flags) {
             return getLastAccessTime(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE,
@@ -3663,13 +3694,14 @@
         private @Nullable NoteOpEvent getLastAccessEvent(@UidState int fromUidState,
                 @UidState int toUidState, @OpFlags int flags) {
             NoteOpEvent lastAccessEvent = null;
-            for (OpFeatureEntry featureEntry : mFeatures.values()) {
-                NoteOpEvent lastFeatureAccessEvent = featureEntry.getLastAccessEvent(fromUidState,
-                        toUidState, flags);
+            for (AttributedOpEntry attributionEntry : mAttributedOpEntries.values()) {
+                NoteOpEvent lastAttributionAccessEvent = attributionEntry.getLastAccessEvent(
+                        fromUidState, toUidState, flags);
 
-                if (lastAccessEvent == null || (lastFeatureAccessEvent != null
-                        && lastFeatureAccessEvent.getNoteTime() > lastAccessEvent.getNoteTime())) {
-                    lastAccessEvent = lastFeatureAccessEvent;
+                if (lastAccessEvent == null || (lastAttributionAccessEvent != null
+                        && lastAttributionAccessEvent.getNoteTime()
+                        > lastAccessEvent.getNoteTime())) {
+                    lastAccessEvent = lastAttributionAccessEvent;
                 }
             }
 
@@ -3689,7 +3721,7 @@
          * @see #getLastAccessTime(int)
          * @see #getLastAccessForegroundTime(int)
          * @see #getLastAccessBackgroundTime(int)
-         * @see OpFeatureEntry#getLastAccessTime(int, int, int)
+         * @see AttributedOpEntry#getLastAccessTime(int, int, int)
          */
         public long getLastAccessTime(@UidState int fromUidState, @UidState int toUidState,
                 @OpFlags int flags) {
@@ -3725,7 +3757,7 @@
          * @see #getLastRejectForegroundTime(int)
          * @see #getLastRejectBackgroundTime(int)
          * @see #getLastRejectTime(int, int, int)
-         * @see OpFeatureEntry#getLastRejectTime(int)
+         * @see AttributedOpEntry#getLastRejectTime(int)
          */
         public long getLastRejectTime(@OpFlags int flags) {
             return getLastRejectTime(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags);
@@ -3742,7 +3774,7 @@
          * @see #getLastRejectTime(int)
          * @see #getLastRejectBackgroundTime(int)
          * @see #getLastRejectTime(int, int, int)
-         * @see OpFeatureEntry#getLastRejectForegroundTime(int)
+         * @see AttributedOpEntry#getLastRejectForegroundTime(int)
          */
         public long getLastRejectForegroundTime(@OpFlags int flags) {
             return getLastRejectTime(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp),
@@ -3760,7 +3792,7 @@
          * @see #getLastRejectTime(int)
          * @see #getLastRejectForegroundTime(int)
          * @see #getLastRejectTime(int, int, int)
-         * @see OpFeatureEntry#getLastRejectBackgroundTime(int)
+         * @see AttributedOpEntry#getLastRejectBackgroundTime(int)
          */
         public long getLastRejectBackgroundTime(@OpFlags int flags) {
             return getLastRejectTime(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE,
@@ -3777,13 +3809,14 @@
         private @Nullable NoteOpEvent getLastRejectEvent(@UidState int fromUidState,
                 @UidState int toUidState, @OpFlags int flags) {
             NoteOpEvent lastAccessEvent = null;
-            for (OpFeatureEntry featureEntry : mFeatures.values()) {
-                NoteOpEvent lastFeatureAccessEvent = featureEntry.getLastRejectEvent(fromUidState,
-                        toUidState, flags);
+            for (AttributedOpEntry attributionEntry : mAttributedOpEntries.values()) {
+                NoteOpEvent lastAttributionAccessEvent = attributionEntry.getLastRejectEvent(
+                        fromUidState, toUidState, flags);
 
-                if (lastAccessEvent == null || (lastFeatureAccessEvent != null
-                        && lastFeatureAccessEvent.getNoteTime() > lastAccessEvent.getNoteTime())) {
-                    lastAccessEvent = lastFeatureAccessEvent;
+                if (lastAccessEvent == null || (lastAttributionAccessEvent != null
+                        && lastAttributionAccessEvent.getNoteTime()
+                        > lastAccessEvent.getNoteTime())) {
+                    lastAccessEvent = lastAttributionAccessEvent;
                 }
             }
 
@@ -3804,7 +3837,7 @@
          * @see #getLastRejectForegroundTime(int)
          * @see #getLastRejectBackgroundTime(int)
          * @see #getLastRejectTime(int, int, int)
-         * @see OpFeatureEntry#getLastRejectTime(int, int, int)
+         * @see AttributedOpEntry#getLastRejectTime(int, int, int)
          */
         public long getLastRejectTime(@UidState int fromUidState, @UidState int toUidState,
                 @OpFlags int flags) {
@@ -3820,8 +3853,8 @@
          * @return Whether the operation is running.
          */
         public boolean isRunning() {
-            for (OpFeatureEntry opFeatureEntry : mFeatures.values()) {
-                if (opFeatureEntry.isRunning()) {
+            for (AttributedOpEntry opAttributionEntry : mAttributedOpEntries.values()) {
+                if (opAttributionEntry.isRunning()) {
                     return true;
                 }
             }
@@ -3847,7 +3880,7 @@
          * @see #getLastForegroundDuration(int)
          * @see #getLastBackgroundDuration(int)
          * @see #getLastDuration(int, int, int)
-         * @see OpFeatureEntry#getLastDuration(int)
+         * @see AttributedOpEntry#getLastDuration(int)
          */
         public long getLastDuration(@OpFlags int flags) {
             return getLastDuration(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags);
@@ -3863,7 +3896,7 @@
          * @see #getLastDuration(int)
          * @see #getLastBackgroundDuration(int)
          * @see #getLastDuration(int, int, int)
-         * @see OpFeatureEntry#getLastForegroundDuration(int)
+         * @see AttributedOpEntry#getLastForegroundDuration(int)
          */
         public long getLastForegroundDuration(@OpFlags int flags) {
             return getLastDuration(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp),
@@ -3880,7 +3913,7 @@
          * @see #getLastDuration(int)
          * @see #getLastForegroundDuration(int)
          * @see #getLastDuration(int, int, int)
-         * @see OpFeatureEntry#getLastBackgroundDuration(int)
+         * @see AttributedOpEntry#getLastBackgroundDuration(int)
          */
         public long getLastBackgroundDuration(@OpFlags int flags) {
             return getLastDuration(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE,
@@ -3899,7 +3932,7 @@
          * @see #getLastDuration(int)
          * @see #getLastForegroundDuration(int)
          * @see #getLastBackgroundDuration(int)
-         * @see OpFeatureEntry#getLastDuration(int, int, int)
+         * @see AttributedOpEntry#getLastDuration(int, int, int)
          */
         public long getLastDuration(@UidState int fromUidState, @UidState int toUidState,
                 @OpFlags int flags) {
@@ -3974,7 +4007,7 @@
          * @see #getLastForegroundProxyInfo(int)
          * @see #getLastBackgroundProxyInfo(int)
          * @see #getLastProxyInfo(int, int, int)
-         * @see OpFeatureEntry#getLastProxyInfo(int)
+         * @see AttributedOpEntry#getLastProxyInfo(int)
          */
         public @Nullable OpEventProxyInfo getLastProxyInfo(@OpFlags int flags) {
             return getLastProxyInfo(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags);
@@ -3991,7 +4024,7 @@
          * @see #getLastProxyInfo(int)
          * @see #getLastBackgroundProxyInfo(int)
          * @see #getLastProxyInfo(int, int, int)
-         * @see OpFeatureEntry#getLastForegroundProxyInfo(int)
+         * @see AttributedOpEntry#getLastForegroundProxyInfo(int)
          */
         public @Nullable OpEventProxyInfo getLastForegroundProxyInfo(@OpFlags int flags) {
             return getLastProxyInfo(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp),
@@ -4009,7 +4042,7 @@
          * @see #getLastProxyInfo(int)
          * @see #getLastForegroundProxyInfo(int)
          * @see #getLastProxyInfo(int, int, int)
-         * @see OpFeatureEntry#getLastBackgroundProxyInfo(int)
+         * @see AttributedOpEntry#getLastBackgroundProxyInfo(int)
          */
         public @Nullable OpEventProxyInfo getLastBackgroundProxyInfo(@OpFlags int flags) {
             return getLastProxyInfo(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE,
@@ -4029,7 +4062,7 @@
          * @see #getLastProxyInfo(int)
          * @see #getLastForegroundProxyInfo(int)
          * @see #getLastBackgroundProxyInfo(int)
-         * @see OpFeatureEntry#getLastProxyInfo(int, int, int)
+         * @see AttributedOpEntry#getLastProxyInfo(int, int, int)
          */
         public @Nullable OpEventProxyInfo getLastProxyInfo(@UidState int fromUidState,
                 @UidState int toUidState, @OpFlags int flags) {
@@ -4063,15 +4096,15 @@
          *   The code of the op
          * @param mode
          *   The mode of the op
-         * @param features
-         *   The features that have been used when checking the op
+         * @param attributedOpEntries
+         *   The attributions that have been used when noting the op
          * @hide
          */
         @DataClass.Generated.Member
         public OpEntry(
                 @IntRange(from = 0, to = _NUM_OP - 1) int op,
                 @Mode int mode,
-                @NonNull Map<String,OpFeatureEntry> features) {
+                @NonNull Map<String, AttributedOpEntry> attributedOpEntries) {
             this.mOp = op;
             com.android.internal.util.AnnotationValidations.validate(
                     IntRange.class, null, mOp,
@@ -4080,9 +4113,9 @@
             this.mMode = mode;
             com.android.internal.util.AnnotationValidations.validate(
                     Mode.class, null, mMode);
-            this.mFeatures = features;
+            this.mAttributedOpEntries = attributedOpEntries;
             com.android.internal.util.AnnotationValidations.validate(
-                    NonNull.class, null, mFeatures);
+                    NonNull.class, null, mAttributedOpEntries);
 
             // onConstructed(); // You can define this method to get a callback
         }
@@ -4096,14 +4129,14 @@
         }
 
         /**
-         * The features that have been used when checking the op keyed by id of the feature.
+         * The attributed entries keyed by attribution tag.
          *
-         * @see Context#createFeatureContext(String)
+         * @see Context#createAttributionContext(String)
          * @see #noteOp(String, int, String, String, String)
          */
         @DataClass.Generated.Member
-        public @NonNull Map<String,OpFeatureEntry> getFeatures() {
-            return mFeatures;
+        public @NonNull Map<String, AttributedOpEntry> getAttributedOpEntries() {
+            return mAttributedOpEntries;
         }
 
         @Override
@@ -4114,7 +4147,7 @@
 
             dest.writeInt(mOp);
             dest.writeInt(mMode);
-            dest.writeMap(mFeatures);
+            dest.writeMap(mAttributedOpEntries);
         }
 
         @Override
@@ -4130,8 +4163,8 @@
 
             int op = in.readInt();
             int mode = in.readInt();
-            Map<String,OpFeatureEntry> features = new java.util.LinkedHashMap<>();
-            in.readMap(features, OpFeatureEntry.class.getClassLoader());
+            Map<String, AttributedOpEntry> attributions = new java.util.LinkedHashMap<>();
+            in.readMap(attributions, AttributedOpEntry.class.getClassLoader());
 
             this.mOp = op;
             com.android.internal.util.AnnotationValidations.validate(
@@ -4141,9 +4174,9 @@
             this.mMode = mode;
             com.android.internal.util.AnnotationValidations.validate(
                     Mode.class, null, mMode);
-            this.mFeatures = features;
+            this.mAttributedOpEntries = attributions;
             com.android.internal.util.AnnotationValidations.validate(
-                    NonNull.class, null, mFeatures);
+                    NonNull.class, null, mAttributedOpEntries);
 
             // onConstructed(); // You can define this method to get a callback
         }
@@ -4167,7 +4200,7 @@
                 time = 1574809856259L,
                 codegenVersion = "1.0.14",
                 sourceFile = "frameworks/base/core/java/android/app/AppOpsManager.java",
-                inputSignatures = "private final @android.annotation.IntRange(from=0L, to=_NUM_OP - 1) int mOp\nprivate final @android.app.Mode int mMode\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.app.OpFeatureEntry> mFeatures\npublic @android.annotation.UnsupportedAppUsage(maxTargetSdk=Build.VERSION_CODES.Q, publicAlternatives=\"{@code \" + \"#getOpStr()}\") int getOp()\npublic @android.annotation.NonNull java.lang.String getOpStr()\npublic @java.lang.Deprecated @android.annotation.UnsupportedAppUsage(maxTargetSdk=Build.VERSION_CODES.Q, publicAlternatives=\"{@code \" + \"#getAccessTime(int, int)}\") long getTime()\npublic @java.lang.Deprecated long getLastAccessTime(int)\npublic @java.lang.Deprecated long getLastAccessForegroundTime(int)\npublic @java.lang.Deprecated long getLastAccessBackgroundTime(int)\npublic @java.lang.Deprecated long getLastAccessTime(int,int,int)\npublic @java.lang.Deprecated @android.annotation.UnsupportedAppUsage(maxTargetSdk=Build.VERSION_CODES.Q, publicAlternatives=\"{@code \" + \"#getLastRejectTime(int, int, int)}\") long getRejectTime()\npublic @java.lang.Deprecated long getLastRejectTime(int)\npublic @java.lang.Deprecated long getLastRejectForegroundTime(int)\npublic @java.lang.Deprecated long getLastRejectBackgroundTime(int)\npublic @java.lang.Deprecated long getLastRejectTime(int,int,int)\npublic  long getAccessTime(int,int)\npublic  long getRejectTime(int,int)\npublic  boolean isRunning()\nprivate  android.app.NoteOpEvent getLastAccessEvent(int,int,int)\npublic @java.lang.Deprecated long getDuration()\npublic @java.lang.Deprecated long getLastForegroundDuration(int)\npublic @java.lang.Deprecated long getLastBackgroundDuration(int)\npublic @java.lang.Deprecated long getLastDuration(int,int,int)\npublic @java.lang.Deprecated int getProxyUid()\npublic @java.lang.Deprecated @android.annotation.Nullable java.lang.String getProxyPackageName()\nprivate @android.app.UidState int getLastAccessUidStateForFlagsInStatesOfAllFeatures(int,int,int)\npublic @android.app.UidState int getLastAccessUidState(int)\npublic @android.app.UidState int getLastForegroundAccessUidState(int)\npublic @android.app.UidState int getLastBackgroundAccessUidState(int)\nprivate @android.app.UidState int getLastRejectUidStateForFlagsInStatesOfAllFeatures(int,int,int)\npublic @android.app.UidState int getLastRejectUidState(int)\npublic @android.app.UidState int getLastForegroundRejectUidState(int)\npublic @android.app.UidState int getLastBackgroundRejectUidState(int)\npublic  long getDuration(int,int)\npublic  int getProxyUid(int,int)\nprivate  int getProxyUid(int,int,int)\npublic @android.annotation.Nullable java.lang.String getProxyPackageName(int,int)\nprivate @android.annotation.Nullable java.lang.String getProxyPackageName(int,int,int)\nclass OpEntry extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
+                inputSignatures = "private final @android.annotation.IntRange(from=0L, to=_NUM_OP - 1) int mOp\nprivate final @android.app.Mode int mMode\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.app.OpAttributionEntry> mAttributions\npublic @android.annotation.UnsupportedAppUsage(maxTargetSdk=Build.VERSION_CODES.Q, publicAlternatives=\"{@code \" + \"#getOpStr()}\") int getOp()\npublic @android.annotation.NonNull java.lang.String getOpStr()\npublic @java.lang.Deprecated @android.annotation.UnsupportedAppUsage(maxTargetSdk=Build.VERSION_CODES.Q, publicAlternatives=\"{@code \" + \"#getAccessTime(int, int)}\") long getTime()\npublic @java.lang.Deprecated long getLastAccessTime(int)\npublic @java.lang.Deprecated long getLastAccessForegroundTime(int)\npublic @java.lang.Deprecated long getLastAccessBackgroundTime(int)\npublic @java.lang.Deprecated long getLastAccessTime(int,int,int)\npublic @java.lang.Deprecated @android.annotation.UnsupportedAppUsage(maxTargetSdk=Build.VERSION_CODES.Q, publicAlternatives=\"{@code \" + \"#getLastRejectTime(int, int, int)}\") long getRejectTime()\npublic @java.lang.Deprecated long getLastRejectTime(int)\npublic @java.lang.Deprecated long getLastRejectForegroundTime(int)\npublic @java.lang.Deprecated long getLastRejectBackgroundTime(int)\npublic @java.lang.Deprecated long getLastRejectTime(int,int,int)\npublic  long getAccessTime(int,int)\npublic  long getRejectTime(int,int)\npublic  boolean isRunning()\nprivate  android.app.NoteOpEvent getLastAccessEvent(int,int,int)\npublic @java.lang.Deprecated long getDuration()\npublic @java.lang.Deprecated long getLastForegroundDuration(int)\npublic @java.lang.Deprecated long getLastBackgroundDuration(int)\npublic @java.lang.Deprecated long getLastDuration(int,int,int)\npublic @java.lang.Deprecated int getProxyUid()\npublic @java.lang.Deprecated @android.annotation.Nullable java.lang.String getProxyPackageName()\nprivate @android.app.UidState int getLastAccessUidStateForFlagsInStatesOfAllAttributions(int,int,int)\npublic @android.app.UidState int getLastAccessUidState(int)\npublic @android.app.UidState int getLastForegroundAccessUidState(int)\npublic @android.app.UidState int getLastBackgroundAccessUidState(int)\nprivate @android.app.UidState int getLastRejectUidStateForFlagsInStatesOfAllAttributions(int,int,int)\npublic @android.app.UidState int getLastRejectUidState(int)\npublic @android.app.UidState int getLastForegroundRejectUidState(int)\npublic @android.app.UidState int getLastBackgroundRejectUidState(int)\npublic  long getDuration(int,int)\npublic  int getProxyUid(int,int)\nprivate  int getProxyUid(int,int,int)\npublic @android.annotation.Nullable java.lang.String getProxyPackageName(int,int)\nprivate @android.annotation.Nullable java.lang.String getProxyPackageName(int,int,int)\nclass OpEntry extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
         @Deprecated
         private void __metadata() {}
          */
@@ -4183,7 +4216,7 @@
         void visitHistoricalOps(@NonNull HistoricalOps ops);
         void visitHistoricalUidOps(@NonNull HistoricalUidOps ops);
         void visitHistoricalPackageOps(@NonNull HistoricalPackageOps ops);
-        void visitHistoricalFeatureOps(@NonNull HistoricalFeatureOps ops);
+        void visitHistoricalAttributionOps(@NonNull AttributedHistoricalOps ops);
         void visitHistoricalOp(@NonNull HistoricalOp ops);
     }
 
@@ -4196,7 +4229,7 @@
     @IntDef(flag = true, prefix = { "FILTER_BY_" }, value = {
             FILTER_BY_UID,
             FILTER_BY_PACKAGE_NAME,
-            FILTER_BY_FEATURE_ID,
+            FILTER_BY_ATTRIBUTION_TAG,
             FILTER_BY_OP_NAMES
     })
     public @interface HistoricalOpsRequestFilter {}
@@ -4216,11 +4249,11 @@
     public static final int FILTER_BY_PACKAGE_NAME = 1<<1;
 
     /**
-     * Filter historical appop request by feature id.
+     * Filter historical appop request by attribution tag.
      *
      * @hide
      */
-    public static final int FILTER_BY_FEATURE_ID = 1<<2;
+    public static final int FILTER_BY_ATTRIBUTION_TAG = 1<<2;
 
     /**
      * Filter historical appop request by op names.
@@ -4241,7 +4274,7 @@
     public static final class HistoricalOpsRequest {
         private final int mUid;
         private final @Nullable String mPackageName;
-        private final @Nullable String mFeatureId;
+        private final @Nullable String mAttributionTag;
         private final @Nullable List<String> mOpNames;
         private final @HistoricalOpsRequestFilter int mFilter;
         private final long mBeginTimeMillis;
@@ -4249,12 +4282,12 @@
         private final @OpFlags int mFlags;
 
         private HistoricalOpsRequest(int uid, @Nullable String packageName,
-                @Nullable String featureId, @Nullable List<String> opNames,
+                @Nullable String attributionTag, @Nullable List<String> opNames,
                 @HistoricalOpsRequestFilter int filter, long beginTimeMillis,
                 long endTimeMillis, @OpFlags int flags) {
             mUid = uid;
             mPackageName = packageName;
-            mFeatureId = featureId;
+            mAttributionTag = attributionTag;
             mOpNames = opNames;
             mFilter = filter;
             mBeginTimeMillis = beginTimeMillis;
@@ -4272,7 +4305,7 @@
         public static final class Builder {
             private int mUid = Process.INVALID_UID;
             private @Nullable String mPackageName;
-            private @Nullable String mFeatureId;
+            private @Nullable String mAttributionTag;
             private @Nullable List<String> mOpNames;
             private @HistoricalOpsRequestFilter int mFilter;
             private final long mBeginTimeMillis;
@@ -4336,14 +4369,14 @@
             }
 
             /**
-             * Sets the feature id to query for.
+             * Sets the attribution tag to query for.
              *
-             * @param featureId The id of the feature.
+             * @param attributionTag attribution tag
              * @return This builder.
              */
-            public @NonNull Builder setFeatureId(@Nullable String featureId) {
-                mFeatureId = featureId;
-                mFilter |= FILTER_BY_FEATURE_ID;
+            public @NonNull Builder setAttributionTag(@Nullable String attributionTag) {
+                mAttributionTag = attributionTag;
+                mFilter |= FILTER_BY_ATTRIBUTION_TAG;
 
                 return this;
             }
@@ -4394,7 +4427,7 @@
              * @return a new {@link HistoricalOpsRequest}.
              */
             public @NonNull HistoricalOpsRequest build() {
-                return new HistoricalOpsRequest(mUid, mPackageName, mFeatureId, mOpNames,
+                return new HistoricalOpsRequest(mUid, mPackageName, mAttributionTag, mOpNames,
                         mFilter, mBeginTimeMillis, mEndTimeMillis, mFlags);
             }
         }
@@ -4554,7 +4587,7 @@
          *
          * @param uid Uid to filter for.
          * @param packageName Package to filter for.
-         * @param featureId Package to filter for.
+         * @param attributionTag attribution tag to filter for
          * @param opNames Ops to filter for.
          * @param filter Which parameters to filter on.
          * @param beginTimeMillis The begin time to filter for or {@link Long#MIN_VALUE} for all.
@@ -4562,7 +4595,7 @@
          *
          * @hide
          */
-        public void filter(int uid, @Nullable String packageName, @Nullable String featureId,
+        public void filter(int uid, @Nullable String packageName, @Nullable String attributionTag,
                 @Nullable String[] opNames, @HistoricalOpsRequestFilter int filter,
                 long beginTimeMillis, long endTimeMillis) {
             final long durationMillis = getDurationMillis();
@@ -4576,7 +4609,7 @@
                 if ((filter & FILTER_BY_UID) != 0 && uid != uidOp.getUid()) {
                     mHistoricalUidOps.removeAt(i);
                 } else {
-                    uidOp.filter(packageName, featureId, opNames, filter, scaleFactor);
+                    uidOp.filter(packageName, attributionTag, opNames, filter, scaleFactor);
                     if (uidOp.getPackageCount() == 0) {
                         mHistoricalUidOps.removeAt(i);
                     }
@@ -4607,28 +4640,28 @@
         /** @hide */
         @TestApi
         public void increaseAccessCount(int opCode, int uid, @NonNull String packageName,
-                @Nullable String featureId, @UidState int uidState,  @OpFlags int flags,
+                @Nullable String attributionTag, @UidState int uidState,  @OpFlags int flags,
                 long increment) {
             getOrCreateHistoricalUidOps(uid).increaseAccessCount(opCode,
-                    packageName, featureId, uidState, flags, increment);
+                    packageName, attributionTag, uidState, flags, increment);
         }
 
         /** @hide */
         @TestApi
         public void increaseRejectCount(int opCode, int uid, @NonNull String packageName,
-                @Nullable String featureId, @UidState int uidState, @OpFlags int flags,
+                @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
                 long increment) {
             getOrCreateHistoricalUidOps(uid).increaseRejectCount(opCode,
-                    packageName, featureId, uidState, flags, increment);
+                    packageName, attributionTag, uidState, flags, increment);
         }
 
         /** @hide */
         @TestApi
         public void increaseAccessDuration(int opCode, int uid, @NonNull String packageName,
-                @Nullable String featureId, @UidState int uidState, @OpFlags int flags,
+                @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
                 long increment) {
             getOrCreateHistoricalUidOps(uid).increaseAccessDuration(opCode,
-                    packageName, featureId, uidState, flags, increment);
+                    packageName, attributionTag, uidState, flags, increment);
         }
 
         /** @hide */
@@ -4908,7 +4941,7 @@
             }
         }
 
-        private void filter(@Nullable String packageName, @Nullable String featureId,
+        private void filter(@Nullable String packageName, @Nullable String attributionTag,
                 @Nullable String[] opNames, @HistoricalOpsRequestFilter int filter,
                 double fractionToRemove) {
             final int packageCount = getPackageCount();
@@ -4918,8 +4951,8 @@
                         packageOps.getPackageName())) {
                     mHistoricalPackageOps.removeAt(i);
                 } else {
-                    packageOps.filter(featureId, opNames, filter, fractionToRemove);
-                    if (packageOps.getFeatureCount() == 0) {
+                    packageOps.filter(attributionTag, opNames, filter, fractionToRemove);
+                    if (packageOps.getAttributedOpsCount() == 0) {
                         mHistoricalPackageOps.removeAt(i);
                     }
                 }
@@ -4938,24 +4971,24 @@
         }
 
         private void increaseAccessCount(int opCode, @NonNull String packageName,
-                @Nullable String featureId, @UidState int uidState, @OpFlags int flags,
+                @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
                 long increment) {
             getOrCreateHistoricalPackageOps(packageName).increaseAccessCount(
-                    opCode, featureId, uidState, flags, increment);
+                    opCode, attributionTag, uidState, flags, increment);
         }
 
         private void increaseRejectCount(int opCode, @NonNull String packageName,
-                @Nullable String featureId, @UidState int uidState,  @OpFlags int flags,
+                @Nullable String attributionTag, @UidState int uidState,  @OpFlags int flags,
                 long increment) {
             getOrCreateHistoricalPackageOps(packageName).increaseRejectCount(
-                    opCode, featureId, uidState, flags, increment);
+                    opCode, attributionTag, uidState, flags, increment);
         }
 
         private void increaseAccessDuration(int opCode, @NonNull String packageName,
-                @Nullable String featureId, @UidState int uidState, @OpFlags int flags,
+                @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
                 long increment) {
             getOrCreateHistoricalPackageOps(packageName).increaseAccessDuration(
-                    opCode, featureId, uidState, flags, increment);
+                    opCode, attributionTag, uidState, flags, increment);
         }
 
         /**
@@ -5100,7 +5133,7 @@
     @SystemApi
     public static final class HistoricalPackageOps implements Parcelable {
         private final @NonNull String mPackageName;
-        private @Nullable ArrayMap<String, HistoricalFeatureOps> mHistoricalFeatureOps;
+        private @Nullable ArrayMap<String, AttributedHistoricalOps> mAttributedHistoricalOps;
 
         /** @hide */
         public HistoricalPackageOps(@NonNull String packageName) {
@@ -5109,70 +5142,71 @@
 
         private HistoricalPackageOps(@NonNull HistoricalPackageOps other) {
             mPackageName = other.mPackageName;
-            final int opCount = other.getFeatureCount();
+            final int opCount = other.getAttributedOpsCount();
             for (int i = 0; i < opCount; i++) {
-                final HistoricalFeatureOps origOps = other.getFeatureOpsAt(i);
-                final HistoricalFeatureOps cloneOps = new HistoricalFeatureOps(origOps);
-                if (mHistoricalFeatureOps == null) {
-                    mHistoricalFeatureOps = new ArrayMap<>(opCount);
+                final AttributedHistoricalOps origOps = other.getAttributedOpsAt(i);
+                final AttributedHistoricalOps cloneOps = new AttributedHistoricalOps(origOps);
+                if (mAttributedHistoricalOps == null) {
+                    mAttributedHistoricalOps = new ArrayMap<>(opCount);
                 }
-                mHistoricalFeatureOps.put(cloneOps.getFeatureId(), cloneOps);
+                mAttributedHistoricalOps.put(cloneOps.getTag(), cloneOps);
             }
         }
 
         private HistoricalPackageOps(@NonNull Parcel parcel) {
             mPackageName = parcel.readString();
-            mHistoricalFeatureOps = parcel.createTypedArrayMap(HistoricalFeatureOps.CREATOR);
+            mAttributedHistoricalOps = parcel.createTypedArrayMap(AttributedHistoricalOps.CREATOR);
         }
 
         private @Nullable HistoricalPackageOps splice(double fractionToRemove) {
             HistoricalPackageOps splice = null;
-            final int featureCount = getFeatureCount();
-            for (int i = 0; i < featureCount; i++) {
-                final HistoricalFeatureOps origOps = getFeatureOpsAt(i);
-                final HistoricalFeatureOps spliceOps = origOps.splice(fractionToRemove);
+            final int attributionCount = getAttributedOpsCount();
+            for (int i = 0; i < attributionCount; i++) {
+                final AttributedHistoricalOps origOps = getAttributedOpsAt(i);
+                final AttributedHistoricalOps spliceOps = origOps.splice(fractionToRemove);
                 if (spliceOps != null) {
                     if (splice == null) {
                         splice = new HistoricalPackageOps(mPackageName);
                     }
-                    if (splice.mHistoricalFeatureOps == null) {
-                        splice.mHistoricalFeatureOps = new ArrayMap<>();
+                    if (splice.mAttributedHistoricalOps == null) {
+                        splice.mAttributedHistoricalOps = new ArrayMap<>();
                     }
-                    splice.mHistoricalFeatureOps.put(spliceOps.getFeatureId(), spliceOps);
+                    splice.mAttributedHistoricalOps.put(spliceOps.getTag(), spliceOps);
                 }
             }
             return splice;
         }
 
         private void merge(@NonNull HistoricalPackageOps other) {
-            final int featureCount = other.getFeatureCount();
-            for (int i = 0; i < featureCount; i++) {
-                final HistoricalFeatureOps otherFeatureOps = other.getFeatureOpsAt(i);
-                final HistoricalFeatureOps thisFeatureOps = getFeatureOps(
-                        otherFeatureOps.getFeatureId());
-                if (thisFeatureOps != null) {
-                    thisFeatureOps.merge(otherFeatureOps);
+            final int attributionCount = other.getAttributedOpsCount();
+            for (int i = 0; i < attributionCount; i++) {
+                final AttributedHistoricalOps otherAttributionOps = other.getAttributedOpsAt(i);
+                final AttributedHistoricalOps thisAttributionOps = getAttributedOps(
+                        otherAttributionOps.getTag());
+                if (thisAttributionOps != null) {
+                    thisAttributionOps.merge(otherAttributionOps);
                 } else {
-                    if (mHistoricalFeatureOps == null) {
-                        mHistoricalFeatureOps = new ArrayMap<>();
+                    if (mAttributedHistoricalOps == null) {
+                        mAttributedHistoricalOps = new ArrayMap<>();
                     }
-                    mHistoricalFeatureOps.put(otherFeatureOps.getFeatureId(), otherFeatureOps);
+                    mAttributedHistoricalOps.put(otherAttributionOps.getTag(),
+                            otherAttributionOps);
                 }
             }
         }
 
-        private void filter(@Nullable String featureId, @Nullable String[] opNames,
+        private void filter(@Nullable String attributionTag, @Nullable String[] opNames,
                 @HistoricalOpsRequestFilter int filter, double fractionToRemove) {
-            final int featureCount = getFeatureCount();
-            for (int i = featureCount - 1; i >= 0; i--) {
-                final HistoricalFeatureOps featureOps = getFeatureOpsAt(i);
-                if ((filter & FILTER_BY_FEATURE_ID) != 0 && !Objects.equals(featureId,
-                        featureOps.getFeatureId())) {
-                    mHistoricalFeatureOps.removeAt(i);
+            final int attributionCount = getAttributedOpsCount();
+            for (int i = attributionCount - 1; i >= 0; i--) {
+                final AttributedHistoricalOps attributionOps = getAttributedOpsAt(i);
+                if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0 && !Objects.equals(attributionTag,
+                        attributionOps.getTag())) {
+                    mAttributedHistoricalOps.removeAt(i);
                 } else {
-                    featureOps.filter(opNames, filter, fractionToRemove);
-                    if (featureOps.getOpCount() == 0) {
-                        mHistoricalFeatureOps.removeAt(i);
+                    attributionOps.filter(opNames, filter, fractionToRemove);
+                    if (attributionOps.getOpCount() == 0) {
+                        mAttributedHistoricalOps.removeAt(i);
                     }
                 }
             }
@@ -5180,38 +5214,38 @@
 
         private void accept(@NonNull HistoricalOpsVisitor visitor) {
             visitor.visitHistoricalPackageOps(this);
-            final int featureCount = getFeatureCount();
-            for (int i = 0; i < featureCount; i++) {
-                getFeatureOpsAt(i).accept(visitor);
+            final int attributionCount = getAttributedOpsCount();
+            for (int i = 0; i < attributionCount; i++) {
+                getAttributedOpsAt(i).accept(visitor);
             }
         }
 
         private boolean isEmpty() {
-            final int featureCount = getFeatureCount();
-            for (int i = featureCount - 1; i >= 0; i--) {
-                final HistoricalFeatureOps featureOps = mHistoricalFeatureOps.valueAt(i);
-                if (!featureOps.isEmpty()) {
+            final int attributionCount = getAttributedOpsCount();
+            for (int i = attributionCount - 1; i >= 0; i--) {
+                final AttributedHistoricalOps attributionOps = mAttributedHistoricalOps.valueAt(i);
+                if (!attributionOps.isEmpty()) {
                     return false;
                 }
             }
             return true;
         }
 
-        private void increaseAccessCount(int opCode, @Nullable String featureId,
+        private void increaseAccessCount(int opCode, @Nullable String attributionTag,
                 @UidState int uidState, @OpFlags int flags, long increment) {
-            getOrCreateHistoricalFeatureOps(featureId).increaseAccessCount(
+            getOrCreateAttributedHistoricalOps(attributionTag).increaseAccessCount(
                     opCode, uidState, flags, increment);
         }
 
-        private void increaseRejectCount(int opCode, @Nullable String featureId,
+        private void increaseRejectCount(int opCode, @Nullable String attributionTag,
                 @UidState int uidState, @OpFlags int flags, long increment) {
-            getOrCreateHistoricalFeatureOps(featureId).increaseRejectCount(
+            getOrCreateAttributedHistoricalOps(attributionTag).increaseRejectCount(
                     opCode, uidState, flags, increment);
         }
 
-        private void increaseAccessDuration(int opCode, @Nullable String featureId,
+        private void increaseAccessDuration(int opCode, @Nullable String attributionTag,
                 @UidState int uidState, @OpFlags int flags, long increment) {
-            getOrCreateHistoricalFeatureOps(featureId).increaseAccessDuration(
+            getOrCreateAttributedHistoricalOps(attributionTag).increaseAccessDuration(
                     opCode, uidState, flags, increment);
         }
 
@@ -5224,17 +5258,18 @@
             return mPackageName;
         }
 
-        private @NonNull HistoricalFeatureOps getOrCreateHistoricalFeatureOps(
-                @Nullable String featureId) {
-            if (mHistoricalFeatureOps == null) {
-                mHistoricalFeatureOps = new ArrayMap<>();
+        private @NonNull AttributedHistoricalOps getOrCreateAttributedHistoricalOps(
+                @Nullable String attributionTag) {
+            if (mAttributedHistoricalOps == null) {
+                mAttributedHistoricalOps = new ArrayMap<>();
             }
-            HistoricalFeatureOps historicalFeatureOp = mHistoricalFeatureOps.get(featureId);
-            if (historicalFeatureOp == null) {
-                historicalFeatureOp = new HistoricalFeatureOps(featureId);
-                mHistoricalFeatureOps.put(featureId, historicalFeatureOp);
+            AttributedHistoricalOps historicalAttributionOp = mAttributedHistoricalOps.get(
+                    attributionTag);
+            if (historicalAttributionOp == null) {
+                historicalAttributionOp = new AttributedHistoricalOps(attributionTag);
+                mAttributedHistoricalOps.put(attributionTag, historicalAttributionOp);
             }
-            return historicalFeatureOp;
+            return historicalAttributionOp;
         }
 
         /**
@@ -5245,13 +5280,13 @@
          */
         public @IntRange(from = 0) int getOpCount() {
             int numOps = 0;
-            int numFeatures = getFeatureCount();
+            int numAttributions = getAttributedOpsCount();
 
             for (int code = 0; code < _NUM_OP; code++) {
                 String opName = opToPublicName(code);
 
-                for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
-                    if (getFeatureOpsAt(featureNum).getOp(opName) != null) {
+                for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) {
+                    if (getAttributedOpsAt(attributionNum).getOp(opName) != null) {
                         numOps++;
                         break;
                     }
@@ -5264,7 +5299,7 @@
         /**
          * Gets the historical op at a given index.
          *
-         * <p>This combines the counts from all features.
+         * <p>This combines the counts from all attributions.
          *
          * @param index The index to lookup.
          * @return The op at the given index.
@@ -5272,13 +5307,13 @@
          */
         public @NonNull HistoricalOp getOpAt(@IntRange(from = 0) int index) {
             int numOpsFound = 0;
-            int numFeatures = getFeatureCount();
+            int numAttributions = getAttributedOpsCount();
 
             for (int code = 0; code < _NUM_OP; code++) {
                 String opName = opToPublicName(code);
 
-                for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
-                    if (getFeatureOpsAt(featureNum).getOp(opName) != null) {
+                for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) {
+                    if (getAttributedOpsAt(attributionNum).getOp(opName) != null) {
                         if (numOpsFound == index) {
                             return getOp(opName);
                         } else {
@@ -5295,25 +5330,25 @@
         /**
          * Gets the historical entry for a given op name.
          *
-         * <p>This combines the counts from all features.
+         * <p>This combines the counts from all attributions.
          *
          * @param opName The op name.
          * @return The historical entry for that op name.
          */
         public @Nullable HistoricalOp getOp(@NonNull String opName) {
-            if (mHistoricalFeatureOps == null) {
+            if (mAttributedHistoricalOps == null) {
                 return null;
             }
 
             HistoricalOp combinedOp = null;
-            int numFeatures = getFeatureCount();
-            for (int i = 0; i < numFeatures; i++) {
-                HistoricalOp featureOp = getFeatureOpsAt(i).getOp(opName);
-                if (featureOp != null) {
+            int numAttributions = getAttributedOpsCount();
+            for (int i = 0; i < numAttributions; i++) {
+                HistoricalOp attributionOp = getAttributedOpsAt(i).getOp(opName);
+                if (attributionOp != null) {
                     if (combinedOp == null) {
-                        combinedOp = new HistoricalOp(featureOp);
+                        combinedOp = new HistoricalOp(attributionOp);
                     } else {
-                        combinedOp.merge(featureOp);
+                        combinedOp.merge(attributionOp);
                     }
                 }
             }
@@ -5329,7 +5364,7 @@
         @Override
         public void writeToParcel(@NonNull Parcel parcel, int flags) {
             parcel.writeString(mPackageName);
-            parcel.writeTypedArrayMap(mHistoricalFeatureOps, flags);
+            parcel.writeTypedArrayMap(mAttributedHistoricalOps, flags);
         }
 
         public static final @android.annotation.NonNull Creator<HistoricalPackageOps> CREATOR =
@@ -5357,11 +5392,11 @@
             if (!mPackageName.equals(other.mPackageName)) {
                 return false;
             }
-            if (mHistoricalFeatureOps == null) {
-                if (other.mHistoricalFeatureOps != null) {
+            if (mAttributedHistoricalOps == null) {
+                if (other.mAttributedHistoricalOps != null) {
                     return false;
                 }
-            } else if (!mHistoricalFeatureOps.equals(other.mHistoricalFeatureOps)) {
+            } else if (!mAttributedHistoricalOps.equals(other.mAttributedHistoricalOps)) {
                 return false;
             }
             return true;
@@ -5370,58 +5405,58 @@
         @Override
         public int hashCode() {
             int result = mPackageName != null ? mPackageName.hashCode() : 0;
-            result = 31 * result + (mHistoricalFeatureOps != null ? mHistoricalFeatureOps.hashCode()
-                    : 0);
+            result = 31 * result + (mAttributedHistoricalOps != null
+                    ? mAttributedHistoricalOps.hashCode() : 0);
             return result;
         }
 
         /**
-         * Gets number of feature with historical ops.
+         * Gets number of attributed historical ops.
          *
-         * @return The number of feature with historical ops.
+         * @return The number of attribution with historical ops.
          *
-         * @see #getFeatureOpsAt(int)
+         * @see #getAttributedOpsAt(int)
          */
-        public @IntRange(from = 0) int getFeatureCount() {
-            if (mHistoricalFeatureOps == null) {
+        public @IntRange(from = 0) int getAttributedOpsCount() {
+            if (mAttributedHistoricalOps == null) {
                 return 0;
             }
-            return mHistoricalFeatureOps.size();
+            return mAttributedHistoricalOps.size();
         }
 
         /**
-         * Gets the historical feature ops at a given index.
+         * Gets the attributed historical ops at a given index.
          *
          * @param index The index.
          *
-         * @return The historical feature ops at the given index.
+         * @return The historical attribution ops at the given index.
          *
-         * @see #getFeatureCount()
+         * @see #getAttributedOpsCount()
          */
-        public @NonNull HistoricalFeatureOps getFeatureOpsAt(@IntRange(from = 0) int index) {
-            if (mHistoricalFeatureOps == null) {
+        public @NonNull AttributedHistoricalOps getAttributedOpsAt(@IntRange(from = 0) int index) {
+            if (mAttributedHistoricalOps == null) {
                 throw new IndexOutOfBoundsException();
             }
-            return mHistoricalFeatureOps.valueAt(index);
+            return mAttributedHistoricalOps.valueAt(index);
         }
 
         /**
-         * Gets the historical feature ops for a given feature.
+         * Gets the attributed historical ops for a given attribution tag.
          *
-         * @param featureId The feature id.
+         * @param attributionTag The attribution tag.
          *
-         * @return The historical ops for the feature.
+         * @return The historical ops for the attribution.
          */
-        public @Nullable HistoricalFeatureOps getFeatureOps(@NonNull String featureId) {
-            if (mHistoricalFeatureOps == null) {
+        public @Nullable AttributedHistoricalOps getAttributedOps(@NonNull String attributionTag) {
+            if (mAttributedHistoricalOps == null) {
                 return null;
             }
-            return mHistoricalFeatureOps.get(featureId);
+            return mAttributedHistoricalOps.get(attributionTag);
         }
     }
 
     /**
-     * This class represents historical app op information about a feature in a package.
+     * This class represents historical app op information about a attribution in a package.
      *
      * @hide
      */
@@ -5431,20 +5466,20 @@
     @DataClass(genHiddenConstructor = true,
             genEqualsHashCode = true, genHiddenCopyConstructor = true) */
     @DataClass.Suppress("getHistoricalOps")
-    public static final class HistoricalFeatureOps implements Parcelable {
-        /** Id of the {@link Context#createFeatureContext feature} in the package */
-        private final @Nullable String mFeatureId;
+    public static final class AttributedHistoricalOps implements Parcelable {
+        /** {@link Context#createAttributionContext attribution} tag */
+        private final @Nullable String mTag;
 
-        /** Ops for this feature */
+        /** Ops for this attribution */
         private @Nullable ArrayMap<String, HistoricalOp> mHistoricalOps;
 
         /** @hide */
-        public HistoricalFeatureOps(@NonNull String featureId) {
-            mFeatureId = featureId;
+        public AttributedHistoricalOps(@NonNull String tag) {
+            mTag = tag;
         }
 
-        private HistoricalFeatureOps(@NonNull HistoricalFeatureOps other) {
-            mFeatureId = other.mFeatureId;
+        private AttributedHistoricalOps(@NonNull AttributedHistoricalOps other) {
+            mTag = other.mTag;
             final int opCount = other.getOpCount();
             for (int i = 0; i < opCount; i++) {
                 final HistoricalOp origOp = other.getOpAt(i);
@@ -5456,15 +5491,15 @@
             }
         }
 
-        private @Nullable HistoricalFeatureOps splice(double fractionToRemove) {
-            HistoricalFeatureOps splice = null;
+        private @Nullable AttributedHistoricalOps splice(double fractionToRemove) {
+            AttributedHistoricalOps splice = null;
             final int opCount = getOpCount();
             for (int i = 0; i < opCount; i++) {
                 final HistoricalOp origOps = getOpAt(i);
                 final HistoricalOp spliceOps = origOps.splice(fractionToRemove);
                 if (spliceOps != null) {
                     if (splice == null) {
-                        splice = new HistoricalFeatureOps(mFeatureId, null);
+                        splice = new AttributedHistoricalOps(mTag, null);
                     }
                     if (splice.mHistoricalOps == null) {
                         splice.mHistoricalOps = new ArrayMap<>();
@@ -5475,7 +5510,7 @@
             return splice;
         }
 
-        private void merge(@NonNull HistoricalFeatureOps other) {
+        private void merge(@NonNull AttributedHistoricalOps other) {
             final int opCount = other.getOpCount();
             for (int i = 0; i < opCount; i++) {
                 final HistoricalOp otherOp = other.getOpAt(i);
@@ -5572,7 +5607,7 @@
         }
 
         private void accept(@NonNull HistoricalOpsVisitor visitor) {
-            visitor.visitHistoricalFeatureOps(this);
+            visitor.visitHistoricalAttributionOps(this);
             final int opCount = getOpCount();
             for (int i = 0; i < opCount; i++) {
                 getOpAt(i).accept(visitor);
@@ -5608,46 +5643,46 @@
 
 
         /**
-         * Creates a new HistoricalFeatureOps.
+         * Creates a new HistoricalAttributionOps.
          *
-         * @param featureId
-         *   Id of the {@link Context#createFeatureContext feature} in the package
+         * @param tag
+         *   {@link Context#createAttributionContext attribution} tag
          * @param historicalOps
-         *   Ops for this feature
+         *   Ops for this attribution
          * @hide
          */
         @DataClass.Generated.Member
-        public HistoricalFeatureOps(
-                @Nullable String featureId,
+        public AttributedHistoricalOps(
+                @Nullable String tag,
                 @Nullable ArrayMap<String,HistoricalOp> historicalOps) {
-            this.mFeatureId = featureId;
+            this.mTag = tag;
             this.mHistoricalOps = historicalOps;
 
             // onConstructed(); // You can define this method to get a callback
         }
 
         /**
-         * Id of the {@link Context#createFeatureContext feature} in the package
+         * {@link Context#createAttributionContext attribution} tag
          */
         @DataClass.Generated.Member
-        public @Nullable String getFeatureId() {
-            return mFeatureId;
+        public @Nullable String getTag() {
+            return mTag;
         }
 
         @Override
         @DataClass.Generated.Member
         public boolean equals(@Nullable Object o) {
             // You can override field equality logic by defining either of the methods like:
-            // boolean fieldNameEquals(HistoricalFeatureOps other) { ... }
+            // boolean fieldNameEquals(HistoricalAttributionOps other) { ... }
             // boolean fieldNameEquals(FieldType otherValue) { ... }
 
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
             @SuppressWarnings("unchecked")
-            HistoricalFeatureOps that = (HistoricalFeatureOps) o;
+            AttributedHistoricalOps that = (AttributedHistoricalOps) o;
             //noinspection PointlessBooleanExpression
             return true
-                    && Objects.equals(mFeatureId, that.mFeatureId)
+                    && Objects.equals(mTag, that.mTag)
                     && Objects.equals(mHistoricalOps, that.mHistoricalOps);
         }
 
@@ -5658,7 +5693,7 @@
             // int fieldNameHashCode() { ... }
 
             int _hash = 1;
-            _hash = 31 * _hash + Objects.hashCode(mFeatureId);
+            _hash = 31 * _hash + Objects.hashCode(mTag);
             _hash = 31 * _hash + Objects.hashCode(mHistoricalOps);
             return _hash;
         }
@@ -5670,10 +5705,10 @@
             // void parcelFieldName(Parcel dest, int flags) { ... }
 
             byte flg = 0;
-            if (mFeatureId != null) flg |= 0x1;
+            if (mTag != null) flg |= 0x1;
             if (mHistoricalOps != null) flg |= 0x2;
             dest.writeByte(flg);
-            if (mFeatureId != null) dest.writeString(mFeatureId);
+            if (mTag != null) dest.writeString(mTag);
             if (mHistoricalOps != null) dest.writeMap(mHistoricalOps);
         }
 
@@ -5684,35 +5719,35 @@
         /** @hide */
         @SuppressWarnings({"unchecked", "RedundantCast"})
         @DataClass.Generated.Member
-        /* package-private */ HistoricalFeatureOps(@NonNull Parcel in) {
+        /* package-private */ AttributedHistoricalOps(@NonNull Parcel in) {
             // You can override field unparcelling by defining methods like:
             // static FieldType unparcelFieldName(Parcel in) { ... }
 
             byte flg = in.readByte();
-            String featureId = (flg & 0x1) == 0 ? null : in.readString();
+            String attributionTag = (flg & 0x1) == 0 ? null : in.readString();
             ArrayMap<String,HistoricalOp> historicalOps = null;
             if ((flg & 0x2) != 0) {
                 historicalOps = new ArrayMap();
                 in.readMap(historicalOps, HistoricalOp.class.getClassLoader());
             }
 
-            this.mFeatureId = featureId;
+            this.mTag = attributionTag;
             this.mHistoricalOps = historicalOps;
 
             // onConstructed(); // You can define this method to get a callback
         }
 
         @DataClass.Generated.Member
-        public static final @NonNull Parcelable.Creator<HistoricalFeatureOps> CREATOR
-                = new Parcelable.Creator<HistoricalFeatureOps>() {
+        public static final @NonNull Parcelable.Creator<AttributedHistoricalOps> CREATOR
+                = new Parcelable.Creator<AttributedHistoricalOps>() {
             @Override
-            public HistoricalFeatureOps[] newArray(int size) {
-                return new HistoricalFeatureOps[size];
+            public AttributedHistoricalOps[] newArray(int size) {
+                return new AttributedHistoricalOps[size];
             }
 
             @Override
-            public HistoricalFeatureOps createFromParcel(@NonNull Parcel in) {
-                return new HistoricalFeatureOps(in);
+            public AttributedHistoricalOps createFromParcel(@NonNull Parcel in) {
+                return new AttributedHistoricalOps(in);
             }
         };
 
@@ -5721,7 +5756,7 @@
                 time = 1578113234821L,
                 codegenVersion = "1.0.14",
                 sourceFile = "frameworks/base/core/java/android/app/AppOpsManager.java",
-                inputSignatures = "private final @android.annotation.Nullable java.lang.String mFeatureId\nprivate @android.annotation.Nullable android.util.ArrayMap<java.lang.String,android.app.HistoricalOp> mHistoricalOps\nprivate @android.annotation.Nullable android.app.HistoricalFeatureOps splice(double)\nprivate  void merge(android.app.HistoricalFeatureOps)\nprivate  void filter(java.lang.String[],int,double)\nprivate  boolean isEmpty()\nprivate  void increaseAccessCount(int,int,int,long)\nprivate  void increaseRejectCount(int,int,int,long)\nprivate  void increaseAccessDuration(int,int,int,long)\npublic @android.annotation.IntRange(from=0L) int getOpCount()\npublic @android.annotation.NonNull android.app.HistoricalOp getOpAt(int)\npublic @android.annotation.Nullable android.app.HistoricalOp getOp(java.lang.String)\nprivate  void accept(android.app.HistoricalOpsVisitor)\nprivate @android.annotation.NonNull android.app.HistoricalOp getOrCreateHistoricalOp(int)\nclass HistoricalFeatureOps extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genEqualsHashCode=true, genHiddenCopyConstructor=true)")
+                inputSignatures = "private final @android.annotation.Nullable java.lang.String mAttributionTag\nprivate @android.annotation.Nullable android.util.ArrayMap<java.lang.String,android.app.HistoricalOp> mHistoricalOps\nprivate @android.annotation.Nullable android.app.HistoricalAttributionOps splice(double)\nprivate  void merge(android.app.HistoricalAttributionOps)\nprivate  void filter(java.lang.String[],int,double)\nprivate  boolean isEmpty()\nprivate  void increaseAccessCount(int,int,int,long)\nprivate  void increaseRejectCount(int,int,int,long)\nprivate  void increaseAccessDuration(int,int,int,long)\npublic @android.annotation.IntRange(from=0L) int getOpCount()\npublic @android.annotation.NonNull android.app.HistoricalOp getOpAt(int)\npublic @android.annotation.Nullable android.app.HistoricalOp getOp(java.lang.String)\nprivate  void accept(android.app.HistoricalOpsVisitor)\nprivate @android.annotation.NonNull android.app.HistoricalOp getOrCreateHistoricalOp(int)\nclass HistoricalAttributionOps extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genEqualsHashCode=true, genHiddenCopyConstructor=true)")
         @Deprecated
         private void __metadata() {}
         */
@@ -6397,7 +6432,7 @@
         Objects.requireNonNull(executor, "executor cannot be null");
         Objects.requireNonNull(callback, "callback cannot be null");
         try {
-            mService.getHistoricalOps(request.mUid, request.mPackageName, request.mFeatureId,
+            mService.getHistoricalOps(request.mUid, request.mPackageName, request.mAttributionTag,
                     request.mOpNames, request.mFilter, request.mBeginTimeMillis,
                     request.mEndTimeMillis, request.mFlags, new RemoteCallback((result) -> {
                 final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS);
@@ -6437,8 +6472,9 @@
         Objects.requireNonNull(callback, "callback cannot be null");
         try {
             mService.getHistoricalOpsFromDiskRaw(request.mUid, request.mPackageName,
-                    request.mFeatureId, request.mOpNames, request.mFilter, request.mBeginTimeMillis,
-                    request.mEndTimeMillis, request.mFlags, new RemoteCallback((result) -> {
+                    request.mAttributionTag, request.mOpNames, request.mFilter,
+                    request.mBeginTimeMillis, request.mEndTimeMillis, request.mFlags,
+                    new RemoteCallback((result) -> {
                 final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS);
                 final long identity = Binder.clearCallingIdentity();
                 try {
@@ -6689,6 +6725,13 @@
                 };
                 mModeWatchers.put(callback, cb);
             }
+
+            // See CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE
+            if (!Compatibility.isChangeEnabled(
+                    CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE)) {
+                flags |= CALL_BACK_ON_SWITCHED_OP;
+            }
+
             try {
                 mService.startWatchingModeWithFlags(op, packageName, flags, cb);
             } catch (RemoteException e) {
@@ -7021,8 +7064,8 @@
      * @param op The operation to note.  One of the OPSTR_* constants.
      * @param uid The user id of the application attempting to perform the operation.
      * @param packageName The name of the application attempting to perform the operation.
-     * @param featureId The {@link Context#createFeatureContext feature} in the package or {@code
-     * null} for default feature
+     * @param attributionTag The {@link Context#createAttributionContext attribution tag} or {@code
+     * null} for default attribution
      * @param message A message describing the reason the op was noted
      *
      * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
@@ -7032,8 +7075,8 @@
      * @throws SecurityException If the app has been configured to crash on this op.
      */
     public int noteOp(@NonNull String op, int uid, @Nullable String packageName,
-            @Nullable String featureId, @Nullable String message) {
-        return noteOp(strOpToOp(op), uid, packageName, featureId, message);
+            @Nullable String attributionTag, @Nullable String message) {
+        return noteOp(strOpToOp(op), uid, packageName, attributionTag, message);
     }
 
     /**
@@ -7049,7 +7092,8 @@
      * @param op The operation to note.  One of the OP_* constants.
      * @param uid The user id of the application attempting to perform the operation.
      * @param packageName The name of the application attempting to perform the operation.
-     * @param featureId The feature in the app or {@code null} for default feature
+     * @param attributionTag The {@link Context#createAttributionContext attribution tag} or {@code
+     * null} for default attribution
      * @param message A message describing the reason the op was noted
      *
      * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
@@ -7060,9 +7104,9 @@
      *
      * @hide
      */
-    public int noteOp(int op, int uid, @Nullable String packageName, @Nullable String featureId,
-            @Nullable String message) {
-        final int mode = noteOpNoThrow(op, uid, packageName, featureId, message);
+    public int noteOp(int op, int uid, @Nullable String packageName,
+            @Nullable String attributionTag, @Nullable String message) {
+        final int mode = noteOpNoThrow(op, uid, packageName, attributionTag, message);
         if (mode == MODE_ERRORED) {
             throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
         }
@@ -7097,8 +7141,8 @@
      * @param op The operation to note.  One of the OPSTR_* constants.
      * @param uid The user id of the application attempting to perform the operation.
      * @param packageName The name of the application attempting to perform the operation.
-     * @param featureId The {@link Context#createFeatureContext feature} in the package or {@code
-     * null} for default feature
+     * @param attributionTag The {@link Context#createAttributionContext attribution tag} or {@code
+     * null} for default attribution
      * @param message A message describing the reason the op was noted
      *
      * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
@@ -7106,8 +7150,8 @@
      * causing the app to crash).
      */
     public int noteOpNoThrow(@NonNull String op, int uid, @NonNull String packageName,
-            @Nullable String featureId, @Nullable String message) {
-        return noteOpNoThrow(strOpToOp(op), uid, packageName, featureId, message);
+            @Nullable String attributionTag, @Nullable String message) {
+        return noteOpNoThrow(strOpToOp(op), uid, packageName, attributionTag, message);
     }
 
     /**
@@ -7117,7 +7161,8 @@
      * @param op The operation to note.  One of the OP_* constants.
      * @param uid The user id of the application attempting to perform the operation.
      * @param packageName The name of the application attempting to perform the operation.
-     * @param featureId The feature in the app or {@code null} for default feature
+     * @param attributionTag The {@link Context#createAttributionContext attribution tag} or {@code
+     * null} for default attribution
      * @param message A message describing the reason the op was noted
      *
      * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
@@ -7127,7 +7172,7 @@
      * @hide
      */
     public int noteOpNoThrow(int op, int uid, @Nullable String packageName,
-            @Nullable String featureId, @Nullable String message) {
+            @Nullable String attributionTag, @Nullable String message) {
         try {
             collectNoteOpCallsForValidation(op);
             int collectionMode = getNotedOpCollectionMode(uid, packageName, op);
@@ -7138,14 +7183,14 @@
                 }
             }
 
-            int mode = mService.noteOperation(op, uid, packageName, featureId,
+            int mode = mService.noteOperation(op, uid, packageName, attributionTag,
                     collectionMode == COLLECT_ASYNC, message);
 
             if (mode == MODE_ALLOWED) {
                 if (collectionMode == COLLECT_SELF) {
-                    collectNotedOpForSelf(op, featureId);
+                    collectNotedOpForSelf(op, attributionTag);
                 } else if (collectionMode == COLLECT_SYNC) {
-                    collectNotedOpSync(op, featureId);
+                    collectNotedOpSync(op, attributionTag);
                 }
             }
 
@@ -7185,8 +7230,8 @@
      * @param op The operation to note. One of the OP_* constants.
      * @param proxiedPackageName The name of the application calling into the proxy application.
      * @param proxiedUid The uid of the proxied application
-     * @param proxiedFeatureId The feature in the proxied app or {@code null} for default
-     *                           feature
+     * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext
+     * attribution tag} or {@code null} for default attribution
      * @param message A message describing the reason the op was noted
      *
      * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED}
@@ -7198,8 +7243,8 @@
      * @hide
      */
     public int noteProxyOp(int op, @Nullable String proxiedPackageName, int proxiedUid,
-            @Nullable String proxiedFeatureId, @Nullable String message) {
-        int mode = noteProxyOpNoThrow(op, proxiedPackageName, proxiedUid, proxiedFeatureId,
+            @Nullable String proxiedAttributionTag, @Nullable String message) {
+        int mode = noteProxyOpNoThrow(op, proxiedPackageName, proxiedUid, proxiedAttributionTag,
                 message);
         if (mode == MODE_ERRORED) {
             throw new SecurityException("Proxy package " + mContext.getOpPackageName()
@@ -7218,8 +7263,8 @@
      * @param op The operation to note. One of the OPSTR_* constants.
      * @param proxiedPackageName The name of the application calling into the proxy application.
      * @param proxiedUid The uid of the proxied application
-     * @param proxiedFeatureId The feature in the proxied app or {@code null} for default
-     *                           feature
+     * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext
+     * attribution tag} or {@code null} for default attribution
      * @param message A message describing the reason the op was noted
      *
      * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED}
@@ -7229,8 +7274,8 @@
      * op.
      */
     public int noteProxyOp(@NonNull String op, @Nullable String proxiedPackageName, int proxiedUid,
-            @Nullable String proxiedFeatureId, @Nullable String message) {
-        return noteProxyOp(strOpToOp(op), proxiedPackageName, proxiedUid, proxiedFeatureId,
+            @Nullable String proxiedAttributionTag, @Nullable String message) {
+        return noteProxyOp(strOpToOp(op), proxiedPackageName, proxiedUid, proxiedAttributionTag,
                 message);
     }
 
@@ -7261,14 +7306,14 @@
      * @param op The op to note
      * @param proxiedPackageName The package to note the op for
      * @param proxiedUid The uid the package belongs to
-     * @param proxiedFeatureId The feature in the proxied app or {@code null} for default
-     *                           feature
+     * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext
+     * attribution tag} or {@code null} for default attribution
      * @param message A message describing the reason the op was noted
      */
     public int noteProxyOpNoThrow(@NonNull String op, @Nullable String proxiedPackageName,
-            int proxiedUid, @Nullable String proxiedFeatureId, @Nullable String message) {
+            int proxiedUid, @Nullable String proxiedAttributionTag, @Nullable String message) {
         return noteProxyOpNoThrow(strOpToOp(op), proxiedPackageName, proxiedUid,
-                proxiedFeatureId, message);
+                proxiedAttributionTag, message);
     }
 
     /**
@@ -7279,14 +7324,14 @@
      * @param proxiedPackageName The package to note the op for or {@code null} if the op should be
      *                           noted for the "android" package
      * @param proxiedUid The uid the package belongs to
-     * @param proxiedFeatureId The feature in the proxied app or {@code null} for default
-     *                           feature
+     * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext
+     * attribution tag} or {@code null} for default attribution
      * @param message A message describing the reason the op was noted
      *
      * @hide
      */
     public int noteProxyOpNoThrow(int op, @Nullable String proxiedPackageName, int proxiedUid,
-            @Nullable String proxiedFeatureId, @Nullable String message) {
+            @Nullable String proxiedAttributionTag, @Nullable String message) {
         int myUid = Process.myUid();
 
         try {
@@ -7300,17 +7345,17 @@
             }
 
             int mode = mService.noteProxyOperation(op, proxiedUid, proxiedPackageName,
-                    proxiedFeatureId, myUid, mContext.getOpPackageName(),
-                    mContext.getFeatureId(), collectionMode == COLLECT_ASYNC, message);
+                    proxiedAttributionTag, myUid, mContext.getOpPackageName(),
+                    mContext.getAttributionTag(), collectionMode == COLLECT_ASYNC, message);
 
             if (mode == MODE_ALLOWED) {
                 if (collectionMode == COLLECT_SELF) {
-                    collectNotedOpForSelf(op, proxiedFeatureId);
+                    collectNotedOpForSelf(op, proxiedAttributionTag);
                 } else if (collectionMode == COLLECT_SYNC
                         // Only collect app-ops when the proxy is trusted
                         && mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1,
                         myUid) == PackageManager.PERMISSION_GRANTED) {
-                    collectNotedOpSync(op, proxiedFeatureId);
+                    collectNotedOpSync(op, proxiedAttributionTag);
                 }
             }
 
@@ -7499,8 +7544,8 @@
      * @param op The operation to start.  One of the OPSTR_* constants.
      * @param uid The user id of the application attempting to perform the operation.
      * @param packageName The name of the application attempting to perform the operation.
-     * @param featureId The {@link Context#createFeatureContext feature} in the package or {@code
-     * null} for default feature
+     * @param attributionTag The {@link Context#createAttributionContext attribution tag} or
+     * {@code null} for default attribution
      * @param message Description why op was started
      *
      * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
@@ -7511,8 +7556,8 @@
      * the package is not in the passed in UID.
      */
     public int startOp(@NonNull String op, int uid, @Nullable String packageName,
-            @Nullable String featureId, @Nullable String message) {
-        return startOp(strOpToOp(op), uid, packageName, false, featureId, message);
+            @Nullable String attributionTag, @Nullable String message) {
+        return startOp(strOpToOp(op), uid, packageName, false, attributionTag, message);
     }
 
     /**
@@ -7521,7 +7566,8 @@
      * @param op The operation to start.  One of the OP_* constants.
      * @param uid The user id of the application attempting to perform the operation.
      * @param packageName The name of the application attempting to perform the operation.
-     * @param featureId The feature in the app or {@code null} for default feature
+     * @param attributionTag The {@link Context#createAttributionContext attribution tag} or
+     * {@code null} for default attribution
      * @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}.
      * @param message Description why op was started
      *
@@ -7535,8 +7581,8 @@
      * @hide
      */
     public int startOp(int op, int uid, @Nullable String packageName, boolean startIfModeDefault,
-            @Nullable String featureId, @Nullable String message) {
-        final int mode = startOpNoThrow(op, uid, packageName, startIfModeDefault, featureId,
+            @Nullable String attributionTag, @Nullable String message) {
+        final int mode = startOpNoThrow(op, uid, packageName, startIfModeDefault, attributionTag,
                 message);
         if (mode == MODE_ERRORED) {
             throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
@@ -7579,7 +7625,8 @@
      * @param op The operation to start.  One of the OP_* constants.
      * @param uid The user id of the application attempting to perform the operation.
      * @param packageName The name of the application attempting to perform the operation.
-     * @param featureId The feature in the app or {@code null} for default feature
+     * @param attributionTag The {@link Context#createAttributionContext attribution tag} or
+     * {@code null} for default attribution
      * @param message Description why op was started
      *
      * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
@@ -7587,8 +7634,8 @@
      * causing the app to crash).
      */
     public int startOpNoThrow(@NonNull String op, int uid, @NonNull String packageName,
-            @NonNull String featureId, @Nullable String message) {
-        return startOpNoThrow(strOpToOp(op), uid, packageName, false, featureId, message);
+            @NonNull String attributionTag, @Nullable String message) {
+        return startOpNoThrow(strOpToOp(op), uid, packageName, false, attributionTag, message);
     }
 
     /**
@@ -7598,7 +7645,8 @@
      * @param op The operation to start.  One of the OP_* constants.
      * @param uid The user id of the application attempting to perform the operation.
      * @param packageName The name of the application attempting to perform the operation.
-     * @param featureId The feature in the app or {@code null} for default feature
+     * @param attributionTag The {@link Context#createAttributionContext attribution tag} or
+     * {@code null} for default attribution
      * @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}.
      * @param message Description why op was started
      *
@@ -7609,7 +7657,7 @@
      * @hide
      */
     public int startOpNoThrow(int op, int uid, @NonNull String packageName,
-            boolean startIfModeDefault, @Nullable String featureId, @Nullable String message) {
+            boolean startIfModeDefault, @Nullable String attributionTag, @Nullable String message) {
         try {
             collectNoteOpCallsForValidation(op);
             int collectionMode = getNotedOpCollectionMode(uid, packageName, op);
@@ -7621,13 +7669,13 @@
             }
 
             int mode = mService.startOperation(getClientId(), op, uid, packageName,
-                    featureId, startIfModeDefault, collectionMode == COLLECT_ASYNC, message);
+                    attributionTag, startIfModeDefault, collectionMode == COLLECT_ASYNC, message);
 
             if (mode == MODE_ALLOWED) {
                 if (collectionMode == COLLECT_SELF) {
-                    collectNotedOpForSelf(op, featureId);
+                    collectNotedOpForSelf(op, attributionTag);
                 } else if (collectionMode == COLLECT_SYNC) {
-                    collectNotedOpSync(op, featureId);
+                    collectNotedOpSync(op, attributionTag);
                 }
             }
 
@@ -7661,8 +7709,8 @@
      * previously passed in when starting the operation.
      */
     public void finishOp(@NonNull String op, int uid, @NonNull String packageName,
-            @Nullable String featureId) {
-        finishOp(strOpToOp(op), uid, packageName, featureId);
+            @Nullable String attributionTag) {
+        finishOp(strOpToOp(op), uid, packageName, attributionTag);
     }
 
     /**
@@ -7683,9 +7731,9 @@
      * @hide
      */
     public void finishOp(int op, int uid, @NonNull String packageName,
-            @Nullable String featureId) {
+            @Nullable String attributionTag) {
         try {
-            mService.finishOperation(getClientId(), op, uid, packageName, featureId);
+            mService.finishOperation(getClientId(), op, uid, packageName, attributionTag);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -7796,15 +7844,15 @@
      * Collect a noted op for the current process.
      *
      * @param op The noted op
-     * @param featureId The feature the op is noted for
+     * @param attributionTag The attribution tag the op is noted for
      */
-    private void collectNotedOpForSelf(int op, @Nullable String featureId) {
+    private void collectNotedOpForSelf(int op, @Nullable String attributionTag) {
         synchronized (sLock) {
             if (sOnOpNotedCallback != null) {
-                sOnOpNotedCallback.onSelfNoted(new SyncNotedAppOp(op, featureId));
+                sOnOpNotedCallback.onSelfNoted(new SyncNotedAppOp(op, attributionTag));
             }
         }
-        sMessageCollector.onSelfNoted(new SyncNotedAppOp(op, featureId));
+        sMessageCollector.onSelfNoted(new SyncNotedAppOp(op, attributionTag));
     }
 
     /**
@@ -7813,9 +7861,9 @@
      * <p> Delivered to caller via {@link #prefixParcelWithAppOpsIfNeeded}
      *
      * @param op The noted op
-     * @param featureId The feature the op is noted for
+     * @param attributionTag The attribution tag the op is noted for
      */
-    private void collectNotedOpSync(int op, @Nullable String featureId) {
+    private void collectNotedOpSync(int op, @Nullable String attributionTag) {
         // If this is inside of a two-way binder call:
         // We are inside of a two-way binder call. Delivered to caller via
         // {@link #prefixParcelWithAppOpsIfNeeded}
@@ -7825,16 +7873,16 @@
             sAppOpsNotedInThisBinderTransaction.set(appOpsNoted);
         }
 
-        long[] appOpsNotedForFeature = appOpsNoted.get(featureId);
-        if (appOpsNotedForFeature == null) {
-            appOpsNotedForFeature = new long[2];
-            appOpsNoted.put(featureId, appOpsNotedForFeature);
+        long[] appOpsNotedForAttribution = appOpsNoted.get(attributionTag);
+        if (appOpsNotedForAttribution == null) {
+            appOpsNotedForAttribution = new long[2];
+            appOpsNoted.put(attributionTag, appOpsNotedForAttribution);
         }
 
         if (op < 64) {
-            appOpsNotedForFeature[0] |= 1L << op;
+            appOpsNotedForAttribution[0] |= 1L << op;
         } else {
-            appOpsNotedForFeature[1] |= 1L << (op - 64);
+            appOpsNotedForAttribution[1] |= 1L << (op - 64);
         }
     }
 
@@ -7915,10 +7963,10 @@
 
         p.writeInt(Parcel.EX_HAS_NOTED_APPOPS_REPLY_HEADER);
 
-        int numFeatureWithNotesAppOps = notedAppOps.size();
-        p.writeInt(numFeatureWithNotesAppOps);
+        int numAttributionWithNotesAppOps = notedAppOps.size();
+        p.writeInt(numAttributionWithNotesAppOps);
 
-        for (int i = 0; i < numFeatureWithNotesAppOps; i++) {
+        for (int i = 0; i < numAttributionWithNotesAppOps; i++) {
             p.writeString(notedAppOps.keyAt(i));
             p.writeLong(notedAppOps.valueAt(i)[0]);
             p.writeLong(notedAppOps.valueAt(i)[1]);
@@ -7936,10 +7984,10 @@
      * @hide
      */
     public static void readAndLogNotedAppops(@NonNull Parcel p) {
-        int numFeaturesWithNotedAppOps = p.readInt();
+        int numAttributionsWithNotedAppOps = p.readInt();
 
-        for (int i = 0; i < numFeaturesWithNotedAppOps; i++) {
-            String featureId = p.readString();
+        for (int i = 0; i < numAttributionsWithNotedAppOps; i++) {
+            String attributionTag = p.readString();
             long[] rawNotedAppOps = new long[2];
             rawNotedAppOps[0] = p.readLong();
             rawNotedAppOps[1] = p.readLong();
@@ -7951,13 +7999,13 @@
                     for (int code = notedAppOps.nextSetBit(0); code != -1;
                             code = notedAppOps.nextSetBit(code + 1)) {
                         if (sOnOpNotedCallback != null) {
-                            sOnOpNotedCallback.onNoted(new SyncNotedAppOp(code, featureId));
+                            sOnOpNotedCallback.onNoted(new SyncNotedAppOp(code, attributionTag));
                         }
                     }
                 }
                 for (int code = notedAppOps.nextSetBit(0); code != -1;
                         code = notedAppOps.nextSetBit(code + 1)) {
-                    sMessageCollector.onNoted(new SyncNotedAppOp(code, featureId));
+                    sMessageCollector.onNoted(new SyncNotedAppOp(code, attributionTag));
                 }
             }
         }
diff --git a/core/java/android/app/AsyncNotedAppOp.java b/core/java/android/app/AsyncNotedAppOp.java
index 4d955db..b0c2762c 100644
--- a/core/java/android/app/AsyncNotedAppOp.java
+++ b/core/java/android/app/AsyncNotedAppOp.java
@@ -48,8 +48,8 @@
     /** Uid that noted the op */
     private final @IntRange(from = 0) int mNotingUid;
 
-    /** {@link android.content.Context#createFeatureContext Feature} in the app */
-    private final @Nullable String mFeatureId;
+    /** {@link android.content.Context#createAttributionContext attribution tag} */
+    private final @Nullable String mAttributionTag;
 
     /** Message associated with the noteOp. This message is set by the app noting the op */
     private final @NonNull String mMessage;
@@ -92,8 +92,8 @@
      *   Op that was noted
      * @param notingUid
      *   Uid that noted the op
-     * @param featureId
-     *   {@link android.content.Context#createFeatureContext Feature} in the app
+     * @param attributionTag
+     *   {@link android.content.Context#createAttributionContext attribution tag}
      * @param message
      *   Message associated with the noteOp. This message is set by the app noting the op
      * @param time
@@ -104,7 +104,7 @@
     public AsyncNotedAppOp(
             @IntRange(from = 0) int opCode,
             @IntRange(from = 0) int notingUid,
-            @Nullable String featureId,
+            @Nullable String attributionTag,
             @NonNull String message,
             @CurrentTimeMillisLong long time) {
         this.mOpCode = opCode;
@@ -115,7 +115,7 @@
         com.android.internal.util.AnnotationValidations.validate(
                 IntRange.class, null, mNotingUid,
                 "from", 0);
-        this.mFeatureId = featureId;
+        this.mAttributionTag = attributionTag;
         this.mMessage = message;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mMessage);
@@ -135,11 +135,11 @@
     }
 
     /**
-     * {@link android.content.Context#createFeatureContext Feature} in the app
+     * {@link android.content.Context#createAttributionContext attribution tag}
      */
     @DataClass.Generated.Member
-    public @Nullable String getFeatureId() {
-        return mFeatureId;
+    public @Nullable String getAttributionTag() {
+        return mAttributionTag;
     }
 
     /**
@@ -173,7 +173,7 @@
         return true
                 && mOpCode == that.mOpCode
                 && mNotingUid == that.mNotingUid
-                && java.util.Objects.equals(mFeatureId, that.mFeatureId)
+                && java.util.Objects.equals(mAttributionTag, that.mAttributionTag)
                 && java.util.Objects.equals(mMessage, that.mMessage)
                 && mTime == that.mTime;
     }
@@ -187,7 +187,7 @@
         int _hash = 1;
         _hash = 31 * _hash + mOpCode;
         _hash = 31 * _hash + mNotingUid;
-        _hash = 31 * _hash + java.util.Objects.hashCode(mFeatureId);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag);
         _hash = 31 * _hash + java.util.Objects.hashCode(mMessage);
         _hash = 31 * _hash + Long.hashCode(mTime);
         return _hash;
@@ -200,11 +200,11 @@
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
         byte flg = 0;
-        if (mFeatureId != null) flg |= 0x4;
+        if (mAttributionTag != null) flg |= 0x4;
         dest.writeByte(flg);
         dest.writeInt(mOpCode);
         dest.writeInt(mNotingUid);
-        if (mFeatureId != null) dest.writeString(mFeatureId);
+        if (mAttributionTag != null) dest.writeString(mAttributionTag);
         dest.writeString(mMessage);
         dest.writeLong(mTime);
     }
@@ -223,7 +223,7 @@
         byte flg = in.readByte();
         int opCode = in.readInt();
         int notingUid = in.readInt();
-        String featureId = (flg & 0x4) == 0 ? null : in.readString();
+        String attributionTag = (flg & 0x4) == 0 ? null : in.readString();
         String message = in.readString();
         long time = in.readLong();
 
@@ -235,7 +235,7 @@
         com.android.internal.util.AnnotationValidations.validate(
                 IntRange.class, null, mNotingUid,
                 "from", 0);
-        this.mFeatureId = featureId;
+        this.mAttributionTag = attributionTag;
         this.mMessage = message;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mMessage);
@@ -261,10 +261,10 @@
     };
 
     @DataClass.Generated(
-            time = 1583866178330L,
+            time = 1583866239013L,
             codegenVersion = "1.0.15",
             sourceFile = "frameworks/base/core/java/android/app/AsyncNotedAppOp.java",
-            inputSignatures = "private final @android.annotation.IntRange(from=0L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.CurrentTimeMillisLong long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nprivate  void onConstructed()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)")
+            inputSignatures = "private final @android.annotation.IntRange(from=0L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mAttributionTag\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.CurrentTimeMillisLong long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nprivate  void onConstructed()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 2873b10..56e6aee 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -219,8 +219,8 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final String mOpPackageName;
 
-    /** If of feature this context is for */
-    private final @Nullable String mFeatureId;
+    /** Attribution tag of this context */
+    private final @Nullable String mAttributionTag;
 
     private final @NonNull ResourcesManager mResourcesManager;
     @UnsupportedAppUsage
@@ -421,8 +421,8 @@
 
     /** @hide */
     @Override
-    public @Nullable String getFeatureId() {
-        return mFeatureId;
+    public @Nullable String getAttributionTag() {
+        return mAttributionTag;
     }
 
     @Override
@@ -1026,10 +1026,10 @@
     public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
         try {
             ActivityTaskManager.getService().startActivityAsUser(
-                mMainThread.getApplicationThread(), getBasePackageName(), getFeatureId(), intent,
-                intent.resolveTypeIfNeeded(getContentResolver()),
-                null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, options,
-                user.getIdentifier());
+                    mMainThread.getApplicationThread(), getBasePackageName(), getAttributionTag(),
+                    intent, intent.resolveTypeIfNeeded(getContentResolver()),
+                    null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, options,
+                    user.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1109,9 +1109,9 @@
         try {
             intent.prepareToLeaveProcess(this);
             ActivityManager.getService().broadcastIntentWithFeature(
-                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
-                    Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
-                    getUserId());
+                    mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+                    null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false,
+                    false, getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1126,9 +1126,9 @@
         try {
             intent.prepareToLeaveProcess(this);
             ActivityManager.getService().broadcastIntentWithFeature(
-                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
-                    Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
-                    null, false, false, getUserId());
+                    mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+                    null, Activity.RESULT_OK, null, null, receiverPermissions,
+                    AppOpsManager.OP_NONE, null, false, false, getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1141,9 +1141,9 @@
         try {
             intent.prepareToLeaveProcess(this);
             ActivityManager.getService().broadcastIntentWithFeature(
-                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
-                    Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
-                    null, false, false, getUserId());
+                    mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+                    null, Activity.RESULT_OK, null, null, receiverPermissions,
+                    AppOpsManager.OP_NONE, null, false, false, getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1156,9 +1156,9 @@
         try {
             intent.prepareToLeaveProcess(this);
             ActivityManager.getService().broadcastIntentWithFeature(
-                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
-                    Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
-                    null, false, false, user.getIdentifier());
+                    mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+                    null, Activity.RESULT_OK, null, null, receiverPermissions,
+                    AppOpsManager.OP_NONE, null, false, false, user.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1173,9 +1173,9 @@
         try {
             intent.prepareToLeaveProcess(this);
             ActivityManager.getService().broadcastIntentWithFeature(
-                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
-                    Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
-                    options, false, false, getUserId());
+                    mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+                    null, Activity.RESULT_OK, null, null, receiverPermissions,
+                    AppOpsManager.OP_NONE, options, false, false, getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1190,9 +1190,9 @@
         try {
             intent.prepareToLeaveProcess(this);
             ActivityManager.getService().broadcastIntentWithFeature(
-                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
-                    Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false, false,
-                    getUserId());
+                    mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+                    null, Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false,
+                    false, getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1207,9 +1207,9 @@
         try {
             intent.prepareToLeaveProcess(this);
             ActivityManager.getService().broadcastIntentWithFeature(
-                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
-                    Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
-                    null, true, false, getUserId());
+                    mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+                    null, Activity.RESULT_OK, null, null, receiverPermissions,
+                    AppOpsManager.OP_NONE, null, true, false, getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1270,8 +1270,8 @@
         try {
             intent.prepareToLeaveProcess(this);
             ActivityManager.getService().broadcastIntentWithFeature(
-                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, rd,
-                initialCode, initialData, initialExtras, receiverPermissions, appOp,
+                    mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+                    rd, initialCode, initialData, initialExtras, receiverPermissions, appOp,
                     options, true, false, getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1284,9 +1284,9 @@
         try {
             intent.prepareToLeaveProcess(this);
             ActivityManager.getService().broadcastIntentWithFeature(
-                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
-                    Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
-                    user.getIdentifier());
+                    mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+                    null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false,
+                    false, user.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1307,9 +1307,9 @@
         try {
             intent.prepareToLeaveProcess(this);
             ActivityManager.getService().broadcastIntentWithFeature(
-                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
-                    Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
-                    options, false, false, user.getIdentifier());
+                    mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+                    null, Activity.RESULT_OK, null, null, receiverPermissions,
+                    AppOpsManager.OP_NONE, options, false, false, user.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1324,9 +1324,9 @@
         try {
             intent.prepareToLeaveProcess(this);
             ActivityManager.getService().broadcastIntentWithFeature(
-                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
-                    Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false, false,
-                    user.getIdentifier());
+                    mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+                    null, Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false,
+                    false, user.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1375,9 +1375,9 @@
         try {
             intent.prepareToLeaveProcess(this);
             ActivityManager.getService().broadcastIntentWithFeature(
-                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, rd,
-                initialCode, initialData, initialExtras, receiverPermissions,
-                    appOp, options, true, false, user.getIdentifier());
+                    mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+                    rd, initialCode, initialData, initialExtras, receiverPermissions, appOp,
+                    options, true, false, user.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1416,9 +1416,9 @@
         try {
             intent.prepareToLeaveProcess(this);
             ActivityManager.getService().broadcastIntentWithFeature(
-                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
-                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true,
-                getUserId());
+                    mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+                    null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false,
+                    true, getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1452,9 +1452,9 @@
         try {
             intent.prepareToLeaveProcess(this);
             ActivityManager.getService().broadcastIntentWithFeature(
-                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, rd,
-                initialCode, initialData, initialExtras, null,
-                    AppOpsManager.OP_NONE, null, true, true, getUserId());
+                    mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+                    rd, initialCode, initialData, initialExtras, null, AppOpsManager.OP_NONE, null,
+                    true, true, getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1484,9 +1484,9 @@
         try {
             intent.prepareToLeaveProcess(this);
             ActivityManager.getService().broadcastIntentWithFeature(
-                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
-                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true,
-                    user.getIdentifier());
+                    mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+                    null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false,
+                    true, user.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1499,9 +1499,9 @@
         try {
             intent.prepareToLeaveProcess(this);
             ActivityManager.getService().broadcastIntentWithFeature(
-                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
-                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, options, false, true,
-                user.getIdentifier());
+                    mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+                    null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, options,
+                    false, true, user.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1534,9 +1534,9 @@
         try {
             intent.prepareToLeaveProcess(this);
             ActivityManager.getService().broadcastIntentWithFeature(
-                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, rd,
-                initialCode, initialData, initialExtras, null,
-                    AppOpsManager.OP_NONE, null, true, true, user.getIdentifier());
+                    mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+                    rd, initialCode, initialData, initialExtras, null, AppOpsManager.OP_NONE, null,
+                    true, true, user.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1620,7 +1620,7 @@
         }
         try {
             final Intent intent = ActivityManager.getService().registerReceiverWithFeature(
-                    mMainThread.getApplicationThread(), mBasePackageName, getFeatureId(), rd,
+                    mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(), rd,
                     filter, broadcastPermission, userId, flags);
             if (intent != null) {
                 intent.setExtrasClassLoader(getClassLoader());
@@ -1696,7 +1696,7 @@
             ComponentName cn = ActivityManager.getService().startService(
                     mMainThread.getApplicationThread(), service,
                     service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
-                    getOpPackageName(), getFeatureId(), user.getIdentifier());
+                    getOpPackageName(), getAttributionTag(), user.getIdentifier());
             if (cn != null) {
                 if (cn.getPackageName().equals("!")) {
                     throw new SecurityException(
@@ -2285,14 +2285,14 @@
         if (packageName.equals("system") || packageName.equals("android")) {
             // The system resources are loaded in every application, so we can safely copy
             // the context without reloading Resources.
-            return new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, null,
+            return new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag, null,
                     mToken, user, flags, null, null);
         }
 
         LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
                 flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
         if (pi != null) {
-            ContextImpl c = new ContextImpl(this, mMainThread, pi, mFeatureId, null,
+            ContextImpl c = new ContextImpl(this, mMainThread, pi, mAttributionTag, null,
                     mToken, user, flags, null, null);
 
             final int displayId = getDisplayId();
@@ -2329,7 +2329,7 @@
         final String[] paths = mPackageInfo.getSplitPaths(splitName);
 
         final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo,
-                mFeatureId, splitName, mToken, mUser, mFlags, classLoader, null);
+                mAttributionTag, splitName, mToken, mUser, mFlags, classLoader, null);
 
         final int displayId = getDisplayId();
 
@@ -2353,7 +2353,7 @@
             throw new IllegalArgumentException("overrideConfiguration must not be null");
         }
 
-        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId,
+        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag,
                 mSplitName, mToken, mUser, mFlags, mClassLoader, null);
 
         final int displayId = getDisplayId();
@@ -2370,7 +2370,7 @@
             throw new IllegalArgumentException("display must not be null");
         }
 
-        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId,
+        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag,
                 mSplitName, mToken, mUser, mFlags, mClassLoader, null);
 
         final int displayId = display.getDisplayId();
@@ -2394,7 +2394,7 @@
     }
 
     ContextImpl createBaseWindowContext(IBinder token) {
-        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId,
+        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag,
                 mSplitName, token, mUser, mFlags, mClassLoader, null);
         context.mIsUiContext = true;
 
@@ -2420,8 +2420,8 @@
     }
 
     @Override
-    public @NonNull Context createFeatureContext(@Nullable String featureId) {
-        return new ContextImpl(this, mMainThread, mPackageInfo, featureId, mSplitName,
+    public @NonNull Context createAttributionContext(@Nullable String attributionTag) {
+        return new ContextImpl(this, mMainThread, mPackageInfo, attributionTag, mSplitName,
                 mToken, mUser, mFlags, mClassLoader, null);
     }
 
@@ -2429,7 +2429,7 @@
     public Context createDeviceProtectedStorageContext() {
         final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE)
                 | Context.CONTEXT_DEVICE_PROTECTED_STORAGE;
-        return new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, mSplitName,
+        return new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag, mSplitName,
                 mToken, mUser, flags, mClassLoader, null);
     }
 
@@ -2437,7 +2437,7 @@
     public Context createCredentialProtectedStorageContext() {
         final int flags = (mFlags & ~Context.CONTEXT_DEVICE_PROTECTED_STORAGE)
                 | Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;
-        return new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, mSplitName,
+        return new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag, mSplitName,
                 mToken, mUser, flags, mClassLoader, null);
     }
 
@@ -2700,7 +2700,7 @@
     }
 
     private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread,
-            @NonNull LoadedApk packageInfo, @Nullable String featureId,
+            @NonNull LoadedApk packageInfo, @Nullable String attributionTag,
             @Nullable String splitName, @Nullable IBinder activityToken, @Nullable UserHandle user,
             int flags, @Nullable ClassLoader classLoader, @Nullable String overrideOpPackageName) {
         mOuterContext = this;
@@ -2754,7 +2754,7 @@
         }
 
         mOpPackageName = overrideOpPackageName != null ? overrideOpPackageName : opPackageName;
-        mFeatureId = featureId;
+        mAttributionTag = attributionTag;
         mContentResolver = new ApplicationContentResolver(this, mainThread);
     }
 
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 28b28da..145d513 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -37,13 +37,15 @@
 
     /**
      * Called whenever IActivityManager.startActivity is called on an activity that is already
-     * running in the pinned stack and the activity is not actually started, but the task is either
-     * brought to the front or a new Intent is delivered to it.
+     * running, but the task is either brought to the front or a new Intent is delivered to it.
      *
+     * @param task information about the task the activity was relaunched into
+     * @param homeVisible whether or not the home task is visible
      * @param clearedTask whether or not the launch activity also cleared the task as a part of
      * starting
      */
-    void onPinnedActivityRestartAttempt(boolean clearedTask);
+    void onActivityRestartAttempt(in ActivityManager.RunningTaskInfo task, boolean homeTaskVisible,
+            boolean clearedTask);
 
     /**
      * Called when we launched an activity that we forced to be resizable.
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 34684c4..4cb8d93 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -175,11 +175,4 @@
      * Called from SystemUI when it shows the AoD UI.
      */
     oneway void setInAmbientMode(boolean inAmbientMode, long animationDuration);
-
-    /**
-     * Called when the wallpaper needs to zoom out.
-     * The zoom value goes from 0 to 1 (inclusive) where 1 means fully zoomed out,
-     * 0 means fully zoomed in
-     */
-    oneway void setWallpaperZoomOut(float zoom, String callingPackage, int displayId);
 }
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 18932c6..818a121 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1720,11 +1720,10 @@
         try {
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(who);
-            int result = ActivityTaskManager.getService()
-                .startActivity(whoThread, who.getBasePackageName(), who.getFeatureId(), intent,
-                        intent.resolveTypeIfNeeded(who.getContentResolver()),
-                        token, target != null ? target.mEmbeddedID : null,
-                        requestCode, 0, null, options);
+            int result = ActivityTaskManager.getService().startActivity(whoThread,
+                    who.getBasePackageName(), who.getAttributionTag(), intent,
+                    intent.resolveTypeIfNeeded(who.getContentResolver()), token,
+                    target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
             checkStartActivityResult(result, intent);
         } catch (RemoteException e) {
             throw new RuntimeException("Failure from system", e);
@@ -1793,9 +1792,9 @@
                 intents[i].prepareToLeaveProcess(who);
                 resolvedTypes[i] = intents[i].resolveTypeIfNeeded(who.getContentResolver());
             }
-            int result = ActivityTaskManager.getService()
-                .startActivities(whoThread, who.getBasePackageName(), who.getFeatureId(), intents,
-                        resolvedTypes, token, options, userId);
+            int result = ActivityTaskManager.getService().startActivities(whoThread,
+                    who.getBasePackageName(), who.getAttributionTag(), intents, resolvedTypes,
+                    token, options, userId);
             checkStartActivityResult(result, intents[0]);
             return result;
         } catch (RemoteException e) {
@@ -1860,10 +1859,10 @@
         try {
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(who);
-            int result = ActivityTaskManager.getService()
-                .startActivity(whoThread, who.getBasePackageName(), who.getFeatureId(), intent,
-                        intent.resolveTypeIfNeeded(who.getContentResolver()),
-                        token, target, requestCode, 0, null, options);
+            int result = ActivityTaskManager.getService().startActivity(whoThread,
+                    who.getBasePackageName(), who.getAttributionTag(), intent,
+                    intent.resolveTypeIfNeeded(who.getContentResolver()), token, target,
+                    requestCode, 0, null, options);
             checkStartActivityResult(result, intent);
         } catch (RemoteException e) {
             throw new RuntimeException("Failure from system", e);
@@ -1927,11 +1926,10 @@
         try {
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(who);
-            int result = ActivityTaskManager.getService()
-                .startActivityAsUser(whoThread, who.getBasePackageName(), who.getFeatureId(),
-                        intent, intent.resolveTypeIfNeeded(who.getContentResolver()),
-                        token, resultWho,
-                        requestCode, 0, null, options, user.getIdentifier());
+            int result = ActivityTaskManager.getService().startActivityAsUser(whoThread,
+                    who.getBasePackageName(), who.getAttributionTag(), intent,
+                    intent.resolveTypeIfNeeded(who.getContentResolver()), token, resultWho,
+                    requestCode, 0, null, options, user.getIdentifier());
             checkStartActivityResult(result, intent);
         } catch (RemoteException e) {
             throw new RuntimeException("Failure from system", e);
@@ -2022,7 +2020,7 @@
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(who);
             int result = appTask.startActivity(whoThread.asBinder(), who.getBasePackageName(),
-                    who.getFeatureId(), intent,
+                    who.getAttributionTag(), intent,
                     intent.resolveTypeIfNeeded(who.getContentResolver()), options);
             checkStartActivityResult(result, intent);
         } catch (RemoteException e) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 32e7d84..061e5ff 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -21,6 +21,7 @@
 import static android.graphics.drawable.Icon.TYPE_URI_ADAPTIVE_BITMAP;
 
 import static com.android.internal.util.ContrastColorUtil.satisfiesTextContrast;
+import static com.android.internal.widget.ConversationLayout.CONVERSATION_LAYOUT_ENABLED;
 
 import android.annotation.ColorInt;
 import android.annotation.DimenRes;
@@ -389,6 +390,7 @@
         STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_text);
         STANDARD_LAYOUTS.add(R.layout.notification_template_material_inbox);
         STANDARD_LAYOUTS.add(R.layout.notification_template_material_messaging);
+        STANDARD_LAYOUTS.add(R.layout.notification_template_material_conversation);
         STANDARD_LAYOUTS.add(R.layout.notification_template_material_media);
         STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_media);
         STANDARD_LAYOUTS.add(R.layout.notification_template_header);
@@ -5138,7 +5140,7 @@
             int color = isColorized(p) ? getPrimaryTextColor(p) : getSecondaryTextColor(p);
             contentView.setDrawableTint(R.id.expand_button, false, color,
                     PorterDuff.Mode.SRC_ATOP);
-            contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
+            contentView.setInt(R.id.expand_button, "setOriginalNotificationColor",
                     color);
         }
 
@@ -6116,7 +6118,9 @@
         }
 
         private int getMessagingLayoutResource() {
-            return R.layout.notification_template_material_messaging;
+            return CONVERSATION_LAYOUT_ENABLED
+                    ? R.layout.notification_template_material_conversation
+                    : R.layout.notification_template_material_messaging;
         }
 
         private int getActionLayoutResource() {
@@ -6221,6 +6225,17 @@
             }
             return loadHeaderAppName();
         }
+
+        /**
+         * @return if this builder uses a template
+         *
+         * @hide
+         */
+        public boolean usesTemplate() {
+            return (mN.contentView == null && mN.headsUpContentView == null
+                    && mN.bigContentView == null)
+                    || (mStyle != null && mStyle.displayCustomViewInline());
+        }
     }
 
     /**
@@ -7379,7 +7394,7 @@
         public RemoteViews makeContentView(boolean increasedHeight) {
             mBuilder.mOriginalActions = mBuilder.mActions;
             mBuilder.mActions = new ArrayList<>();
-            RemoteViews remoteViews = makeMessagingView(true /* displayImagesAtEnd */,
+            RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */,
                     false /* hideLargeIcon */);
             mBuilder.mActions = mBuilder.mOriginalActions;
             mBuilder.mOriginalActions = null;
@@ -7469,19 +7484,18 @@
          */
         @Override
         public RemoteViews makeBigContentView() {
-            return makeMessagingView(false /* displayImagesAtEnd */, true /* hideLargeIcon */);
+            return makeMessagingView(false /* isCollapsed */, true /* hideLargeIcon */);
         }
 
         /**
          * Create a messaging layout.
          *
-         * @param displayImagesAtEnd should images be displayed at the end of the content instead
-         *                           of inline.
+         * @param isCollapsed Should this use the collapsed layout
          * @param hideRightIcons Should the reply affordance be shown at the end of the notification
          * @return the created remoteView.
          */
         @NonNull
-        private RemoteViews makeMessagingView(boolean displayImagesAtEnd, boolean hideRightIcons) {
+        private RemoteViews makeMessagingView(boolean isCollapsed, boolean hideRightIcons) {
             CharSequence conversationTitle = !TextUtils.isEmpty(super.mBigContentTitle)
                     ? super.mBigContentTitle
                     : mConversationTitle;
@@ -7522,14 +7536,21 @@
                     mBuilder.getPrimaryTextColor(p));
             contentView.setInt(R.id.status_bar_latest_event_content, "setMessageTextColor",
                     mBuilder.getSecondaryTextColor(p));
-            contentView.setBoolean(R.id.status_bar_latest_event_content, "setDisplayImagesAtEnd",
-                    displayImagesAtEnd);
+            contentView.setInt(R.id.status_bar_latest_event_content,
+                    "setNotificationBackgroundColor",
+                    mBuilder.resolveBackgroundColor(p));
+            contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsCollapsed",
+                    isCollapsed);
             contentView.setIcon(R.id.status_bar_latest_event_content, "setAvatarReplacement",
                     avatarReplacement);
             contentView.setCharSequence(R.id.status_bar_latest_event_content, "setNameReplacement",
                     nameReplacement);
             contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsOneToOne",
                     isOneToOne);
+            contentView.setCharSequence(R.id.status_bar_latest_event_content,
+                    "setConversationTitle", conversationTitle);
+            contentView.setIcon(R.id.status_bar_latest_event_content, "setLargeIcon",
+                    mBuilder.mN.mLargeIcon);
             contentView.setBundle(R.id.status_bar_latest_event_content, "setData",
                     mBuilder.mN.extras);
             return contentView;
@@ -7590,9 +7611,11 @@
          */
         @Override
         public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
-            RemoteViews remoteViews = makeMessagingView(true /* displayImagesAtEnd */,
+            RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */,
                     true /* hideLargeIcon */);
-            remoteViews.setInt(R.id.notification_messaging, "setMaxDisplayedLines", 1);
+            if (!CONVERSATION_LAYOUT_ENABLED) {
+                remoteViews.setInt(R.id.notification_messaging, "setMaxDisplayedLines", 1);
+            }
             return remoteViews;
         }
 
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 3f2ec44..94b237c 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -134,6 +134,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     public static final int USER_LOCKED_SOUND = 0x00000020;
 
     /**
@@ -331,6 +332,7 @@
     /**
      * @hide
      */
+    @TestApi
     public void lockFields(int field) {
         mUserLockedFields |= field;
     }
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index f68c929..792f840 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -355,8 +355,8 @@
             intent.prepareToLeaveProcess(context);
             IIntentSender target =
                 ActivityManager.getService().getIntentSenderWithFeature(
-                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName, context.getFeatureId(),
-                    null, null, requestCode, new Intent[] { intent },
+                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+                    context.getAttributionTag(), null, null, requestCode, new Intent[] { intent },
                     resolvedType != null ? new String[] { resolvedType } : null,
                     flags, options, context.getUserId());
             return target != null ? new PendingIntent(target) : null;
@@ -381,8 +381,8 @@
             intent.prepareToLeaveProcess(context);
             IIntentSender target =
                 ActivityManager.getService().getIntentSenderWithFeature(
-                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName, context.getFeatureId(),
-                    null, null, requestCode, new Intent[] { intent },
+                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+                    context.getAttributionTag(), null, null, requestCode, new Intent[] { intent },
                     resolvedType != null ? new String[] { resolvedType } : null,
                     flags, options, user.getIdentifier());
             return target != null ? new PendingIntent(target) : null;
@@ -498,9 +498,9 @@
         try {
             IIntentSender target =
                 ActivityManager.getService().getIntentSenderWithFeature(
-                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName, context.getFeatureId(),
-                    null, null, requestCode, intents, resolvedTypes, flags, options,
-                    context.getUserId());
+                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+                    context.getAttributionTag(), null, null, requestCode, intents, resolvedTypes,
+                    flags, options, context.getUserId());
             return target != null ? new PendingIntent(target) : null;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -524,8 +524,8 @@
         try {
             IIntentSender target =
                 ActivityManager.getService().getIntentSenderWithFeature(
-                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName, context.getFeatureId(),
-                    null, null, requestCode, intents, resolvedTypes,
+                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+                    context.getAttributionTag(), null, null, requestCode, intents, resolvedTypes,
                     flags, options, user.getIdentifier());
             return target != null ? new PendingIntent(target) : null;
         } catch (RemoteException e) {
@@ -576,8 +576,8 @@
             intent.prepareToLeaveProcess(context);
             IIntentSender target =
                 ActivityManager.getService().getIntentSenderWithFeature(
-                    ActivityManager.INTENT_SENDER_BROADCAST, packageName, context.getFeatureId(),
-                    null, null, requestCode, new Intent[] { intent },
+                    ActivityManager.INTENT_SENDER_BROADCAST, packageName,
+                    context.getAttributionTag(), null, null, requestCode, new Intent[] { intent },
                     resolvedType != null ? new String[] { resolvedType } : null,
                     flags, null, userHandle.getIdentifier());
             return target != null ? new PendingIntent(target) : null;
@@ -655,7 +655,7 @@
             intent.prepareToLeaveProcess(context);
             IIntentSender target =
                 ActivityManager.getService().getIntentSenderWithFeature(
-                    serviceKind, packageName, context.getFeatureId(),
+                    serviceKind, packageName, context.getAttributionTag(),
                     null, null, requestCode, new Intent[] { intent },
                     resolvedType != null ? new String[] { resolvedType } : null,
                     flags, null, context.getUserId());
diff --git a/core/java/android/app/RuntimeAppOpAccessMessage.java b/core/java/android/app/RuntimeAppOpAccessMessage.java
index a81b8e7..a19f815 100644
--- a/core/java/android/app/RuntimeAppOpAccessMessage.java
+++ b/core/java/android/app/RuntimeAppOpAccessMessage.java
@@ -44,7 +44,7 @@
     /** Name of package for which runtime app op access message was collected */
     private final @NonNull String mPackageName;
     /** Feature of package for which runtime app op access message was collected */
-    private final @Nullable String mFeatureId;
+    private final @Nullable String mAttributionTag;
     /** Message collected (including stacktrace for synchronous ops) */
     private final @NonNull String mMessage;
     /** Sampling strategy used to collect this message. */
@@ -63,8 +63,8 @@
      *   Op code of operation access which was collected
      * @param packageName
      *   Name of package for which runtime app op access message was collected
-     * @param featureId
-     *   Feature of package for which runtime app op access message was collected
+     * @param attributionTag
+     *   Attribution tag for which runtime app op access message was collected
      * @param message
      *   Message collected (including stacktrace for synchronous ops)
      * @param samplingStrategy
@@ -75,7 +75,7 @@
             @IntRange(from = 0L) int uid,
             @IntRange(from = 0L) int opCode,
             @NonNull String packageName,
-            @Nullable String featureId,
+            @Nullable String attributionTag,
             @NonNull String message,
             @AppOpsManager.SamplingStrategy int samplingStrategy) {
         this.mUid = uid;
@@ -90,7 +90,7 @@
         this.mPackageName = packageName;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mPackageName);
-        this.mFeatureId = featureId;
+        this.mAttributionTag = attributionTag;
         this.mMessage = message;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mMessage);
@@ -134,11 +134,11 @@
     }
 
     /**
-     * Feature of package for which runtime app op access message was collected
+     * Attribution tag for which runtime app op access message was collected
      */
     @DataClass.Generated.Member
-    public @Nullable String getFeatureId() {
-        return mFeatureId;
+    public @Nullable String getAttributionTag() {
+        return mAttributionTag;
     }
 
     /**
@@ -164,12 +164,12 @@
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
         byte flg = 0;
-        if (mFeatureId != null) flg |= 0x8;
+        if (mAttributionTag != null) flg |= 0x8;
         dest.writeByte(flg);
         dest.writeInt(mUid);
         dest.writeInt(mOpCode);
         dest.writeString(mPackageName);
-        if (mFeatureId != null) dest.writeString(mFeatureId);
+        if (mAttributionTag != null) dest.writeString(mAttributionTag);
         dest.writeString(mMessage);
         dest.writeInt(mSamplingStrategy);
     }
@@ -189,7 +189,7 @@
         int uid = in.readInt();
         int opCode = in.readInt();
         String packageName = in.readString();
-        String featureId = (flg & 0x8) == 0 ? null : in.readString();
+        String attributionTag = (flg & 0x8) == 0 ? null : in.readString();
         String message = in.readString();
         int samplingStrategy = in.readInt();
 
@@ -205,7 +205,7 @@
         this.mPackageName = packageName;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mPackageName);
-        this.mFeatureId = featureId;
+        this.mAttributionTag = attributionTag;
         this.mMessage = message;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mMessage);
@@ -234,7 +234,7 @@
             time = 1581517099127L,
             codegenVersion = "1.0.14",
             sourceFile = "frameworks/base/core/java/android/app/RuntimeAppOpAccessMessage.java",
-            inputSignatures = "private final @android.annotation.IntRange(from=0L) int mUid\nprivate final @android.annotation.IntRange(from=0L, to=AppOpsManager._NUM_OP - 1) int mOpCode\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.app.AppOpsManager.SamplingStrategy int mSamplingStrategy\npublic @android.annotation.NonNull java.lang.String getOp()\nclass RuntimeAppOpAccessMessage extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false)")*/
+            inputSignatures = "private final @android.annotation.IntRange(from=0L) int mUid\nprivate final @android.annotation.IntRange(from=0L, to=AppOpsManager._NUM_OP - 1) int mOpCode\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mAttributionTag\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.app.AppOpsManager.SamplingStrategy int mSamplingStrategy\npublic @android.annotation.NonNull java.lang.String getOp()\nclass RuntimeAppOpAccessMessage extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false)")*/
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/app/SyncNotedAppOp.java b/core/java/android/app/SyncNotedAppOp.java
index 13b90ca..0a880dc 100644
--- a/core/java/android/app/SyncNotedAppOp.java
+++ b/core/java/android/app/SyncNotedAppOp.java
@@ -43,24 +43,24 @@
 
     /** op code of synchronous appop noted */
     private final @IntRange(from = 0L, to = AppOpsManager._NUM_OP - 1) int mOpCode;
-    /** featureId of synchronous appop noted */
-    private final @Nullable String mFeatureId;
+    /** attributionTag of synchronous appop noted */
+    private final @Nullable String mAttributionTag;
 
     /**
      * Creates a new SyncNotedAppOp.
      *
      * @param opCode
      *   op code of synchronous appop noted
-     * @param featureId
-     *   featureId of synchronous appop noted
+     * @param attributionTag
+     *   attributionTag of synchronous appop noted
      */
-    public SyncNotedAppOp(@IntRange(from = 0L) int opCode, @Nullable String featureId) {
+    public SyncNotedAppOp(@IntRange(from = 0L) int opCode, @Nullable String attributionTag) {
         this.mOpCode = opCode;
         com.android.internal.util.AnnotationValidations.validate(
                 IntRange.class, null, mOpCode,
                 "from", 0,
                 "to", AppOpsManager._NUM_OP - 1);
-        this.mFeatureId = featureId;
+        this.mAttributionTag = attributionTag;
     }
 
     /**
@@ -84,11 +84,11 @@
 
 
     /**
-     * featureId of synchronous appop noted
+     * attributionTag of synchronous appop noted
      */
     @DataClass.Generated.Member
-    public @Nullable String getFeatureId() {
-        return mFeatureId;
+    public @Nullable String getAttributionTag() {
+        return mAttributionTag;
     }
 
     @Override
@@ -105,7 +105,7 @@
         //noinspection PointlessBooleanExpression
         return true
                 && mOpCode == that.mOpCode
-                && java.util.Objects.equals(mFeatureId, that.mFeatureId);
+                && java.util.Objects.equals(mAttributionTag, that.mAttributionTag);
     }
 
     @Override
@@ -116,7 +116,7 @@
 
         int _hash = 1;
         _hash = 31 * _hash + mOpCode;
-        _hash = 31 * _hash + java.util.Objects.hashCode(mFeatureId);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag);
         return _hash;
     }
 
@@ -127,10 +127,10 @@
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
         byte flg = 0;
-        if (mFeatureId != null) flg |= 0x2;
+        if (mAttributionTag != null) flg |= 0x2;
         dest.writeByte(flg);
         dest.writeInt(mOpCode);
-        if (mFeatureId != null) dest.writeString(mFeatureId);
+        if (mAttributionTag != null) dest.writeString(mAttributionTag);
     }
 
     @Override
@@ -146,14 +146,14 @@
 
         byte flg = in.readByte();
         int opCode = in.readInt();
-        String featureId = (flg & 0x2) == 0 ? null : in.readString();
+        String attributionTag = (flg & 0x2) == 0 ? null : in.readString();
 
         this.mOpCode = opCode;
         com.android.internal.util.AnnotationValidations.validate(
                 IntRange.class, null, mOpCode,
                 "from", 0,
                 "to", AppOpsManager._NUM_OP - 1);
-        this.mFeatureId = featureId;
+        this.mAttributionTag = attributionTag;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -176,7 +176,7 @@
             time = 1579188889960L,
             codegenVersion = "1.0.14",
             sourceFile = "frameworks/base/core/java/android/app/SyncNotedAppOp.java",
-            inputSignatures = "private final @android.annotation.IntRange(from=0L, to=AppOpsManager._NUM_OP - 1) int mOpCode\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\npublic @android.annotation.NonNull java.lang.String getOp()\npublic @android.annotation.SystemApi int getOpCode()\nclass SyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genConstructor=false)")*/
+            inputSignatures = "private final @android.annotation.IntRange(from=0L, to=AppOpsManager._NUM_OP - 1) int mOpCode\nprivate final @android.annotation.Nullable java.lang.String mAttributionTag\npublic @android.annotation.NonNull java.lang.String getOp()\npublic @android.annotation.SystemApi int getOpCode()\nclass SyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genConstructor=false)")*/
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index b892b8e..93772de 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManager.TaskSnapshot;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
@@ -53,7 +54,8 @@
 
     @Override
     @UnsupportedAppUsage
-    public void onPinnedActivityRestartAttempt(boolean clearedTask) throws RemoteException {
+    public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
+            boolean clearedTask) throws RemoteException {
     }
 
     @Override
@@ -68,14 +70,14 @@
     }
 
     @Override
-    public void onActivityLaunchOnSecondaryDisplayFailed(ActivityManager.RunningTaskInfo taskInfo,
+    public void onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo taskInfo,
             int requestedDisplayId) throws RemoteException {
         onActivityLaunchOnSecondaryDisplayFailed();
     }
 
     /**
      * @deprecated see {@link
-     *         #onActivityLaunchOnSecondaryDisplayFailed(ActivityManager.RunningTaskInfo, int)}
+     *         #onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo, int)}
      */
     @Deprecated
     @UnsupportedAppUsage
@@ -84,7 +86,7 @@
 
     @Override
     @UnsupportedAppUsage
-    public void onActivityLaunchOnSecondaryDisplayRerouted(ActivityManager.RunningTaskInfo taskInfo,
+    public void onActivityLaunchOnSecondaryDisplayRerouted(RunningTaskInfo taskInfo,
             int requestedDisplayId) throws RemoteException {
     }
 
@@ -98,13 +100,13 @@
     }
 
     @Override
-    public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo)
+    public void onTaskMovedToFront(RunningTaskInfo taskInfo)
             throws RemoteException {
         onTaskMovedToFront(taskInfo.taskId);
     }
 
     /**
-     * @deprecated see {@link #onTaskMovedToFront(ActivityManager.RunningTaskInfo)}
+     * @deprecated see {@link #onTaskMovedToFront(RunningTaskInfo)}
      */
     @Deprecated
     @UnsupportedAppUsage
@@ -112,26 +114,26 @@
     }
 
     @Override
-    public void onTaskRemovalStarted(ActivityManager.RunningTaskInfo taskInfo)
+    public void onTaskRemovalStarted(RunningTaskInfo taskInfo)
             throws RemoteException {
         onTaskRemovalStarted(taskInfo.taskId);
     }
 
     /**
-     * @deprecated see {@link #onTaskRemovalStarted(ActivityManager.RunningTaskInfo)}
+     * @deprecated see {@link #onTaskRemovalStarted(RunningTaskInfo)}
      */
     @Deprecated
     public void onTaskRemovalStarted(int taskId) throws RemoteException {
     }
 
     @Override
-    public void onTaskDescriptionChanged(ActivityManager.RunningTaskInfo taskInfo)
+    public void onTaskDescriptionChanged(RunningTaskInfo taskInfo)
             throws RemoteException {
         onTaskDescriptionChanged(taskInfo.taskId, taskInfo.taskDescription);
     }
 
     /**
-     * @deprecated see {@link #onTaskDescriptionChanged(ActivityManager.RunningTaskInfo)}
+     * @deprecated see {@link #onTaskDescriptionChanged(RunningTaskInfo)}
      */
     @Deprecated
     public void onTaskDescriptionChanged(int taskId, ActivityManager.TaskDescription td)
@@ -166,7 +168,7 @@
     }
 
     @Override
-    public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo)
+    public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo)
             throws RemoteException {
     }
 
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index d9405e1..1b1568a 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -478,7 +478,7 @@
             try {
                 Bundle params = new Bundle();
                 ParcelFileDescriptor pfd = mService.getWallpaperWithFeature(
-                        context.getOpPackageName(), context.getFeatureId(), this, FLAG_SYSTEM,
+                        context.getOpPackageName(), context.getAttributionTag(), this, FLAG_SYSTEM,
                         params, userId);
 
                 if (pfd != null) {
@@ -1069,7 +1069,7 @@
             try {
                 Bundle outParams = new Bundle();
                 return sGlobals.mService.getWallpaperWithFeature(mContext.getOpPackageName(),
-                        mContext.getFeatureId(), null, which, outParams, userId);
+                        mContext.getAttributionTag(), null, which, outParams, userId);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             } catch (SecurityException e) {
@@ -1858,13 +1858,12 @@
      *
      * @hide
      */
-    public void setWallpaperZoomOut(float zoom) {
+    public void setWallpaperZoomOut(IBinder windowToken, float zoom) {
         if (zoom < 0 || zoom > 1f) {
             throw new IllegalArgumentException("zoom must be between 0 and one: " + zoom);
         }
         try {
-            sGlobals.mService.setWallpaperZoomOut(zoom, mContext.getOpPackageName(),
-                    mContext.getDisplayId());
+            WindowManagerGlobal.getWindowSession().setWallpaperZoomOut(windowToken, zoom);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index c532279..347648a 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6001,7 +6001,7 @@
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param required Whether auto time is set required or not.
      * @throws SecurityException if {@code admin} is not a device owner.
-     * @deprecated From {@link android.os.Build.VERSION_CODES#R}. Use {@link #setAutoTime}
+     * @deprecated From {@link android.os.Build.VERSION_CODES#R}. Use {@link #setAutoTimeEnabled}
      * to turn auto time on or off and use {@link UserManager#DISALLOW_CONFIG_DATE_TIME}
      * to prevent the user from changing this setting.
      */
@@ -6019,7 +6019,7 @@
 
     /**
      * @return true if auto time is required.
-     * @deprecated From {@link android.os.Build.VERSION_CODES#R}. Use {@link #getAutoTime}
+     * @deprecated From {@link android.os.Build.VERSION_CODES#R}. Use {@link #getAutoTimeEnabled}
      */
     @Deprecated
     public boolean getAutoTimeRequired() {
@@ -6049,10 +6049,10 @@
      * @throws SecurityException if caller is not a device owner, a profile owner for the
      * primary user, or a profile owner of an organization-owned managed profile.
      */
-    public void setAutoTime(@NonNull ComponentName admin, boolean enabled) {
+    public void setAutoTimeEnabled(@NonNull ComponentName admin, boolean enabled) {
         if (mService != null) {
             try {
-                mService.setAutoTime(admin, enabled);
+                mService.setAutoTimeEnabled(admin, enabled);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -6064,10 +6064,10 @@
      * @throws SecurityException if caller is not a device owner, a profile owner for the
      * primary user, or a profile owner of an organization-owned managed profile.
      */
-    public boolean getAutoTime(@NonNull ComponentName admin) {
+    public boolean getAutoTimeEnabled(@NonNull ComponentName admin) {
         if (mService != null) {
             try {
-                return mService.getAutoTime(admin);
+                return mService.getAutoTimeEnabled(admin);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -6090,11 +6090,11 @@
      * @throws SecurityException if caller is not a device owner, a profile owner for the
      * primary user, or a profile owner of an organization-owned managed profile.
      */
-    public void setAutoTimeZone(@NonNull ComponentName admin, boolean enabled) {
+    public void setAutoTimeZoneEnabled(@NonNull ComponentName admin, boolean enabled) {
         throwIfParentInstance("setAutoTimeZone");
         if (mService != null) {
             try {
-                mService.setAutoTimeZone(admin, enabled);
+                mService.setAutoTimeZoneEnabled(admin, enabled);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -6106,11 +6106,11 @@
      * @throws SecurityException if caller is not a device owner, a profile owner for the
      * primary user, or a profile owner of an organization-owned managed profile.
      */
-    public boolean getAutoTimeZone(@NonNull ComponentName admin) {
+    public boolean getAutoTimeZoneEnabled(@NonNull ComponentName admin) {
         throwIfParentInstance("getAutoTimeZone");
         if (mService != null) {
             try {
-                return mService.getAutoTimeZone(admin);
+                return mService.getAutoTimeZoneEnabled(admin);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -8804,10 +8804,11 @@
      * <p>
      * The following settings used to be supported, but can be controlled in other ways:
      * <ul>
-     * <li>{@link android.provider.Settings.Global#AUTO_TIME} : Use {@link #setAutoTime} and
+     * <li>{@link android.provider.Settings.Global#AUTO_TIME} : Use {@link #setAutoTimeEnabled} and
      * {@link UserManager#DISALLOW_CONFIG_DATE_TIME} instead.</li>
-     * <li>{@link android.provider.Settings.Global#AUTO_TIME_ZONE} : Use {@link #setAutoTimeZone}
-     * and {@link UserManager#DISALLOW_CONFIG_DATE_TIME} instead.</li>
+     * <li>{@link android.provider.Settings.Global#AUTO_TIME_ZONE} : Use
+     * {@link #setAutoTimeZoneEnabled} and {@link UserManager#DISALLOW_CONFIG_DATE_TIME}
+     * instead.</li>
      * <li>{@link android.provider.Settings.Global#DATA_ROAMING} : Use
      * {@link UserManager#DISALLOW_DATA_ROAMING} instead.</li>
      * </ul>
@@ -11984,17 +11985,17 @@
      * must handle this intent.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with
-     * @param timeoutMs Maximum time the profile is allowed to be off in milliseconds or 0 if
+     * @param timeoutMillis Maximum time the profile is allowed to be off in milliseconds or 0 if
      *        not limited.
      * @throws IllegalStateException if the profile owner doesn't have an activity that handles
      *        {@link #ACTION_CHECK_POLICY_COMPLIANCE}
      * @see #setPersonalAppsSuspended
      */
-    public void setManagedProfileMaximumTimeOff(@NonNull ComponentName admin, long timeoutMs) {
+    public void setManagedProfileMaximumTimeOff(@NonNull ComponentName admin, long timeoutMillis) {
         throwIfParentInstance("setManagedProfileMaximumTimeOff");
         if (mService != null) {
             try {
-                mService.setManagedProfileMaximumTimeOff(admin, timeoutMs);
+                mService.setManagedProfileMaximumTimeOff(admin, timeoutMillis);
             } catch (RemoteException re) {
                 throw re.rethrowFromSystemServer();
             }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index da48663..f071239 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -308,11 +308,11 @@
     void setAutoTimeRequired(in ComponentName who, boolean required);
     boolean getAutoTimeRequired();
 
-    void setAutoTime(in ComponentName who, boolean enabled);
-    boolean getAutoTime(in ComponentName who);
+    void setAutoTimeEnabled(in ComponentName who, boolean enabled);
+    boolean getAutoTimeEnabled(in ComponentName who);
 
-    void setAutoTimeZone(in ComponentName who, boolean enabled);
-    boolean getAutoTimeZone(in ComponentName who);
+    void setAutoTimeZoneEnabled(in ComponentName who, boolean enabled);
+    boolean getAutoTimeZoneEnabled(in ComponentName who);
 
     void setForceEphemeralUsers(in ComponentName who, boolean forceEpehemeralUsers);
     boolean getForceEphemeralUsers(in ComponentName who);
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index db4f1de..917eeb8 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -636,7 +636,6 @@
      * @hide
      */
     @Nullable
-    @SystemApi
     public String getDefaultSmsPackage(@UserIdInt int userId) {
         try {
             return mService.getDefaultSmsPackage(userId);
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index ccd8199..0d461f5 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -174,7 +174,7 @@
     public static final String ACTION_APPWIDGET_CONFIGURE = "android.appwidget.action.APPWIDGET_CONFIGURE";
 
     /**
-     * An intent extra that contains one appWidgetId.
+     * An intent extra (int) that contains one appWidgetId.
      * <p>
      * The value will be an int that can be retrieved like this:
      * {@sample frameworks/base/tests/appwidgets/AppWidgetHostTest/src/com/android/tests/appwidgethost/AppWidgetHostActivity.java getExtra_EXTRA_APPWIDGET_ID}
@@ -182,7 +182,7 @@
     public static final String EXTRA_APPWIDGET_ID = "appWidgetId";
 
     /**
-     * A bundle extra that contains whether or not an app has finished restoring a widget.
+     * A bundle extra (boolean) that contains whether or not an app has finished restoring a widget.
      * <p> After restore, the app should set OPTION_APPWIDGET_RESTORE_COMPLETED to true on its
      * widgets followed by calling {@link #updateAppWidget} to update the views.
      *
@@ -192,22 +192,26 @@
 
 
     /**
-     * A bundle extra that contains the lower bound on the current width, in dips, of a widget instance.
+     * A bundle extra (int) that contains the lower bound on the current width, in dips, of a
+     * widget instance.
      */
     public static final String OPTION_APPWIDGET_MIN_WIDTH = "appWidgetMinWidth";
 
     /**
-     * A bundle extra that contains the lower bound on the current height, in dips, of a widget instance.
+     * A bundle extra (int) that contains the lower bound on the current height, in dips, of a
+     * widget instance.
      */
     public static final String OPTION_APPWIDGET_MIN_HEIGHT = "appWidgetMinHeight";
 
     /**
-     * A bundle extra that contains the upper bound on the current width, in dips, of a widget instance.
+     * A bundle extra (int) that contains the upper bound on the current width, in dips, of a
+     * widget instance.
      */
     public static final String OPTION_APPWIDGET_MAX_WIDTH = "appWidgetMaxWidth";
 
     /**
-     * A bundle extra that contains the upper bound on the current width, in dips, of a widget instance.
+     * A bundle extra (int) that contains the upper bound on the current width, in dips, of a
+     * widget instance.
      */
     public static final String OPTION_APPWIDGET_MAX_HEIGHT = "appWidgetMaxHeight";
 
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 6ae68fc..608b563 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -849,7 +849,7 @@
         synchronized (mLock) {
             if (sBluetoothLeScanner == null) {
                 sBluetoothLeScanner = new BluetoothLeScanner(mManagerService, getOpPackageName(),
-                        getFeatureId());
+                        getAttributionTag());
             }
         }
         return sBluetoothLeScanner;
@@ -1663,11 +1663,11 @@
         return ActivityThread.currentOpPackageName();
     }
 
-    private String getFeatureId() {
+    private String getAttributionTag() {
         // Workaround for legacy API for getting a BluetoothAdapter not
         // passing a context
         if (mContext != null) {
-            return mContext.getFeatureId();
+            return mContext.getAttributionTag();
         }
         return null;
     }
@@ -1709,7 +1709,7 @@
         try {
             mServiceLock.readLock().lock();
             if (mService != null) {
-                return mService.startDiscovery(getOpPackageName(), getFeatureId());
+                return mService.startDiscovery(getOpPackageName(), getAttributionTag());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index 7ff6466..3b4fe0a 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -62,7 +62,7 @@
      * @hide
      */
     public BluetoothManager(Context context) {
-        if (context.getFeatureId() == null) {
+        if (context.getAttributionTag() == null) {
             context = context.getApplicationContext();
             if (context == null) {
                 throw new IllegalArgumentException(
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index bd3298c7..d8e8b27 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -230,12 +230,12 @@
         }
 
         @Override
-        public Cursor query(String callingPkg, @Nullable String featureId, Uri uri,
+        public Cursor query(String callingPkg, @Nullable String attributionTag, Uri uri,
                 @Nullable String[] projection, @Nullable Bundle queryArgs,
                 @Nullable ICancellationSignal cancellationSignal) {
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
-            if (enforceReadPermission(callingPkg, featureId, uri, null)
+            if (enforceReadPermission(callingPkg, attributionTag, uri, null)
                     != AppOpsManager.MODE_ALLOWED) {
                 // The caller has no access to the data, so return an empty cursor with
                 // the columns in the requested order. The caller may ask for an invalid
@@ -253,7 +253,7 @@
                 // columns. We then use the column names to return an empty cursor.
                 Cursor cursor;
                 final Pair<String, String> original = setCallingPackage(
-                        new Pair<>(callingPkg, featureId));
+                        new Pair<>(callingPkg, attributionTag));
                 try {
                     cursor = mInterface.query(
                             uri, projection, queryArgs,
@@ -272,7 +272,7 @@
             }
             Trace.traceBegin(TRACE_TAG_DATABASE, "query");
             final Pair<String, String> original = setCallingPackage(
-                    new Pair<>(callingPkg, featureId));
+                    new Pair<>(callingPkg, attributionTag));
             try {
                 return mInterface.query(
                         uri, projection, queryArgs,
@@ -308,15 +308,15 @@
         }
 
         @Override
-        public Uri insert(String callingPkg, @Nullable String featureId, Uri uri,
+        public Uri insert(String callingPkg, @Nullable String attributionTag, Uri uri,
                 ContentValues initialValues, Bundle extras) {
             uri = validateIncomingUri(uri);
             int userId = getUserIdFromUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
-            if (enforceWritePermission(callingPkg, featureId, uri, null)
+            if (enforceWritePermission(callingPkg, attributionTag, uri, null)
                     != AppOpsManager.MODE_ALLOWED) {
                 final Pair<String, String> original = setCallingPackage(
-                        new Pair<>(callingPkg, featureId));
+                        new Pair<>(callingPkg, attributionTag));
                 try {
                     return rejectInsert(uri, initialValues);
                 } finally {
@@ -325,7 +325,7 @@
             }
             Trace.traceBegin(TRACE_TAG_DATABASE, "insert");
             final Pair<String, String> original = setCallingPackage(
-                    new Pair<>(callingPkg, featureId));
+                    new Pair<>(callingPkg, attributionTag));
             try {
                 return maybeAddUserId(mInterface.insert(uri, initialValues, extras), userId);
             } catch (RemoteException e) {
@@ -337,17 +337,17 @@
         }
 
         @Override
-        public int bulkInsert(String callingPkg, @Nullable String featureId, Uri uri,
+        public int bulkInsert(String callingPkg, @Nullable String attributionTag, Uri uri,
                 ContentValues[] initialValues) {
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
-            if (enforceWritePermission(callingPkg, featureId, uri, null)
+            if (enforceWritePermission(callingPkg, attributionTag, uri, null)
                     != AppOpsManager.MODE_ALLOWED) {
                 return 0;
             }
             Trace.traceBegin(TRACE_TAG_DATABASE, "bulkInsert");
             final Pair<String, String> original = setCallingPackage(
-                    new Pair<>(callingPkg, featureId));
+                    new Pair<>(callingPkg, attributionTag));
             try {
                 return mInterface.bulkInsert(uri, initialValues);
             } catch (RemoteException e) {
@@ -359,8 +359,9 @@
         }
 
         @Override
-        public ContentProviderResult[] applyBatch(String callingPkg, @Nullable String featureId,
-                String authority, ArrayList<ContentProviderOperation> operations)
+        public ContentProviderResult[] applyBatch(String callingPkg,
+                @Nullable String attributionTag, String authority,
+                ArrayList<ContentProviderOperation> operations)
                 throws OperationApplicationException {
             validateIncomingAuthority(authority);
             int numOperations = operations.size();
@@ -377,13 +378,13 @@
                     operations.set(i, operation);
                 }
                 if (operation.isReadOperation()) {
-                    if (enforceReadPermission(callingPkg, featureId, uri, null)
+                    if (enforceReadPermission(callingPkg, attributionTag, uri, null)
                             != AppOpsManager.MODE_ALLOWED) {
                         throw new OperationApplicationException("App op not allowed", 0);
                     }
                 }
                 if (operation.isWriteOperation()) {
-                    if (enforceWritePermission(callingPkg, featureId, uri, null)
+                    if (enforceWritePermission(callingPkg, attributionTag, uri, null)
                             != AppOpsManager.MODE_ALLOWED) {
                         throw new OperationApplicationException("App op not allowed", 0);
                     }
@@ -391,7 +392,7 @@
             }
             Trace.traceBegin(TRACE_TAG_DATABASE, "applyBatch");
             final Pair<String, String> original = setCallingPackage(
-                    new Pair<>(callingPkg, featureId));
+                    new Pair<>(callingPkg, attributionTag));
             try {
                 ContentProviderResult[] results = mInterface.applyBatch(authority,
                         operations);
@@ -413,16 +414,17 @@
         }
 
         @Override
-        public int delete(String callingPkg, @Nullable String featureId, Uri uri, Bundle extras) {
+        public int delete(String callingPkg, @Nullable String attributionTag, Uri uri,
+                Bundle extras) {
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
-            if (enforceWritePermission(callingPkg, featureId, uri, null)
+            if (enforceWritePermission(callingPkg, attributionTag, uri, null)
                     != AppOpsManager.MODE_ALLOWED) {
                 return 0;
             }
             Trace.traceBegin(TRACE_TAG_DATABASE, "delete");
             final Pair<String, String> original = setCallingPackage(
-                    new Pair<>(callingPkg, featureId));
+                    new Pair<>(callingPkg, attributionTag));
             try {
                 return mInterface.delete(uri, extras);
             } catch (RemoteException e) {
@@ -434,17 +436,17 @@
         }
 
         @Override
-        public int update(String callingPkg, @Nullable String featureId, Uri uri,
+        public int update(String callingPkg, @Nullable String attributionTag, Uri uri,
                 ContentValues values, Bundle extras) {
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
-            if (enforceWritePermission(callingPkg, featureId, uri, null)
+            if (enforceWritePermission(callingPkg, attributionTag, uri, null)
                     != AppOpsManager.MODE_ALLOWED) {
                 return 0;
             }
             Trace.traceBegin(TRACE_TAG_DATABASE, "update");
             final Pair<String, String> original = setCallingPackage(
-                    new Pair<>(callingPkg, featureId));
+                    new Pair<>(callingPkg, attributionTag));
             try {
                 return mInterface.update(uri, values, extras);
             } catch (RemoteException e) {
@@ -456,15 +458,15 @@
         }
 
         @Override
-        public ParcelFileDescriptor openFile(String callingPkg, @Nullable String featureId,
+        public ParcelFileDescriptor openFile(String callingPkg, @Nullable String attributionTag,
                 Uri uri, String mode, ICancellationSignal cancellationSignal, IBinder callerToken)
                 throws FileNotFoundException {
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
-            enforceFilePermission(callingPkg, featureId, uri, mode, callerToken);
+            enforceFilePermission(callingPkg, attributionTag, uri, mode, callerToken);
             Trace.traceBegin(TRACE_TAG_DATABASE, "openFile");
             final Pair<String, String> original = setCallingPackage(
-                    new Pair<>(callingPkg, featureId));
+                    new Pair<>(callingPkg, attributionTag));
             try {
                 return mInterface.openFile(
                         uri, mode, CancellationSignal.fromTransport(cancellationSignal));
@@ -477,15 +479,15 @@
         }
 
         @Override
-        public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String featureId,
+        public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String attributionTag,
                 Uri uri, String mode, ICancellationSignal cancellationSignal)
                 throws FileNotFoundException {
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
-            enforceFilePermission(callingPkg, featureId, uri, mode, null);
+            enforceFilePermission(callingPkg, attributionTag, uri, mode, null);
             Trace.traceBegin(TRACE_TAG_DATABASE, "openAssetFile");
             final Pair<String, String> original = setCallingPackage(
-                    new Pair<>(callingPkg, featureId));
+                    new Pair<>(callingPkg, attributionTag));
             try {
                 return mInterface.openAssetFile(
                         uri, mode, CancellationSignal.fromTransport(cancellationSignal));
@@ -498,13 +500,13 @@
         }
 
         @Override
-        public Bundle call(String callingPkg, @Nullable String featureId, String authority,
+        public Bundle call(String callingPkg, @Nullable String attributionTag, String authority,
                 String method, @Nullable String arg, @Nullable Bundle extras) {
             validateIncomingAuthority(authority);
             Bundle.setDefusable(extras, true);
             Trace.traceBegin(TRACE_TAG_DATABASE, "call");
             final Pair<String, String> original = setCallingPackage(
-                    new Pair<>(callingPkg, featureId));
+                    new Pair<>(callingPkg, attributionTag));
             try {
                 return mInterface.call(authority, method, arg, extras);
             } catch (RemoteException e) {
@@ -532,15 +534,15 @@
 
         @Override
         public AssetFileDescriptor openTypedAssetFile(String callingPkg,
-                @Nullable String featureId, Uri uri, String mimeType, Bundle opts,
+                @Nullable String attributionTag, Uri uri, String mimeType, Bundle opts,
                 ICancellationSignal cancellationSignal) throws FileNotFoundException {
             Bundle.setDefusable(opts, true);
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
-            enforceFilePermission(callingPkg, featureId, uri, "r", null);
+            enforceFilePermission(callingPkg, attributionTag, uri, "r", null);
             Trace.traceBegin(TRACE_TAG_DATABASE, "openTypedAssetFile");
             final Pair<String, String> original = setCallingPackage(
-                    new Pair<>(callingPkg, featureId));
+                    new Pair<>(callingPkg, attributionTag));
             try {
                 return mInterface.openTypedAssetFile(
                         uri, mimeType, opts, CancellationSignal.fromTransport(cancellationSignal));
@@ -558,17 +560,17 @@
         }
 
         @Override
-        public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri) {
+        public Uri canonicalize(String callingPkg, @Nullable String attributionTag, Uri uri) {
             uri = validateIncomingUri(uri);
             int userId = getUserIdFromUri(uri);
             uri = getUriWithoutUserId(uri);
-            if (enforceReadPermission(callingPkg, featureId, uri, null)
+            if (enforceReadPermission(callingPkg, attributionTag, uri, null)
                     != AppOpsManager.MODE_ALLOWED) {
                 return null;
             }
             Trace.traceBegin(TRACE_TAG_DATABASE, "canonicalize");
             final Pair<String, String> original = setCallingPackage(
-                    new Pair<>(callingPkg, featureId));
+                    new Pair<>(callingPkg, attributionTag));
             try {
                 return maybeAddUserId(mInterface.canonicalize(uri), userId);
             } catch (RemoteException e) {
@@ -580,26 +582,26 @@
         }
 
         @Override
-        public void canonicalizeAsync(String callingPkg, @Nullable String featureId, Uri uri,
+        public void canonicalizeAsync(String callingPkg, @Nullable String attributionTag, Uri uri,
                 RemoteCallback callback) {
             final Bundle result = new Bundle();
             result.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
-                    canonicalize(callingPkg, featureId, uri));
+                    canonicalize(callingPkg, attributionTag, uri));
             callback.sendResult(result);
         }
 
         @Override
-        public Uri uncanonicalize(String callingPkg, String featureId,  Uri uri) {
+        public Uri uncanonicalize(String callingPkg, String attributionTag,  Uri uri) {
             uri = validateIncomingUri(uri);
             int userId = getUserIdFromUri(uri);
             uri = getUriWithoutUserId(uri);
-            if (enforceReadPermission(callingPkg, featureId, uri, null)
+            if (enforceReadPermission(callingPkg, attributionTag, uri, null)
                     != AppOpsManager.MODE_ALLOWED) {
                 return null;
             }
             Trace.traceBegin(TRACE_TAG_DATABASE, "uncanonicalize");
             final Pair<String, String> original = setCallingPackage(
-                    new Pair<>(callingPkg, featureId));
+                    new Pair<>(callingPkg, attributionTag));
             try {
                 return maybeAddUserId(mInterface.uncanonicalize(uri), userId);
             } catch (RemoteException e) {
@@ -611,17 +613,17 @@
         }
 
         @Override
-        public boolean refresh(String callingPkg, String featureId, Uri uri, Bundle extras,
+        public boolean refresh(String callingPkg, String attributionTag, Uri uri, Bundle extras,
                 ICancellationSignal cancellationSignal) throws RemoteException {
             uri = validateIncomingUri(uri);
             uri = getUriWithoutUserId(uri);
-            if (enforceReadPermission(callingPkg, featureId, uri, null)
+            if (enforceReadPermission(callingPkg, attributionTag, uri, null)
                     != AppOpsManager.MODE_ALLOWED) {
                 return false;
             }
             Trace.traceBegin(TRACE_TAG_DATABASE, "refresh");
             final Pair<String, String> original = setCallingPackage(
-                    new Pair<>(callingPkg, featureId));
+                    new Pair<>(callingPkg, attributionTag));
             try {
                 return mInterface.refresh(uri, extras,
                         CancellationSignal.fromTransport(cancellationSignal));
@@ -632,13 +634,13 @@
         }
 
         @Override
-        public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri uri,
+        public int checkUriPermission(String callingPkg, @Nullable String attributionTag, Uri uri,
                 int uid, int modeFlags) {
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
             Trace.traceBegin(TRACE_TAG_DATABASE, "checkUriPermission");
             final Pair<String, String> original = setCallingPackage(
-                    new Pair<>(callingPkg, featureId));
+                    new Pair<>(callingPkg, attributionTag));
             try {
                 return mInterface.checkUriPermission(uri, uid, modeFlags);
             } catch (RemoteException e) {
@@ -649,47 +651,50 @@
             }
         }
 
-        private void enforceFilePermission(String callingPkg, @Nullable String featureId, Uri uri,
-                String mode, IBinder callerToken) throws FileNotFoundException, SecurityException {
+        private void enforceFilePermission(String callingPkg, @Nullable String attributionTag,
+                Uri uri, String mode, IBinder callerToken)
+                throws FileNotFoundException, SecurityException {
             if (mode != null && mode.indexOf('w') != -1) {
-                if (enforceWritePermission(callingPkg, featureId, uri, callerToken)
+                if (enforceWritePermission(callingPkg, attributionTag, uri, callerToken)
                         != AppOpsManager.MODE_ALLOWED) {
                     throw new FileNotFoundException("App op not allowed");
                 }
             } else {
-                if (enforceReadPermission(callingPkg, featureId, uri, callerToken)
+                if (enforceReadPermission(callingPkg, attributionTag, uri, callerToken)
                         != AppOpsManager.MODE_ALLOWED) {
                     throw new FileNotFoundException("App op not allowed");
                 }
             }
         }
 
-        private int enforceReadPermission(String callingPkg, @Nullable String featureId, Uri uri,
-                IBinder callerToken)
+        private int enforceReadPermission(String callingPkg, @Nullable String attributionTag,
+                Uri uri, IBinder callerToken)
                 throws SecurityException {
-            final int mode = enforceReadPermissionInner(uri, callingPkg, featureId, callerToken);
+            final int mode = enforceReadPermissionInner(uri, callingPkg, attributionTag,
+                    callerToken);
             if (mode != MODE_ALLOWED) {
                 return mode;
             }
 
-            return noteProxyOp(callingPkg, featureId, mReadOp);
+            return noteProxyOp(callingPkg, attributionTag, mReadOp);
         }
 
-        private int enforceWritePermission(String callingPkg, String featureId, Uri uri,
+        private int enforceWritePermission(String callingPkg, String attributionTag, Uri uri,
                 IBinder callerToken)
                 throws SecurityException {
-            final int mode = enforceWritePermissionInner(uri, callingPkg, featureId, callerToken);
+            final int mode = enforceWritePermissionInner(uri, callingPkg, attributionTag,
+                    callerToken);
             if (mode != MODE_ALLOWED) {
                 return mode;
             }
 
-            return noteProxyOp(callingPkg, featureId, mWriteOp);
+            return noteProxyOp(callingPkg, attributionTag, mWriteOp);
         }
 
-        private int noteProxyOp(String callingPkg, String featureId, int op) {
+        private int noteProxyOp(String callingPkg, String attributionTag, int op) {
             if (op != AppOpsManager.OP_NONE) {
                 int mode = mAppOpsManager.noteProxyOp(op, callingPkg, Binder.getCallingUid(),
-                        featureId, null);
+                        attributionTag, null);
                 return mode == MODE_DEFAULT ? MODE_IGNORED : mode;
             }
 
@@ -711,19 +716,19 @@
      * associated with that permission.
      */
     private int checkPermissionAndAppOp(String permission, String callingPkg,
-            @Nullable String featureId, IBinder callerToken) {
+            @Nullable String attributionTag, IBinder callerToken) {
         if (getContext().checkPermission(permission, Binder.getCallingPid(), Binder.getCallingUid(),
                 callerToken) != PERMISSION_GRANTED) {
             return MODE_ERRORED;
         }
 
-        return mTransport.noteProxyOp(callingPkg, featureId,
+        return mTransport.noteProxyOp(callingPkg, attributionTag,
                 AppOpsManager.permissionToOpCode(permission));
     }
 
     /** {@hide} */
     protected int enforceReadPermissionInner(Uri uri, String callingPkg,
-            @Nullable String featureId, IBinder callerToken) throws SecurityException {
+            @Nullable String attributionTag, IBinder callerToken) throws SecurityException {
         final Context context = getContext();
         final int pid = Binder.getCallingPid();
         final int uid = Binder.getCallingUid();
@@ -737,7 +742,7 @@
         if (mExported && checkUser(pid, uid, context)) {
             final String componentPerm = getReadPermission();
             if (componentPerm != null) {
-                final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, featureId,
+                final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, attributionTag,
                         callerToken);
                 if (mode == MODE_ALLOWED) {
                     return MODE_ALLOWED;
@@ -757,8 +762,8 @@
                 for (PathPermission pp : pps) {
                     final String pathPerm = pp.getReadPermission();
                     if (pathPerm != null && pp.match(path)) {
-                        final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, featureId,
-                                callerToken);
+                        final int mode = checkPermissionAndAppOp(pathPerm, callingPkg,
+                                attributionTag, callerToken);
                         if (mode == MODE_ALLOWED) {
                             return MODE_ALLOWED;
                         } else {
@@ -807,7 +812,7 @@
 
     /** {@hide} */
     protected int enforceWritePermissionInner(Uri uri, String callingPkg,
-            @Nullable String featureId, IBinder callerToken) throws SecurityException {
+            @Nullable String attributionTag, IBinder callerToken) throws SecurityException {
         final Context context = getContext();
         final int pid = Binder.getCallingPid();
         final int uid = Binder.getCallingUid();
@@ -821,8 +826,8 @@
         if (mExported && checkUser(pid, uid, context)) {
             final String componentPerm = getWritePermission();
             if (componentPerm != null) {
-                final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, featureId,
-                        callerToken);
+                final int mode = checkPermissionAndAppOp(componentPerm, callingPkg,
+                        attributionTag, callerToken);
                 if (mode == MODE_ALLOWED) {
                     return MODE_ALLOWED;
                 } else {
@@ -841,8 +846,8 @@
                 for (PathPermission pp : pps) {
                     final String pathPerm = pp.getWritePermission();
                     if (pathPerm != null && pp.match(path)) {
-                        final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, featureId,
-                                callerToken);
+                        final int mode = checkPermissionAndAppOp(pathPerm, callingPkg,
+                                attributionTag, callerToken);
                         if (mode == MODE_ALLOWED) {
                             return MODE_ALLOWED;
                         } else {
@@ -943,16 +948,16 @@
     }
 
     /**
-     * Return the feature in the package of the caller that initiated the request being
+     * Return the attribution tag of the caller that initiated the request being
      * processed on the current thread. Returns {@code null} if not currently processing
-     * a request of the request is for the default feature.
+     * a request of the request is for the default attribution.
      * <p>
      * This will always return {@code null} when processing
      * {@link #getType(Uri)} or {@link #getStreamTypes(Uri, String)} requests.
      *
      * @see #getCallingPackage
      */
-    public final @Nullable String getCallingFeatureId() {
+    public final @Nullable String getCallingAttributionTag() {
         final Pair<String, String> pkg = mCallingPackage.get();
         if (pkg != null) {
             return pkg.second;
@@ -962,6 +967,14 @@
     }
 
     /**
+     * @removed
+     */
+    @Deprecated
+    public final @Nullable String getCallingFeatureId() {
+        return getCallingAttributionTag();
+    }
+
+    /**
      * Return the package name of the caller that initiated the request being
      * processed on the current thread. The returned package will have
      * <em>not</em> been verified to belong to the calling UID. Returns
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index a9b7862..d0f5ec4 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -80,7 +80,7 @@
     private final IContentProvider mContentProvider;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final String mPackageName;
-    private final @Nullable String mFeatureId;
+    private final @Nullable String mAttributionTag;
     private final String mAuthority;
     private final boolean mStable;
 
@@ -104,7 +104,7 @@
         mContentResolver = contentResolver;
         mContentProvider = contentProvider;
         mPackageName = contentResolver.mPackageName;
-        mFeatureId = contentResolver.mFeatureId;
+        mAttributionTag = contentResolver.mAttributionTag;
 
         mAuthority = authority;
         mStable = stable;
@@ -195,7 +195,8 @@
                 cancellationSignal.setRemote(remoteCancellationSignal);
             }
             final Cursor cursor = mContentProvider.query(
-                    mPackageName, mFeatureId, uri, projection, queryArgs, remoteCancellationSignal);
+                    mPackageName, mAttributionTag, uri, projection, queryArgs,
+                    remoteCancellationSignal);
             if (cursor == null) {
                 return null;
             }
@@ -255,7 +256,7 @@
 
         beforeRemote();
         try {
-            return mContentProvider.canonicalize(mPackageName, mFeatureId, url);
+            return mContentProvider.canonicalize(mPackageName, mAttributionTag, url);
         } catch (DeadObjectException e) {
             if (!mStable) {
                 mContentResolver.unstableProviderDied(mContentProvider);
@@ -273,7 +274,7 @@
 
         beforeRemote();
         try {
-            return mContentProvider.uncanonicalize(mPackageName, mFeatureId, url);
+            return mContentProvider.uncanonicalize(mPackageName, mAttributionTag, url);
         } catch (DeadObjectException e) {
             if (!mStable) {
                 mContentResolver.unstableProviderDied(mContentProvider);
@@ -298,7 +299,7 @@
                 remoteCancellationSignal = mContentProvider.createCancellationSignal();
                 cancellationSignal.setRemote(remoteCancellationSignal);
             }
-            return mContentProvider.refresh(mPackageName, mFeatureId, url, extras,
+            return mContentProvider.refresh(mPackageName, mAttributionTag, url, extras,
                     remoteCancellationSignal);
         } catch (DeadObjectException e) {
             if (!mStable) {
@@ -318,7 +319,7 @@
 
         beforeRemote();
         try {
-            return mContentProvider.checkUriPermission(mPackageName, mFeatureId, uri, uid,
+            return mContentProvider.checkUriPermission(mPackageName, mAttributionTag, uri, uid,
                     modeFlags);
         } catch (DeadObjectException e) {
             if (!mStable) {
@@ -344,7 +345,8 @@
 
         beforeRemote();
         try {
-            return mContentProvider.insert(mPackageName, mFeatureId, url, initialValues, extras);
+            return mContentProvider.insert(mPackageName, mAttributionTag, url, initialValues,
+                    extras);
         } catch (DeadObjectException e) {
             if (!mStable) {
                 mContentResolver.unstableProviderDied(mContentProvider);
@@ -364,7 +366,7 @@
 
         beforeRemote();
         try {
-            return mContentProvider.bulkInsert(mPackageName, mFeatureId, url, initialValues);
+            return mContentProvider.bulkInsert(mPackageName, mAttributionTag, url, initialValues);
         } catch (DeadObjectException e) {
             if (!mStable) {
                 mContentResolver.unstableProviderDied(mContentProvider);
@@ -388,7 +390,7 @@
 
         beforeRemote();
         try {
-            return mContentProvider.delete(mPackageName, mFeatureId, url, extras);
+            return mContentProvider.delete(mPackageName, mAttributionTag, url, extras);
         } catch (DeadObjectException e) {
             if (!mStable) {
                 mContentResolver.unstableProviderDied(mContentProvider);
@@ -413,7 +415,7 @@
 
         beforeRemote();
         try {
-            return mContentProvider.update(mPackageName, mFeatureId, url, values, extras);
+            return mContentProvider.update(mPackageName, mAttributionTag, url, values, extras);
         } catch (DeadObjectException e) {
             if (!mStable) {
                 mContentResolver.unstableProviderDied(mContentProvider);
@@ -457,8 +459,8 @@
                 remoteSignal = mContentProvider.createCancellationSignal();
                 signal.setRemote(remoteSignal);
             }
-            return mContentProvider.openFile(mPackageName, mFeatureId, url, mode, remoteSignal,
-                    null);
+            return mContentProvider.openFile(mPackageName, mAttributionTag, url, mode,
+                    remoteSignal, null);
         } catch (DeadObjectException e) {
             if (!mStable) {
                 mContentResolver.unstableProviderDied(mContentProvider);
@@ -502,7 +504,7 @@
                 remoteSignal = mContentProvider.createCancellationSignal();
                 signal.setRemote(remoteSignal);
             }
-            return mContentProvider.openAssetFile(mPackageName, mFeatureId, url, mode,
+            return mContentProvider.openAssetFile(mPackageName, mAttributionTag, url, mode,
                     remoteSignal);
         } catch (DeadObjectException e) {
             if (!mStable) {
@@ -544,7 +546,7 @@
                 signal.setRemote(remoteSignal);
             }
             return mContentProvider.openTypedAssetFile(
-                    mPackageName, mFeatureId, uri, mimeTypeFilter, opts, remoteSignal);
+                    mPackageName, mAttributionTag, uri, mimeTypeFilter, opts, remoteSignal);
         } catch (DeadObjectException e) {
             if (!mStable) {
                 mContentResolver.unstableProviderDied(mContentProvider);
@@ -571,7 +573,8 @@
 
         beforeRemote();
         try {
-            return mContentProvider.applyBatch(mPackageName, mFeatureId, authority, operations);
+            return mContentProvider.applyBatch(mPackageName, mAttributionTag, authority,
+                    operations);
         } catch (DeadObjectException e) {
             if (!mStable) {
                 mContentResolver.unstableProviderDied(mContentProvider);
@@ -597,7 +600,8 @@
 
         beforeRemote();
         try {
-            return mContentProvider.call(mPackageName, mFeatureId, authority, method, arg, extras);
+            return mContentProvider.call(mPackageName, mAttributionTag, authority, method, arg,
+                    extras);
         } catch (DeadObjectException e) {
             if (!mStable) {
                 mContentResolver.unstableProviderDied(mContentProvider);
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 31e1fc8..b134c37 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -767,7 +767,7 @@
     public ContentResolver(@Nullable Context context, @Nullable ContentInterface wrapped) {
         mContext = context != null ? context : ActivityThread.currentApplication();
         mPackageName = mContext.getOpPackageName();
-        mFeatureId = mContext.getFeatureId();
+        mAttributionTag = mContext.getAttributionTag();
         mTargetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
         mWrapped = wrapped;
     }
@@ -1144,7 +1144,7 @@
                 cancellationSignal.setRemote(remoteCancellationSignal);
             }
             try {
-                qCursor = unstableProvider.query(mPackageName, mFeatureId, uri, projection,
+                qCursor = unstableProvider.query(mPackageName, mAttributionTag, uri, projection,
                         queryArgs, remoteCancellationSignal);
             } catch (DeadObjectException e) {
                 // The remote process has died...  but we only hold an unstable
@@ -1155,7 +1155,7 @@
                 if (stableProvider == null) {
                     return null;
                 }
-                qCursor = stableProvider.query(mPackageName, mFeatureId, uri, projection,
+                qCursor = stableProvider.query(mPackageName, mAttributionTag, uri, projection,
                         queryArgs, remoteCancellationSignal);
             }
             if (qCursor == null) {
@@ -1247,7 +1247,7 @@
 
         try {
             final UriResultListener resultListener = new UriResultListener();
-            provider.canonicalizeAsync(mPackageName, mFeatureId, url,
+            provider.canonicalizeAsync(mPackageName, mAttributionTag, url,
                     new RemoteCallback(resultListener));
             resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS);
             return resultListener.result;
@@ -1294,7 +1294,7 @@
         }
 
         try {
-            return provider.uncanonicalize(mPackageName, mFeatureId, url);
+            return provider.uncanonicalize(mPackageName, mAttributionTag, url);
         } catch (RemoteException e) {
             // Arbitrary and not worth documenting, as Activity
             // Manager will kill this process shortly anyway.
@@ -1346,7 +1346,7 @@
                 remoteCancellationSignal = provider.createCancellationSignal();
                 cancellationSignal.setRemote(remoteCancellationSignal);
             }
-            return provider.refresh(mPackageName, mFeatureId, url, extras,
+            return provider.refresh(mPackageName, mAttributionTag, url, extras,
                     remoteCancellationSignal);
         } catch (RemoteException e) {
             // Arbitrary and not worth documenting, as Activity
@@ -1748,7 +1748,8 @@
 
                     try {
                         fd = unstableProvider.openAssetFile(
-                                mPackageName, mFeatureId, uri, mode, remoteCancellationSignal);
+                                mPackageName, mAttributionTag, uri, mode,
+                                remoteCancellationSignal);
                         if (fd == null) {
                             // The provider will be released by the finally{} clause
                             return null;
@@ -1763,7 +1764,7 @@
                             throw new FileNotFoundException("No content provider: " + uri);
                         }
                         fd = stableProvider.openAssetFile(
-                                mPackageName, mFeatureId, uri, mode, remoteCancellationSignal);
+                                mPackageName, mAttributionTag, uri, mode, remoteCancellationSignal);
                         if (fd == null) {
                             // The provider will be released by the finally{} clause
                             return null;
@@ -1914,7 +1915,8 @@
 
             try {
                 fd = unstableProvider.openTypedAssetFile(
-                        mPackageName, mFeatureId, uri, mimeType, opts, remoteCancellationSignal);
+                        mPackageName, mAttributionTag, uri, mimeType, opts,
+                        remoteCancellationSignal);
                 if (fd == null) {
                     // The provider will be released by the finally{} clause
                     return null;
@@ -1929,7 +1931,8 @@
                     throw new FileNotFoundException("No content provider: " + uri);
                 }
                 fd = stableProvider.openTypedAssetFile(
-                        mPackageName, mFeatureId, uri, mimeType, opts, remoteCancellationSignal);
+                        mPackageName, mAttributionTag, uri, mimeType, opts,
+                        remoteCancellationSignal);
                 if (fd == null) {
                     // The provider will be released by the finally{} clause
                     return null;
@@ -2077,7 +2080,7 @@
         }
         try {
             long startTime = SystemClock.uptimeMillis();
-            Uri createdRow = provider.insert(mPackageName, mFeatureId, url, values, extras);
+            Uri createdRow = provider.insert(mPackageName, mAttributionTag, url, values, extras);
             long durationMillis = SystemClock.uptimeMillis() - startTime;
             maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */);
             return createdRow;
@@ -2158,7 +2161,7 @@
         }
         try {
             long startTime = SystemClock.uptimeMillis();
-            int rowsCreated = provider.bulkInsert(mPackageName, mFeatureId, url, values);
+            int rowsCreated = provider.bulkInsert(mPackageName, mAttributionTag, url, values);
             long durationMillis = SystemClock.uptimeMillis() - startTime;
             maybeLogUpdateToEventLog(durationMillis, url, "bulkinsert", null /* where */);
             return rowsCreated;
@@ -2217,7 +2220,7 @@
         }
         try {
             long startTime = SystemClock.uptimeMillis();
-            int rowsDeleted = provider.delete(mPackageName, mFeatureId, url, extras);
+            int rowsDeleted = provider.delete(mPackageName, mAttributionTag, url, extras);
             long durationMillis = SystemClock.uptimeMillis() - startTime;
             maybeLogUpdateToEventLog(durationMillis, url, "delete", null);
             return rowsDeleted;
@@ -2284,7 +2287,7 @@
         }
         try {
             long startTime = SystemClock.uptimeMillis();
-            int rowsUpdated = provider.update(mPackageName, mFeatureId, uri, values, extras);
+            int rowsUpdated = provider.update(mPackageName, mAttributionTag, uri, values, extras);
             long durationMillis = SystemClock.uptimeMillis() - startTime;
             maybeLogUpdateToEventLog(durationMillis, uri, "update", null);
             return rowsUpdated;
@@ -2333,7 +2336,7 @@
             throw new IllegalArgumentException("Unknown authority " + authority);
         }
         try {
-            final Bundle res = provider.call(mPackageName, mFeatureId, authority, method, arg,
+            final Bundle res = provider.call(mPackageName, mAttributionTag, authority, method, arg,
                     extras);
             Bundle.setDefusable(res, true);
             return res;
@@ -3746,8 +3749,8 @@
     }
 
     /** @hide */
-    public @Nullable String getFeatureId() {
-        return mFeatureId;
+    public @Nullable String getAttributionTag() {
+        return mAttributionTag;
     }
 
     @UnsupportedAppUsage
@@ -3757,7 +3760,7 @@
 
     @UnsupportedAppUsage
     final String mPackageName;
-    final @Nullable String mFeatureId;
+    final @Nullable String mAttributionTag;
     final int mTargetSdkVersion;
     final ContentInterface mWrapped;
 
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 6cba327..318ae11 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -71,6 +71,7 @@
 import android.view.View;
 import android.view.ViewDebug;
 import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams.WindowType;
 import android.view.autofill.AutofillManager.AutofillClient;
 import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient;
 import android.view.textclassifier.TextClassificationManager;
@@ -814,16 +815,25 @@
     }
 
     /**
-     * <p>Features are used in complex apps to logically separate parts of the app. E.g. a
-     * blogging app might also have a instant messaging app built in.
+     * <p>Attribution can be used in complex apps to logically separate parts of the app. E.g. a
+     * blogging app might also have a instant messaging app built in. In this case two separate tags
+     * can for used each sub-feature.
      *
-     * @return the feature id this context is for or {@code null} if this is the default
-     * feature.
+     * @return the attribution tag this context is for or {@code null} if this is the default.
      */
-    public @Nullable String getFeatureId() {
+    public @Nullable String getAttributionTag() {
         return null;
     }
 
+    // TODO moltmann: Remove
+    /**
+     * @removed
+     */
+    @Deprecated
+    public @Nullable String getFeatureId() {
+        return getAttributionTag();
+    }
+
     /** Return the full application info for this context's package. */
     public abstract ApplicationInfo getApplicationInfo();
 
@@ -5820,24 +5830,34 @@
      * @see #WALLPAPER_SERVICE
      * @throws IllegalArgumentException if token is invalid
      */
-    public @NonNull Context createWindowContext(int type, @Nullable Bundle options)  {
+    public @NonNull Context createWindowContext(@WindowType int type, @Nullable Bundle options)  {
         throw new RuntimeException("Not implemented. Must override in a subclass.");
     }
 
     /**
-     * Return a new Context object for the current Context but for a different feature in the app.
-     * Features can be used by complex apps to separate logical parts.
+     * Return a new Context object for the current Context but attribute to a different tag.
+     * In complex apps attribution tagging can be used to distinguish between separate logical
+     * parts.
      *
-     * @param featureId The feature id or {@code null} to create a context for the default feature.
+     * @param attributionTag The tag or {@code null} to create a context for the default.
      *
-     * @return A {@link Context} for the feature
+     * @return A {@link Context} that is tagged for the new attribution
      *
-     * @see #getFeatureId()
+     * @see #getAttributionTag()
      */
-    public @NonNull Context createFeatureContext(@Nullable String featureId) {
+    public @NonNull Context createAttributionContext(@Nullable String attributionTag) {
         throw new RuntimeException("Not implemented. Must override in a subclass.");
     }
 
+    // TODO moltmann: remove
+    /**
+     * @removed
+     */
+    @Deprecated
+    public @NonNull Context createFeatureContext(@Nullable String featureId) {
+        return createAttributionContext(featureId);
+    }
+
     /**
      * Return a new Context object for the current Context but whose storage
      * APIs are backed by device-protected storage.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 91d214b..d389d2a 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -42,6 +42,7 @@
 import android.os.UserHandle;
 import android.view.Display;
 import android.view.DisplayAdjustments;
+import android.view.WindowManager.LayoutParams.WindowType;
 import android.view.autofill.AutofillManager.AutofillClient;
 
 import java.io.File;
@@ -163,8 +164,8 @@
 
     /** @hide */
     @Override
-    public @Nullable String getFeatureId() {
-        return mBase.getFeatureId();
+    public @Nullable String getAttributionTag() {
+        return mBase.getAttributionTag();
     }
 
     @Override
@@ -978,13 +979,13 @@
 
     @Override
     @NonNull
-    public Context createWindowContext(int type, @Nullable Bundle options) {
+    public Context createWindowContext(@WindowType int type, @Nullable Bundle options) {
         return mBase.createWindowContext(type, options);
     }
 
     @Override
-    public @NonNull Context createFeatureContext(@Nullable String featureId) {
-        return mBase.createFeatureContext(featureId);
+    public @NonNull Context createAttributionContext(@Nullable String attributionTag) {
+        return mBase.createAttributionContext(attributionTag);
     }
 
     @Override
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index 37643da..84b0f0e 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -38,7 +38,7 @@
  * @hide
  */
 public interface IContentProvider extends IInterface {
-    public Cursor query(String callingPkg, @Nullable String featureId, Uri url,
+    public Cursor query(String callingPkg, @Nullable String attributionTag, Uri url,
             @Nullable String[] projection,
             @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal)
             throws RemoteException;
@@ -59,8 +59,8 @@
             throws RemoteException {
         return insert(callingPkg, null, url, initialValues, null);
     }
-    public Uri insert(String callingPkg, String featureId, Uri url, ContentValues initialValues,
-            Bundle extras) throws RemoteException;
+    public Uri insert(String callingPkg, String attributionTag, Uri url,
+            ContentValues initialValues, Bundle extras) throws RemoteException;
     @Deprecated
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
             + "ContentProviderClient#bulkInsert(android.net.Uri, android.content.ContentValues[])"
@@ -69,7 +69,7 @@
             throws RemoteException {
         return bulkInsert(callingPkg, null, url, initialValues);
     }
-    public int bulkInsert(String callingPkg, String featureId, Uri url,
+    public int bulkInsert(String callingPkg, String attributionTag, Uri url,
             ContentValues[] initialValues) throws RemoteException;
     @Deprecated
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
@@ -80,7 +80,7 @@
         return delete(callingPkg, null, url,
                 ContentResolver.createSqlQueryBundle(selection, selectionArgs));
     }
-    public int delete(String callingPkg, String featureId, Uri url, Bundle extras)
+    public int delete(String callingPkg, String attributionTag, Uri url, Bundle extras)
             throws RemoteException;
     @Deprecated
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
@@ -91,18 +91,18 @@
         return update(callingPkg, null, url, values,
                 ContentResolver.createSqlQueryBundle(selection, selectionArgs));
     }
-    public int update(String callingPkg, String featureId, Uri url, ContentValues values,
+    public int update(String callingPkg, String attributionTag, Uri url, ContentValues values,
             Bundle extras) throws RemoteException;
 
-    public ParcelFileDescriptor openFile(String callingPkg, @Nullable String featureId, Uri url,
-            String mode, ICancellationSignal signal, IBinder callerToken)
+    public ParcelFileDescriptor openFile(String callingPkg, @Nullable String attributionTag,
+            Uri url, String mode, ICancellationSignal signal, IBinder callerToken)
             throws RemoteException, FileNotFoundException;
 
-    public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String featureId,
+    public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String attributionTag,
             Uri url, String mode, ICancellationSignal signal)
             throws RemoteException, FileNotFoundException;
 
-    public ContentProviderResult[] applyBatch(String callingPkg, @Nullable String featureId,
+    public ContentProviderResult[] applyBatch(String callingPkg, @Nullable String attributionTag,
             String authority, ArrayList<ContentProviderOperation> operations)
             throws RemoteException, OperationApplicationException;
 
@@ -115,15 +115,15 @@
         return call(callingPkg, null, "unknown", method, arg, extras);
     }
 
-    public Bundle call(String callingPkg, @Nullable String featureId, String authority,
+    public Bundle call(String callingPkg, @Nullable String attributionTag, String authority,
             String method, @Nullable String arg, @Nullable Bundle extras) throws RemoteException;
 
-    public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri uri, int uid,
-            int modeFlags) throws RemoteException;
+    public int checkUriPermission(String callingPkg, @Nullable String attributionTag, Uri uri,
+            int uid, int modeFlags) throws RemoteException;
 
     public ICancellationSignal createCancellationSignal() throws RemoteException;
 
-    public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri)
+    public Uri canonicalize(String callingPkg, @Nullable String attributionTag, Uri uri)
             throws RemoteException;
 
     /**
@@ -131,20 +131,21 @@
      * call returns immediately, and the resulting type is returned when available via
      * a binder callback.
      */
-    void canonicalizeAsync(String callingPkg, @Nullable String featureId, Uri uri,
+    void canonicalizeAsync(String callingPkg, @Nullable String attributionTag, Uri uri,
             RemoteCallback callback) throws RemoteException;
 
-    public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri)
+    public Uri uncanonicalize(String callingPkg, @Nullable String attributionTag, Uri uri)
             throws RemoteException;
 
-    public boolean refresh(String callingPkg, @Nullable String featureId, Uri url,
+    public boolean refresh(String callingPkg, @Nullable String attributionTag, Uri url,
             @Nullable Bundle extras, ICancellationSignal cancellationSignal) throws RemoteException;
 
     // Data interchange.
     public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException;
 
-    public AssetFileDescriptor openTypedAssetFile(String callingPkg, @Nullable String featureId,
-            Uri url, String mimeType, Bundle opts, ICancellationSignal signal)
+    public AssetFileDescriptor openTypedAssetFile(String callingPkg,
+            @Nullable String attributionTag, Uri url, String mimeType, Bundle opts,
+            ICancellationSignal signal)
             throws RemoteException, FileNotFoundException;
 
     /* IPC constants */
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 4bb7346d..38c1890 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -26,6 +26,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.app.AppGlobals;
@@ -86,6 +87,7 @@
 import java.util.Locale;
 import java.util.Objects;
 import java.util.Set;
+import java.util.TimeZone;
 
 /**
  * An intent is an abstract description of an operation to be performed.  It
@@ -2312,7 +2314,8 @@
     /**
      * Broadcast Action: The timezone has changed. The intent will have the following extra values:</p>
      * <ul>
-     *   <li><em>time-zone</em> - The java.util.TimeZone.getID() value identifying the new time zone.</li>
+     *   <li>{@link #EXTRA_TIMEZONE} - The java.util.TimeZone.getID() value identifying the new
+     *   time zone.</li>
      * </ul>
      *
      * <p class="note">This is a protected intent that can only be sent
@@ -5785,6 +5788,14 @@
     public static final String EXTRA_TIME = "android.intent.extra.TIME";
 
     /**
+     * Extra sent with {@link #ACTION_TIMEZONE_CHANGED} specifying the new time zone of the device.
+     *
+     * <p>Type: String, the same as returned by {@link TimeZone#getID()} to identify time zones.
+     */
+    @SuppressLint("ActionValue")
+    public static final String EXTRA_TIMEZONE = "time-zone";
+
+    /**
      * Optional int extra for {@link #ACTION_TIME_CHANGED} that indicates the
      * user has set their time format preference. See {@link #EXTRA_TIME_PREF_VALUE_USE_12_HOUR},
      * {@link #EXTRA_TIME_PREF_VALUE_USE_24_HOUR} and
diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java
index 33bd839..052c920 100644
--- a/core/java/android/content/PermissionChecker.java
+++ b/core/java/android/content/PermissionChecker.java
@@ -123,7 +123,7 @@
      * @param uid The uid for which to check.
      * @param packageName The package name for which to check. If null the
      *     the first package for the calling UID will be used.
-     * @param featureId Feature in the package
+     * @param attributionTag attribution tag
      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
      *     or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
      * @param message A message describing the reason the permission was checked
@@ -133,9 +133,9 @@
     @PermissionResult
     public static int checkPermissionForDataDelivery(@NonNull Context context,
             @NonNull String permission, int pid, int uid, @Nullable String packageName,
-            @Nullable String featureId, @Nullable String message) {
-        return checkPermissionCommon(context, permission, pid, uid, packageName, featureId, message,
-                true /*forDataDelivery*/);
+            @Nullable String attributionTag, @Nullable String message) {
+        return checkPermissionCommon(context, permission, pid, uid, packageName, attributionTag,
+                message, true /*forDataDelivery*/);
     }
 
     /**
@@ -171,8 +171,8 @@
     @PermissionResult
     public static int checkPermissionForPreflight(@NonNull Context context,
             @NonNull String permission, int pid, int uid, @Nullable String packageName) {
-        return checkPermissionCommon(context, permission, pid, uid, packageName, null /*featureId*/,
-                null /*message*/, false /*forDataDelivery*/);
+        return checkPermissionCommon(context, permission, pid, uid, packageName,
+                null /*attributionTag*/, null /*message*/, false /*forDataDelivery*/);
     }
 
     /**
@@ -207,7 +207,7 @@
     public static int checkSelfPermissionForDataDelivery(@NonNull Context context,
             @NonNull String permission, @Nullable String message) {
         return checkPermissionForDataDelivery(context, permission, Process.myPid(),
-                Process.myUid(), context.getPackageName(), context.getFeatureId(), message);
+                Process.myUid(), context.getPackageName(), context.getAttributionTag(), message);
     }
 
     /**
@@ -266,7 +266,7 @@
      * @param permission The permission to check.
      * @param packageName The package name making the IPC. If null the
      *     the first package for the calling UID will be used.
-     * @param featureId The feature inside of the app
+     * @param attributionTag attribution tag
      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
      *     or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
      * @param message A message describing the reason the permission was checked
@@ -276,12 +276,12 @@
     @PermissionResult
     public static int checkCallingPermissionForDataDelivery(@NonNull Context context,
             @NonNull String permission, @Nullable String packageName,
-            @Nullable String featureId, @Nullable String message) {
+            @Nullable String attributionTag, @Nullable String message) {
         if (Binder.getCallingPid() == Process.myPid()) {
             return PERMISSION_HARD_DENIED;
         }
         return checkPermissionForDataDelivery(context, permission, Binder.getCallingPid(),
-                Binder.getCallingUid(), packageName, featureId, message);
+                Binder.getCallingUid(), packageName, attributionTag, message);
     }
 
     /**
@@ -343,20 +343,20 @@
      * @param permission The permission to check.
      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
      *     or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
-     * @param featureId feature Id of caller (if not self)
+     * @param attributionTag attribution tag of caller (if not self)
      * @param message A message describing the reason the permission was checked
      *
      * @see #checkCallingOrSelfPermissionForPreflight(Context, String)
      */
     @PermissionResult
     public static int checkCallingOrSelfPermissionForDataDelivery(@NonNull Context context,
-            @NonNull String permission, @Nullable String featureId, @Nullable String message) {
+            @NonNull String permission, @Nullable String attributionTag, @Nullable String message) {
         String packageName = (Binder.getCallingPid() == Process.myPid())
                 ? context.getPackageName() : null;
-        featureId = (Binder.getCallingPid() == Process.myPid())
-                ? context.getFeatureId() : featureId;
+        attributionTag = (Binder.getCallingPid() == Process.myPid())
+                ? context.getAttributionTag() : attributionTag;
         return checkPermissionForDataDelivery(context, permission, Binder.getCallingPid(),
-                Binder.getCallingUid(), packageName, featureId, message);
+                Binder.getCallingUid(), packageName, attributionTag, message);
     }
 
     /**
@@ -395,7 +395,7 @@
     }
 
     static int checkPermissionCommon(@NonNull Context context, @NonNull String permission,
-            int pid, int uid, @Nullable String packageName, @Nullable String featureId,
+            int pid, int uid, @Nullable String packageName, @Nullable String attributionTag,
             @Nullable String message, boolean forDataDelivery) {
         final PermissionInfo permissionInfo;
         try {
@@ -414,18 +414,18 @@
         }
 
         if (permissionInfo.isAppOp()) {
-            return checkAppOpPermission(context, permission, pid, uid, packageName, featureId,
+            return checkAppOpPermission(context, permission, pid, uid, packageName, attributionTag,
                     message, forDataDelivery);
         }
         if (permissionInfo.isRuntime()) {
-            return checkRuntimePermission(context, permission, pid, uid, packageName, featureId,
-                    message, forDataDelivery);
+            return checkRuntimePermission(context, permission, pid, uid, packageName,
+                    attributionTag, message, forDataDelivery);
         }
         return context.checkPermission(permission, pid, uid);
     }
 
     private static int checkAppOpPermission(@NonNull Context context, @NonNull String permission,
-            int pid, int uid, @Nullable String packageName, @Nullable String featureId,
+            int pid, int uid, @Nullable String packageName, @Nullable String attributionTag,
             @Nullable String message, boolean forDataDelivery) {
         final String op = AppOpsManager.permissionToOp(permission);
         if (op == null || packageName == null) {
@@ -434,7 +434,7 @@
 
         final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
         final int opMode = (forDataDelivery)
-                ? appOpsManager.noteProxyOpNoThrow(op, packageName, uid, featureId, message)
+                ? appOpsManager.noteProxyOpNoThrow(op, packageName, uid, attributionTag, message)
                 : appOpsManager.unsafeCheckOpNoThrow(op, uid, packageName);
 
         switch (opMode) {
@@ -453,7 +453,7 @@
     }
 
     private static int checkRuntimePermission(@NonNull Context context, @NonNull String permission,
-            int pid, int uid, @Nullable String packageName, @Nullable String featureId,
+            int pid, int uid, @Nullable String packageName, @Nullable String attributionTag,
             @Nullable String message, boolean forDataDelivery) {
         if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) {
             return PERMISSION_HARD_DENIED;
@@ -466,7 +466,7 @@
 
         final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
         final int opMode = (forDataDelivery)
-                ? appOpsManager.noteProxyOpNoThrow(op, packageName, uid, featureId, message)
+                ? appOpsManager.noteProxyOpNoThrow(op, packageName, uid, attributionTag, message)
                 : appOpsManager.unsafeCheckOpNoThrow(op, uid, packageName);
 
         if (opMode == AppOpsManager.MODE_ALLOWED) {
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 0b2b5b1..f25ce76 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1222,13 +1222,7 @@
         dest.writeInt(lockTaskLaunchMode);
         if (windowLayout != null) {
             dest.writeInt(1);
-            dest.writeInt(windowLayout.width);
-            dest.writeFloat(windowLayout.widthFraction);
-            dest.writeInt(windowLayout.height);
-            dest.writeFloat(windowLayout.heightFraction);
-            dest.writeInt(windowLayout.gravity);
-            dest.writeInt(windowLayout.minWidth);
-            dest.writeInt(windowLayout.minHeight);
+            windowLayout.writeToParcel(dest);
         } else {
             dest.writeInt(0);
         }
@@ -1372,8 +1366,8 @@
      * @attr ref android.R.styleable#AndroidManifestLayout_minHeight
      */
     public static final class WindowLayout {
-        public WindowLayout(int width, float widthFraction, int height, float heightFraction, int gravity,
-                int minWidth, int minHeight) {
+        public WindowLayout(int width, float widthFraction, int height, float heightFraction,
+                int gravity, int minWidth, int minHeight) {
             this.width = width;
             this.widthFraction = widthFraction;
             this.height = height;
@@ -1392,6 +1386,7 @@
             gravity = source.readInt();
             minWidth = source.readInt();
             minHeight = source.readInt();
+            windowLayoutAffinity = source.readString();
         }
 
         /**
@@ -1458,11 +1453,30 @@
         public final int minHeight;
 
         /**
+         * Affinity of window layout parameters. Activities with the same UID and window layout
+         * affinity will share the same window dimension record.
+         * @hide
+         */
+        public String windowLayoutAffinity;
+
+        /**
          * Returns if this {@link WindowLayout} has specified bounds.
          * @hide
          */
         public boolean hasSpecifiedSize() {
             return width >= 0 || height >= 0 || widthFraction >= 0 || heightFraction >= 0;
         }
+
+        /** @hide */
+        public void writeToParcel(Parcel dest) {
+            dest.writeInt(width);
+            dest.writeFloat(widthFraction);
+            dest.writeInt(height);
+            dest.writeFloat(heightFraction);
+            dest.writeInt(gravity);
+            dest.writeInt(minWidth);
+            dest.writeInt(minHeight);
+            dest.writeString(windowLayoutAffinity);
+        }
     }
 }
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index a15afe0..c82fffa 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -25,6 +25,7 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ProcessInfo;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
@@ -38,6 +39,8 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.Parcelling;
+import com.android.internal.util.Parcelling.BuiltIn.ForBoolean;
 import com.android.server.SystemConfig;
 
 import java.lang.annotation.Retention;
@@ -56,7 +59,8 @@
  * &lt;application&gt; tag.
  */
 public class ApplicationInfo extends PackageItemInfo implements Parcelable {
-    
+    private static ForBoolean sForBoolean = Parcelling.Cache.getOrCreate(ForBoolean.class);
+
     /**
      * Default task affinity of all activities in this application. See 
      * {@link ActivityInfo#taskAffinity} for more information.  This comes 
@@ -1273,6 +1277,14 @@
     public String zygotePreloadName;
 
     /**
+     * Indicates if the application has requested GWP-ASan to be enabled, disabled, or left
+     * unspecified. Processes can override this setting.
+     * @hide
+     */
+    @Nullable
+    public Boolean enableGwpAsan;
+
+    /**
      * Represents the default policy. The actual policy used will depend on other properties of
      * the application, e.g. the target SDK version.
      * @hide
@@ -1413,6 +1425,9 @@
             pw.println(prefix + "usesNonSdkApi=" + usesNonSdkApi());
             pw.println(prefix + "allowsPlaybackCapture="
                         + (isAudioPlaybackCaptureAllowed() ? "true" : "false"));
+            if (enableGwpAsan != null) {
+                pw.println(prefix + "enableGwpAsan=" + enableGwpAsan);
+            }
         }
         super.dumpBack(pw, prefix);
     }
@@ -1511,6 +1526,9 @@
             if (category != CATEGORY_UNDEFINED) {
                 proto.write(ApplicationInfoProto.Detail.CATEGORY, category);
             }
+            if (enableGwpAsan != null) {
+                proto.write(ApplicationInfoProto.Detail.ENABLE_GWP_ASAN, enableGwpAsan);
+            }
             proto.end(detailToken);
         }
         proto.end(token);
@@ -1620,6 +1638,7 @@
         mHiddenApiPolicy = orig.mHiddenApiPolicy;
         hiddenUntilInstalled = orig.hiddenUntilInstalled;
         zygotePreloadName = orig.zygotePreloadName;
+        enableGwpAsan = orig.enableGwpAsan;
     }
 
     public String toString() {
@@ -1703,6 +1722,7 @@
         dest.writeInt(mHiddenApiPolicy);
         dest.writeInt(hiddenUntilInstalled ? 1 : 0);
         dest.writeString(zygotePreloadName);
+        sForBoolean.parcel(enableGwpAsan, dest, parcelableFlags);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<ApplicationInfo> CREATOR
@@ -1783,6 +1803,7 @@
         mHiddenApiPolicy = source.readInt();
         hiddenUntilInstalled = source.readInt() != 0;
         zygotePreloadName = source.readString();
+        enableGwpAsan = sForBoolean.unparcel(source);
     }
 
     /**
@@ -2161,6 +2182,7 @@
     /** {@hide} */ public void setResourcePath(String resourcePath) { scanPublicSourceDir = resourcePath; }
     /** {@hide} */ public void setBaseResourcePath(String baseResourcePath) { publicSourceDir = baseResourcePath; }
     /** {@hide} */ public void setSplitResourcePaths(String[] splitResourcePaths) { splitPublicSourceDirs = splitResourcePaths; }
+    /** {@hide} */ public void setGwpAsanEnabled(@Nullable Boolean value) { enableGwpAsan = value; }
 
     /** {@hide} */
     @UnsupportedAppUsage
@@ -2172,4 +2194,6 @@
     @UnsupportedAppUsage
     public String getBaseResourcePath() { return publicSourceDir; }
     /** {@hide} */ public String[] getSplitResourcePaths() { return splitPublicSourceDirs; }
+    @Nullable
+    public Boolean isGwpAsanEnabled() { return enableGwpAsan; }
 }
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 3261cb1..dc3a0297 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -27,6 +27,7 @@
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -95,7 +96,7 @@
             mService.startActivityAsUser(
                     mContext.getIApplicationThread(),
                     mContext.getPackageName(),
-                    mContext.getFeatureId(),
+                    mContext.getAttributionTag(),
                     component,
                     targetUser.getIdentifier(),
                     true);
@@ -128,14 +129,44 @@
             @NonNull Intent intent,
             @NonNull UserHandle targetUser,
             @Nullable Activity callingActivity) {
+        startActivity(intent, targetUser, callingActivity, /* options= */ null);
+    }
+
+    /**
+     * Starts the specified activity of the caller package in the specified profile.
+     *
+     * <p>The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES},
+     * {@code android.Manifest.permission#INTERACT_ACROSS_USERS}, or {@code
+     * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission. Both the caller and
+     * target user profiles must be in the same profile group. The target user must be a valid user
+     * returned from {@link #getTargetUserProfiles()}.
+     *
+     * @param intent The intent to launch. A component in the caller package must be specified.
+     * @param targetUser The {@link UserHandle} of the profile; must be one of the users returned by
+     *        {@link #getTargetUserProfiles()} if different to the calling user, otherwise a
+     *        {@link SecurityException} will be thrown.
+     * @param callingActivity The activity to start the new activity from for the purposes of
+     *        deciding which task the new activity should belong to. If {@code null}, the activity
+     *        will always be started in a new task.
+     * @param options The activity options or {@code null}. See {@link android.app.ActivityOptions}.
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.INTERACT_ACROSS_PROFILES,
+            android.Manifest.permission.INTERACT_ACROSS_USERS})
+    public void startActivity(
+            @NonNull Intent intent,
+            @NonNull UserHandle targetUser,
+            @Nullable Activity callingActivity,
+            @Nullable Bundle options) {
         try {
             mService.startActivityAsUserByIntent(
                     mContext.getIApplicationThread(),
                     mContext.getPackageName(),
-                    mContext.getFeatureId(),
+                    mContext.getAttributionTag(),
                     intent,
                     targetUser.getIdentifier(),
-                    callingActivity != null ? callingActivity.getActivityToken() : null);
+                    callingActivity != null ? callingActivity.getActivityToken() : null,
+                    options);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
@@ -159,7 +190,7 @@
     public void startActivity(@NonNull ComponentName component, @NonNull UserHandle targetUser) {
         try {
             mService.startActivityAsUser(mContext.getIApplicationThread(),
-                    mContext.getPackageName(), mContext.getFeatureId(), component,
+                    mContext.getPackageName(), mContext.getAttributionTag(), component,
                     targetUser.getIdentifier(), false);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
diff --git a/core/java/android/content/pm/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl
index 5a6e008..4cecb30 100644
--- a/core/java/android/content/pm/ICrossProfileApps.aidl
+++ b/core/java/android/content/pm/ICrossProfileApps.aidl
@@ -31,7 +31,8 @@
             in String callingFeatureId, in ComponentName component, int userId,
             boolean launchMainActivity);
     void startActivityAsUserByIntent(in IApplicationThread caller, in String callingPackage,
-            in String callingFeatureId, in Intent intent, int userId, in IBinder callingActivity);
+            in String callingFeatureId, in Intent intent, int userId, in IBinder callingActivity,
+            in Bundle options);
     List<UserHandle> getTargetUserProfiles(in String callingPackage);
     boolean canInteractAcrossProfiles(in String callingPackage);
     boolean canRequestInteractAcrossProfiles(in String callingPackage);
diff --git a/core/java/android/content/pm/InstallSourceInfo.java b/core/java/android/content/pm/InstallSourceInfo.java
index c0fdcc9..a45bf79 100644
--- a/core/java/android/content/pm/InstallSourceInfo.java
+++ b/core/java/android/content/pm/InstallSourceInfo.java
@@ -66,7 +66,18 @@
         mInstallingPackageName = source.readString();
     }
 
-    /** The name of the package that requested the installation, or null if not available. */
+    /**
+     * The name of the package that requested the installation, or null if not available.
+     *
+     * This is normally the same as the installing package name. If the installing package name
+     * is changed, for example by calling
+     * {@link PackageManager#setInstallerPackageName(String, String)}, the initiating package name
+     * remains unchanged. It continues to identify the actual package that performed the install
+     * or update.
+     * <p>
+     * Null may be returned if the app was not installed by a package (e.g. a system app or an app
+     * installed via adb) or if the initiating package has itself been uninstalled.
+     */
     @Nullable
     public String getInitiatingPackageName() {
         return mInitiatingPackageName;
@@ -100,9 +111,11 @@
     /**
      * The name of the package responsible for the installation (the installer of record), or null
      * if not available.
-     * Note that this may differ from the initiating package name and can be modified.
-     *
-     * @see PackageManager#setInstallerPackageName(String, String)
+     * Note that this may differ from the initiating package name and can be modified via
+     * {@link PackageManager#setInstallerPackageName(String, String)}.
+     * <p>
+     * Null may be returned if the app was not installed by a package (e.g. a system app or an app
+     * installed via adb) or if the installing package has itself been uninstalled.
      */
     @Nullable
     public String getInstallingPackageName() {
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 86242fd..4e4897f 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -721,7 +721,7 @@
         }
         try {
             mService.startActivityAsUser(mContext.getIApplicationThread(),
-                    mContext.getPackageName(), mContext.getFeatureId(),
+                    mContext.getPackageName(), mContext.getAttributionTag(),
                     component, sourceBounds, opts, user);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
@@ -739,8 +739,8 @@
             @Nullable Rect sourceBounds, @Nullable Bundle opts) {
         try {
             mService.startSessionDetailsActivityAsUser(mContext.getIApplicationThread(),
-                    mContext.getPackageName(), mContext.getFeatureId(), sessionInfo, sourceBounds,
-                    opts, sessionInfo.getUser());
+                    mContext.getPackageName(), mContext.getAttributionTag(), sessionInfo,
+                    sourceBounds, opts, sessionInfo.getUser());
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -760,7 +760,7 @@
         logErrorForInvalidProfileAccess(user);
         try {
             mService.showAppDetailsAsUser(mContext.getIApplicationThread(),
-                    mContext.getPackageName(), mContext.getFeatureId(),
+                    mContext.getPackageName(), mContext.getAttributionTag(),
                     component, sourceBounds, opts, user);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 1f53176..ec3590f 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2323,6 +2323,7 @@
 
         /**
          * Get the value set in {@link SessionParams#setOriginatingUri(Uri)}.
+         * Note: This value will only be non-null for the owner of the session.
          */
         public @Nullable Uri getOriginatingUri() {
             return originatingUri;
@@ -2337,6 +2338,7 @@
 
         /**
          * Get the value set in {@link SessionParams#setReferrerUri(Uri)}
+         * Note: This value will only be non-null for the owner of the session.
          */
         public @Nullable Uri getReferrerUri() {
             return referrerUri;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 7600a08..b0964fa 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3411,12 +3411,13 @@
     public static final int FLAG_PERMISSION_AUTO_REVOKED = 1 << 17;
 
     /**
-     * Permission flags: Reserved for use by the permission controller.
-     *
+     * Permission flags: Reserved for use by the permission controller. The platform and any
+     * packages besides the permission controller should not assume any definition about these
+     * flags.
      * @hide
      */
     @SystemApi
-    public static final int FLAGS_PERMISSION_RESERVED_PERMISSIONCONTROLLER = 1 << 28 | 1 << 29
+    public static final int FLAGS_PERMISSION_RESERVED_PERMISSION_CONTROLLER = 1 << 28 | 1 << 29
             | 1 << 30 | 1 << 31;
 
     /**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index c6875a4..18f1343 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -55,7 +55,6 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.parsing.ParsingPackageUtils;
 import android.content.pm.permission.SplitPermissionInfoParcelable;
 import android.content.pm.split.DefaultSplitAssetLoader;
 import android.content.pm.split.SplitAssetDependencyLoader;
@@ -190,7 +189,7 @@
     public static final String TAG_OVERLAY = "overlay";
     public static final String TAG_PACKAGE = "package";
     public static final String TAG_PACKAGE_VERIFIER = "package-verifier";
-    public static final String TAG_FEATURE = "feature";
+    public static final String TAG_ATTRIBUTION = "attribution";
     public static final String TAG_PERMISSION = "permission";
     public static final String TAG_PERMISSION_GROUP = "permission-group";
     public static final String TAG_PERMISSION_TREE = "permission-tree";
@@ -209,6 +208,8 @@
     public static final String TAG_USES_SPLIT = "uses-split";
 
     public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
+    public static final String METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY =
+            "android.activity_window_layout_affinity";
 
     /**
      * Bit mask of all the valid bits that can be set in recreateOnConfigChanges.
@@ -4560,6 +4561,8 @@
             }
         }
 
+        resolveWindowLayout(a);
+
         if (!setExported) {
             a.info.exported = a.intents.size() > 0;
         }
@@ -4726,6 +4729,35 @@
                 height, heightFraction, gravity, minWidth, minHeight);
     }
 
+    /**
+     * Resolves values in {@link ActivityInfo.WindowLayout}.
+     *
+     * <p>{@link ActivityInfo.WindowLayout#windowLayoutAffinity} has a fallback metadata used in
+     * Android R and some variants of pre-R.
+     */
+    private void resolveWindowLayout(Activity activity) {
+        // There isn't a metadata for us to fall back. Whatever is in layout is correct.
+        if (activity.metaData == null
+                || !activity.metaData.containsKey(METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY)) {
+            return;
+        }
+
+        final ActivityInfo aInfo = activity.info;
+        // Layout already specifies a value. We should just use that one.
+        if (aInfo.windowLayout != null && aInfo.windowLayout.windowLayoutAffinity != null) {
+            return;
+        }
+
+        String windowLayoutAffinity = activity.metaData.getString(
+                METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY);
+        if (aInfo.windowLayout == null) {
+            aInfo.windowLayout = new ActivityInfo.WindowLayout(-1 /* width */,
+                    -1 /* widthFraction */, -1 /* height */, -1 /* heightFraction */,
+                    Gravity.NO_GRAVITY, -1 /* minWidth */, -1 /* minHeight */);
+        }
+        aInfo.windowLayout.windowLayoutAffinity = windowLayoutAffinity;
+    }
+
     private Activity parseActivityAlias(Package owner, Resources res,
             XmlResourceParser parser, int flags, String[] outError,
             CachedComponentArgs cachedArgs)
diff --git a/core/java/android/content/pm/ProcessInfo.java b/core/java/android/content/pm/ProcessInfo.java
index c77a267..a373067 100644
--- a/core/java/android/content/pm/ProcessInfo.java
+++ b/core/java/android/content/pm/ProcessInfo.java
@@ -23,66 +23,157 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
 /**
  * Information about a process an app may run.  This corresponds to information collected from the
  * AndroidManifest.xml's &lt;permission-group&gt; tags.
  * @hide
  */
+@DataClass(genGetters = true, genSetters = false, genParcelable = true, genAidl = false,
+        genBuilder = false)
 public class ProcessInfo implements Parcelable {
     /**
      * The name of the process, fully-qualified based on the app's package name.
      */
+    @NonNull
     public String name;
 
     /**
      * If non-null, these are permissions that are not allowed in this process.
      */
     @Nullable
+    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringArraySet.class)
     public ArraySet<String> deniedPermissions;
 
-    public ProcessInfo(String name, ArraySet<String> deniedPermissions) {
-        this.name = name;
-        this.deniedPermissions = deniedPermissions;
-    }
+    /**
+     * Indicates if the process has requested GWP-ASan to be enabled, disabled, or left unspecified.
+     */
+    @Nullable
+    public Boolean enableGwpAsan;
 
     @Deprecated
     public ProcessInfo(@NonNull ProcessInfo orig) {
         this.name = orig.name;
         this.deniedPermissions = orig.deniedPermissions;
+        this.enableGwpAsan = orig.enableGwpAsan;
     }
 
-    public int describeContents() {
-        return 0;
+
+
+    // Code below generated by codegen v1.0.15.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/ProcessInfo.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new ProcessInfo.
+     *
+     * @param name
+     *   The name of the process, fully-qualified based on the app's package name.
+     * @param deniedPermissions
+     *   If non-null, these are permissions that are not allowed in this process.
+     * @param enableGwpAsan
+     *   Indicates if the process has requested GWP-ASan to be enabled, disabled, or left unspecified.
+     */
+    @DataClass.Generated.Member
+    public ProcessInfo(
+            @NonNull String name,
+            @Nullable ArraySet<String> deniedPermissions,
+            @Nullable Boolean enableGwpAsan) {
+        this.name = name;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        this.deniedPermissions = deniedPermissions;
+        this.enableGwpAsan = enableGwpAsan;
+
+        // onConstructed(); // You can define this method to get a callback
     }
 
-    public void writeToParcel(Parcel dest, int parcelableFlags) {
-        dest.writeString(this.name);
-        final int numDenied = this.deniedPermissions != null
-                ? this.deniedPermissions.size() : 0;
-        dest.writeInt(numDenied);
-        for (int i = 0; i < numDenied; i++) {
-            dest.writeString(this.deniedPermissions.valueAt(i));
+    @DataClass.Generated.Member
+    static Parcelling<ArraySet<String>> sParcellingForDeniedPermissions =
+            Parcelling.Cache.get(
+                    Parcelling.BuiltIn.ForInternedStringArraySet.class);
+    static {
+        if (sParcellingForDeniedPermissions == null) {
+            sParcellingForDeniedPermissions = Parcelling.Cache.put(
+                    new Parcelling.BuiltIn.ForInternedStringArraySet());
         }
     }
 
-    public static final @NonNull Creator<ProcessInfo> CREATOR =
-            new Creator<ProcessInfo>() {
-                public ProcessInfo createFromParcel(Parcel source) {
-                    return new ProcessInfo(source);
-                }
-                public ProcessInfo[] newArray(int size) {
-                    return new ProcessInfo[size];
-                }
-            };
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
 
-    private ProcessInfo(Parcel source) {
-        this.name = source.readString();
-        final int numDenied = source.readInt();
-        if (numDenied > 0) {
-            this.deniedPermissions = new ArraySet<>(numDenied);
-            for (int i = numDenied - 1; i >= 0; i--) {
-                this.deniedPermissions.add(TextUtils.safeIntern(source.readString()));
-            }
-        }
+        byte flg = 0;
+        if (deniedPermissions != null) flg |= 0x2;
+        if (enableGwpAsan != null) flg |= 0x4;
+        dest.writeByte(flg);
+        dest.writeString(name);
+        sParcellingForDeniedPermissions.parcel(deniedPermissions, dest, flags);
+        if (enableGwpAsan != null) dest.writeBoolean(enableGwpAsan);
     }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected ProcessInfo(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        String _name = in.readString();
+        ArraySet<String> _deniedPermissions = sParcellingForDeniedPermissions.unparcel(in);
+        Boolean _enableGwpAsan = (flg & 0x4) == 0 ? null : (Boolean) in.readBoolean();
+
+        this.name = _name;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        this.deniedPermissions = _deniedPermissions;
+        this.enableGwpAsan = _enableGwpAsan;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<ProcessInfo> CREATOR
+            = new Parcelable.Creator<ProcessInfo>() {
+        @Override
+        public ProcessInfo[] newArray(int size) {
+            return new ProcessInfo[size];
+        }
+
+        @Override
+        public ProcessInfo createFromParcel(@NonNull Parcel in) {
+            return new ProcessInfo(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1582840056156L,
+            codegenVersion = "1.0.15",
+            sourceFile = "frameworks/base/core/java/android/content/pm/ProcessInfo.java",
+            inputSignatures = "public @android.annotation.NonNull java.lang.String name\npublic @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringArraySet.class) android.util.ArraySet<java.lang.String> deniedPermissions\npublic @android.annotation.Nullable java.lang.Boolean enableGwpAsan\nclass ProcessInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
 }
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 49e8c05..af87578 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -1558,11 +1558,6 @@
      * "Rank" of a shortcut, which is a non-negative, sequential value that's unique for each
      * {@link #getActivity} for each of the two types of shortcuts (static and dynamic).
      *
-     * <p>Because static shortcuts and dynamic shortcuts have overlapping ranks,
-     * when a launcher app shows shortcuts for an activity, it should first show
-     * the static shortcuts, followed by the dynamic shortcuts.  Within each of those categories,
-     * shortcuts should be sorted by rank in ascending order.
-     *
      * <p><em>Floating shortcuts</em>, or shortcuts that are neither static nor dynamic, will all
      * have rank 0, because they aren't sorted.
      *
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index aa93d80..1e02a7d 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -24,7 +24,7 @@
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageParser;
 import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.parsing.component.ParsedFeature;
+import android.content.pm.parsing.component.ParsedAttribution;
 import android.content.pm.parsing.component.ParsedInstrumentation;
 import android.content.pm.parsing.component.ParsedIntentInfo;
 import android.content.pm.parsing.component.ParsedPermission;
@@ -77,7 +77,7 @@
 
     ParsingPackage addProvider(ParsedProvider parsedProvider);
 
-    ParsingPackage addFeature(ParsedFeature permission);
+    ParsingPackage addAttribution(ParsedAttribution attribution);
 
     ParsingPackage addReceiver(ParsedActivity parsedReceiver);
 
@@ -236,6 +236,8 @@
 
     ParsingPackage setEnabled(boolean enabled);
 
+    ParsingPackage setGwpAsanEnabled(Boolean enableGwpAsan);
+
     ParsingPackage setCrossProfile(boolean crossProfile);
 
     ParsingPackage setFullBackupContent(int fullBackupContent);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index a9b72d0..d7c0dfb 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -30,9 +30,10 @@
 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;
-import android.content.pm.parsing.component.ParsedFeature;
 import android.content.pm.parsing.component.ParsedInstrumentation;
 import android.content.pm.parsing.component.ParsedIntentInfo;
 import android.content.pm.parsing.component.ParsedMainComponent;
@@ -243,7 +244,7 @@
     protected List<ParsedProvider> providers = emptyList();
 
     @NonNull
-    private List<ParsedFeature> features = emptyList();
+    private List<ParsedAttribution> attributions = emptyList();
 
     @NonNull
     protected List<ParsedPermission> permissions = emptyList();
@@ -406,6 +407,10 @@
     private boolean allowNativeHeapPointerTagging;
     private boolean preserveLegacyExternalStorage;
 
+    @Nullable
+    @DataClass.ParcelWith(ForBoolean.class)
+    protected Boolean enableGwpAsan = null;
+
     // TODO(chiuwinson): Non-null
     @Nullable
     private ArraySet<String> mimeGroups;
@@ -620,8 +625,8 @@
     }
 
     @Override
-    public ParsingPackageImpl addFeature(ParsedFeature feature) {
-        this.features = CollectionUtils.add(this.features, feature);
+    public ParsingPackageImpl addAttribution(ParsedAttribution attribution) {
+        this.attributions = CollectionUtils.add(this.attributions, attribution);
         return this;
     }
 
@@ -904,7 +909,7 @@
         appInfo.volumeUuid = volumeUuid;
         appInfo.zygotePreloadName = zygotePreloadName;
         appInfo.crossProfile = isCrossProfile();
-
+        appInfo.setGwpAsanEnabled(enableGwpAsan);
         appInfo.setBaseCodePath(baseCodePath);
         appInfo.setBaseResourcePath(baseCodePath);
         appInfo.setCodePath(codePath);
@@ -990,7 +995,7 @@
         dest.writeTypedList(this.receivers);
         dest.writeTypedList(this.services);
         dest.writeTypedList(this.providers);
-        dest.writeTypedList(this.features);
+        dest.writeTypedList(this.attributions);
         dest.writeTypedList(this.permissions);
         dest.writeTypedList(this.permissionGroups);
         dest.writeTypedList(this.instrumentations);
@@ -1086,6 +1091,7 @@
         dest.writeBoolean(this.allowNativeHeapPointerTagging);
         dest.writeBoolean(this.preserveLegacyExternalStorage);
         dest.writeArraySet(this.mimeGroups);
+        sForBoolean.parcel(this.enableGwpAsan, dest, flags);
     }
 
     public ParsingPackageImpl(Parcel in) {
@@ -1149,7 +1155,7 @@
         this.receivers = in.createTypedArrayList(ParsedActivity.CREATOR);
         this.services = in.createTypedArrayList(ParsedService.CREATOR);
         this.providers = in.createTypedArrayList(ParsedProvider.CREATOR);
-        this.features = in.createTypedArrayList(ParsedFeature.CREATOR);
+        this.attributions = in.createTypedArrayList(ParsedAttribution.CREATOR);
         this.permissions = in.createTypedArrayList(ParsedPermission.CREATOR);
         this.permissionGroups = in.createTypedArrayList(ParsedPermissionGroup.CREATOR);
         this.instrumentations = in.createTypedArrayList(ParsedInstrumentation.CREATOR);
@@ -1243,6 +1249,7 @@
         this.allowNativeHeapPointerTagging = in.readBoolean();
         this.preserveLegacyExternalStorage = in.readBoolean();
         this.mimeGroups = (ArraySet<String>) in.readArraySet(boot);
+        this.enableGwpAsan = sForBoolean.unparcel(in);
     }
 
     public static final Parcelable.Creator<ParsingPackageImpl> CREATOR =
@@ -1509,8 +1516,8 @@
 
     @NonNull
     @Override
-    public List<ParsedFeature> getFeatures() {
-        return features;
+    public List<ParsedAttribution> getAttributions() {
+        return attributions;
     }
 
     @NonNull
@@ -1965,6 +1972,12 @@
     }
 
     @Override
+    @Nullable
+    public Boolean isGwpAsanEnabled() {
+        return enableGwpAsan;
+    }
+
+    @Override
     public boolean isPartiallyDirectBootAware() {
         return partiallyDirectBootAware;
     }
@@ -2420,6 +2433,12 @@
     }
 
     @Override
+    public ParsingPackageImpl setGwpAsanEnabled(@Nullable Boolean value) {
+        enableGwpAsan = value;
+        return this;
+    }
+
+    @Override
     public ParsingPackageImpl setPartiallyDirectBootAware(boolean value) {
         partiallyDirectBootAware = value;
         return this;
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index 048b924..0b673b5 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -28,7 +28,7 @@
 import android.content.pm.PackageParser;
 import android.content.pm.ServiceInfo;
 import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.parsing.component.ParsedFeature;
+import android.content.pm.parsing.component.ParsedAttribution;
 import android.content.pm.parsing.component.ParsedInstrumentation;
 import android.content.pm.parsing.component.ParsedIntentInfo;
 import android.content.pm.parsing.component.ParsedPermission;
@@ -80,7 +80,7 @@
     List<ConfigurationInfo> getConfigPreferences();
 
     @NonNull
-    List<ParsedFeature> getFeatures();
+    List<ParsedAttribution> getAttributions();
 
     /**
      * @see PackageInfo#featureGroups
@@ -840,6 +840,13 @@
     @Nullable
     Set<String> getMimeGroups();
 
+    /**
+     * @see ApplicationInfo#enableGwpAsan
+     * @see R.styleable#AndroidManifest_enableGwpAsan
+     */
+    @Nullable
+    public Boolean isGwpAsanEnabled();
+
     // TODO(b/135203078): Hide and enforce going through PackageInfoUtils
     ApplicationInfo toAppInfoWithoutState();
 }
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index b4f2159..3ed0fd5 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -40,6 +40,7 @@
 import android.content.pm.FeatureGroupInfo;
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.PackageParserException;
@@ -48,8 +49,8 @@
 import android.content.pm.parsing.component.ComponentParseUtils;
 import android.content.pm.parsing.component.ParsedActivity;
 import android.content.pm.parsing.component.ParsedActivityUtils;
-import android.content.pm.parsing.component.ParsedFeature;
-import android.content.pm.parsing.component.ParsedFeatureUtils;
+import android.content.pm.parsing.component.ParsedAttribution;
+import android.content.pm.parsing.component.ParsedAttributionUtils;
 import android.content.pm.parsing.component.ParsedInstrumentation;
 import android.content.pm.parsing.component.ParsedInstrumentationUtils;
 import android.content.pm.parsing.component.ParsedIntentInfo;
@@ -672,7 +673,7 @@
             );
         }
 
-        if (!ParsedFeature.isCombinationValid(pkg.getFeatures())) {
+        if (!ParsedAttribution.isCombinationValid(pkg.getAttributions())) {
             return input.error(
                     INSTALL_PARSE_FAILED_BAD_MANIFEST,
                     "Combination <feature> tags are not valid"
@@ -707,8 +708,9 @@
                 return parseOverlay(input, pkg, res, parser);
             case PackageParser.TAG_KEY_SETS:
                 return parseKeySets(input, pkg, res, parser);
-            case PackageParser.TAG_FEATURE:
-                return parseFeature(input, pkg, res, parser);
+            case "feature": // TODO moltmann: Remove
+            case PackageParser.TAG_ATTRIBUTION:
+                return parseAttribution(input, pkg, res, parser);
             case PackageParser.TAG_PERMISSION_GROUP:
                 return parsePermissionGroup(input, pkg, res, parser);
             case PackageParser.TAG_PERMISSION:
@@ -917,13 +919,15 @@
         return input.success(pkg);
     }
 
-    private static ParseResult<ParsingPackage> parseFeature(ParseInput input, ParsingPackage pkg,
-            Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException {
-        ParseResult<ParsedFeature> result = ParsedFeatureUtils.parseFeature(res, parser, input);
+    private static ParseResult<ParsingPackage> parseAttribution(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser)
+            throws IOException, XmlPullParserException {
+        ParseResult<ParsedAttribution> result = ParsedAttributionUtils.parseAttribution(res,
+                parser, input);
         if (result.isError()) {
             return input.error(result);
         }
-        return input.success(pkg.addFeature(result.getResult()));
+        return input.success(pkg.addAttribution(result.getResult()));
     }
 
     private static ParseResult<ParsingPackage> parsePermissionGroup(ParseInput input,
@@ -1660,6 +1664,11 @@
                     && !ClassLoaderFactory.isValidClassLoaderName(classLoaderName)) {
                 return input.error("Invalid class loader name: " + classLoaderName);
             }
+
+            if (sa.hasValue(R.styleable.AndroidManifestApplication_enableGwpAsan)) {
+                pkg.setGwpAsanEnabled(
+                        sa.getBoolean(R.styleable.AndroidManifestApplication_enableGwpAsan, false));
+            }
         } finally {
             sa.recycle();
         }
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivity.java b/core/java/android/content/pm/parsing/component/ParsedActivity.java
index d32171d..4c93d09 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivity.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivity.java
@@ -285,14 +285,8 @@
         dest.writeBundle(this.metaData);
 
         if (windowLayout != null) {
-            dest.writeBoolean(true);
-            dest.writeInt(windowLayout.width);
-            dest.writeFloat(windowLayout.widthFraction);
-            dest.writeInt(windowLayout.height);
-            dest.writeFloat(windowLayout.heightFraction);
-            dest.writeInt(windowLayout.gravity);
-            dest.writeInt(windowLayout.minWidth);
-            dest.writeInt(windowLayout.minHeight);
+            dest.writeInt(1);
+            windowLayout.writeToParcel(dest);
         } else {
             dest.writeBoolean(false);
         }
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index 1dcf262..6e5c51a 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -17,16 +17,17 @@
 package android.content.pm.parsing.component;
 
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-
 import static android.content.pm.parsing.component.ComponentParseUtils.flag;
 
 import android.annotation.NonNull;
 import android.app.ActivityTaskManager;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageParser;
-
 import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
 import android.content.pm.parsing.ParsingUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -42,9 +43,6 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseResult;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -379,6 +377,12 @@
             }
         }
 
+        ParseResult<ActivityInfo.WindowLayout> layoutResult = resolveWindowLayout(activity, input);
+        if (layoutResult.isError()) {
+            return input.error(layoutResult);
+        }
+        activity.windowLayout = layoutResult.getResult();
+
         if (!setExported) {
             activity.exported = activity.getIntents().size() > 0;
         }
@@ -481,4 +485,35 @@
             sw.recycle();
         }
     }
+
+    /**
+     * Resolves values in {@link ActivityInfo.WindowLayout}.
+     *
+     * <p>{@link ActivityInfo.WindowLayout#windowLayoutAffinity} has a fallback metadata used in
+     * Android R and some variants of pre-R.
+     */
+    private static ParseResult<ActivityInfo.WindowLayout> resolveWindowLayout(
+            ParsedActivity activity, ParseInput input) {
+        // There isn't a metadata for us to fall back. Whatever is in layout is correct.
+        if (activity.metaData == null || !activity.metaData.containsKey(
+                PackageParser.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY)) {
+            return input.success(activity.windowLayout);
+        }
+
+        // Layout already specifies a value. We should just use that one.
+        if (activity.windowLayout != null && activity.windowLayout.windowLayoutAffinity != null) {
+            return input.success(activity.windowLayout);
+        }
+
+        String windowLayoutAffinity = activity.metaData.getString(
+                PackageParser.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY);
+        ActivityInfo.WindowLayout layout = activity.windowLayout;
+        if (layout == null) {
+            layout = new ActivityInfo.WindowLayout(-1 /* width */, -1 /* widthFraction */,
+                    -1 /* height */, -1 /* heightFraction */, Gravity.NO_GRAVITY,
+                    -1 /* minWidth */, -1 /* minHeight */);
+        }
+        layout.windowLayoutAffinity = windowLayoutAffinity;
+        return input.success(layout);
+    }
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedAttribution.java b/core/java/android/content/pm/parsing/component/ParsedAttribution.java
new file mode 100644
index 0000000..02b3c7d
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedAttribution.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.parsing.component;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArraySet;
+
+import com.android.internal.util.DataClass;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A {@link android.R.styleable#AndroidManifestAttribution &lt;attribution&gt;} tag parsed from the
+ * manifest.
+ *
+ * @hide
+ */
+@DataClass(genAidl = false)
+public class ParsedAttribution implements Parcelable {
+    /** Maximum length of attribution tag */
+    public static final int MAX_ATTRIBUTION_TAG_LEN = 50;
+
+    /** Maximum amount of attributions per package */
+    private static final int MAX_NUM_ATTRIBUTIONS = 1000;
+
+    /** Tag of the attribution */
+    public final @NonNull String tag;
+
+    /** User visible label fo the attribution */
+    public final @StringRes int label;
+
+    /** Ids of previously declared attributions this attribution inherits from */
+    public final @NonNull List<String> inheritFrom;
+
+    /**
+     * @return Is this set of attributions a valid combination for a single package?
+     */
+    public static boolean isCombinationValid(@Nullable List<ParsedAttribution> attributions) {
+        if (attributions == null) {
+            return true;
+        }
+
+        ArraySet<String> attributionTags = new ArraySet<>(attributions.size());
+        ArraySet<String> inheritFromAttributionTags = new ArraySet<>();
+
+        int numAttributions = attributions.size();
+        if (numAttributions > MAX_NUM_ATTRIBUTIONS) {
+            return false;
+        }
+
+        for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) {
+            boolean wasAdded = attributionTags.add(attributions.get(attributionNum).tag);
+            if (!wasAdded) {
+                // feature id is not unique
+                return false;
+            }
+        }
+
+        for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) {
+            ParsedAttribution feature = attributions.get(attributionNum);
+
+            int numInheritFrom = feature.inheritFrom.size();
+            for (int inheritFromNum = 0; inheritFromNum < numInheritFrom; inheritFromNum++) {
+                String inheritFrom = feature.inheritFrom.get(inheritFromNum);
+
+                if (attributionTags.contains(inheritFrom)) {
+                    // Cannot inherit from a attribution that is still defined
+                    return false;
+                }
+
+                boolean wasAdded = inheritFromAttributionTags.add(inheritFrom);
+                if (!wasAdded) {
+                    // inheritFrom is not unique
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+
+
+    // Code below generated by codegen v1.0.14.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttribution.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @android.annotation.IntDef(prefix = "MAX_", value = {
+        MAX_ATTRIBUTION_TAG_LEN,
+        MAX_NUM_ATTRIBUTIONS
+    })
+    @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+    @DataClass.Generated.Member
+    public @interface Max {}
+
+    @DataClass.Generated.Member
+    public static String maxToString(@Max int value) {
+        switch (value) {
+            case MAX_ATTRIBUTION_TAG_LEN:
+                    return "MAX_ATTRIBUTION_TAG_LEN";
+            case MAX_NUM_ATTRIBUTIONS:
+                    return "MAX_NUM_ATTRIBUTIONS";
+            default: return Integer.toHexString(value);
+        }
+    }
+
+    /**
+     * Creates a new ParsedAttribution.
+     *
+     * @param tag
+     *   Tag of the attribution
+     * @param label
+     *   User visible label fo the attribution
+     * @param inheritFrom
+     *   Ids of previously declared attributions this attribution inherits from
+     */
+    @DataClass.Generated.Member
+    public ParsedAttribution(
+            @NonNull String tag,
+            @StringRes int label,
+            @NonNull List<String> inheritFrom) {
+        this.tag = tag;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, tag);
+        this.label = label;
+        com.android.internal.util.AnnotationValidations.validate(
+                StringRes.class, null, label);
+        this.inheritFrom = inheritFrom;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, inheritFrom);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeString(tag);
+        dest.writeInt(label);
+        dest.writeStringList(inheritFrom);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected ParsedAttribution(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        String _tag = in.readString();
+        int _label = in.readInt();
+        List<String> _inheritFrom = new ArrayList<>();
+        in.readStringList(_inheritFrom);
+
+        this.tag = _tag;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, tag);
+        this.label = _label;
+        com.android.internal.util.AnnotationValidations.validate(
+                StringRes.class, null, label);
+        this.inheritFrom = _inheritFrom;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, inheritFrom);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<ParsedAttribution> CREATOR
+            = new Parcelable.Creator<ParsedAttribution>() {
+        @Override
+        public ParsedAttribution[] newArray(int size) {
+            return new ParsedAttribution[size];
+        }
+
+        @Override
+        public ParsedAttribution createFromParcel(@NonNull Parcel in) {
+            return new ParsedAttribution(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1583436566499L,
+            codegenVersion = "1.0.14",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttribution.java",
+            inputSignatures = "public static final  int MAX_ATTRIBUTION_TAG_LEN\nprivate static final  int MAX_NUM_ATTRIBUTIONS\npublic final @android.annotation.NonNull java.lang.String tag\npublic final @android.annotation.StringRes int label\npublic final @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\npublic static  boolean isCombinationValid(java.util.List<android.content.pm.parsing.component.ParsedAttribution>)\nclass ParsedAttribution extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedFeatureUtils.java b/core/java/android/content/pm/parsing/component/ParsedAttributionUtils.java
similarity index 63%
rename from core/java/android/content/pm/parsing/component/ParsedFeatureUtils.java
rename to core/java/android/content/pm/parsing/component/ParsedAttributionUtils.java
index fb52801..c4b1a0e 100644
--- a/core/java/android/content/pm/parsing/component/ParsedFeatureUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedAttributionUtils.java
@@ -34,34 +34,40 @@
 import java.util.List;
 
 /** @hide */
-public class ParsedFeatureUtils {
+public class ParsedAttributionUtils {
 
     @NonNull
-    public static ParseResult<ParsedFeature> parseFeature(Resources res, XmlResourceParser parser,
-            ParseInput input) throws IOException, XmlPullParserException {
-        String featureId;
+    public static ParseResult<ParsedAttribution> parseAttribution(Resources res,
+            XmlResourceParser parser, ParseInput input)
+            throws IOException, XmlPullParserException {
+        String attributionTag;
         int label;
         List<String> inheritFrom = null;
 
-        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestFeature);
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestAttribution);
         if (sa == null) {
-            return input.error("<feature> could not be parsed");
+            return input.error("<attribution> could not be parsed");
         }
 
         try {
-            featureId = sa.getNonConfigurationString(R.styleable.AndroidManifestFeature_featureId,
-                    0);
-            if (featureId == null) {
-                return input.error("<featureId> does not specify android:featureId");
+            attributionTag = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestAttribution_tag, 0);
+            if (attributionTag == null) {
+                // TODO moltmann: Remove handling of featureId
+                attributionTag = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestAttribution_featureId, 0);
+                if (attributionTag == null) {
+                    return input.error("<attribution> does not specify android:tag");
+                }
             }
-            if (featureId.length() > ParsedFeature.MAX_FEATURE_ID_LEN) {
-                return input.error("<featureId> is too long. Max length is "
-                        + ParsedFeature.MAX_FEATURE_ID_LEN);
+            if (attributionTag.length() > ParsedAttribution.MAX_ATTRIBUTION_TAG_LEN) {
+                return input.error("android:tag is too long. Max length is "
+                        + ParsedAttribution.MAX_ATTRIBUTION_TAG_LEN);
             }
 
-            label = sa.getResourceId(R.styleable.AndroidManifestFeature_label, 0);
+            label = sa.getResourceId(R.styleable.AndroidManifestAttribution_label, 0);
             if (label == Resources.ID_NULL) {
-                return input.error("<featureId> does not specify android:label");
+                return input.error("<attribution> does not specify android:label");
             }
         } finally {
             sa.recycle();
@@ -77,14 +83,15 @@
 
             String tagName = parser.getName();
             if (tagName.equals("inherit-from")) {
-                sa = res.obtainAttributes(parser, R.styleable.AndroidManifestFeatureInheritFrom);
+                sa = res.obtainAttributes(parser,
+                        R.styleable.AndroidManifestAttributionInheritFrom);
                 if (sa == null) {
                     return input.error("<inherit-from> could not be parsed");
                 }
 
                 try {
                     String inheritFromId = sa.getNonConfigurationString(
-                            R.styleable.AndroidManifestFeatureInheritFrom_featureId,0);
+                            R.styleable.AndroidManifestAttributionInheritFrom_tag, 0);
 
                     if (inheritFrom == null) {
                         inheritFrom = new ArrayList<>();
@@ -94,7 +101,7 @@
                     sa.recycle();
                 }
             } else {
-                return input.error("Bad element under <feature>: " + tagName);
+                return input.error("Bad element under <attribution>: " + tagName);
             }
         }
 
@@ -104,6 +111,6 @@
             ((ArrayList) inheritFrom).trimToSize();
         }
 
-        return input.success(new ParsedFeature(featureId, label, inheritFrom));
+        return input.success(new ParsedAttribution(attributionTag, label, inheritFrom));
     }
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedFeature.java b/core/java/android/content/pm/parsing/component/ParsedFeature.java
deleted file mode 100644
index b8a9098..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedFeature.java
+++ /dev/null
@@ -1,229 +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 android.content.pm.parsing.component;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.StringRes;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.ArraySet;
-
-import com.android.internal.util.DataClass;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A {@link android.R.styleable#AndroidManifestFeature &lt;feature&gt;} tag parsed from the
- * manifest.
- *
- * @hide
- */
-@DataClass(genAidl = false)
-public class ParsedFeature implements Parcelable {
-    /** Maximum length of featureId */
-    public static final int MAX_FEATURE_ID_LEN = 50;
-
-    /** Maximum amount of features per package */
-    private static final int MAX_NUM_FEATURES = 1000;
-
-    /** Id of the feature */
-    public final @NonNull String id;
-
-    /** User visible label fo the feature */
-    public final @StringRes int label;
-
-    /** Ids of previously declared features this feature inherits from */
-    public final @NonNull List<String> inheritFrom;
-
-    /**
-     * @return Is this set of features a valid combination for a single package?
-     */
-    public static boolean isCombinationValid(@Nullable List<ParsedFeature> features) {
-        if (features == null) {
-            return true;
-        }
-
-        ArraySet<String> featureIds = new ArraySet<>(features.size());
-        ArraySet<String> inheritFromFeatureIds = new ArraySet<>();
-
-        int numFeatures = features.size();
-        if (numFeatures > MAX_NUM_FEATURES) {
-            return false;
-        }
-
-        for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
-            boolean wasAdded = featureIds.add(features.get(featureNum).id);
-            if (!wasAdded) {
-                // feature id is not unique
-                return false;
-            }
-        }
-
-        for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
-            ParsedFeature feature = features.get(featureNum);
-
-            int numInheritFrom = feature.inheritFrom.size();
-            for (int inheritFromNum = 0; inheritFromNum < numInheritFrom; inheritFromNum++) {
-                String inheritFrom = feature.inheritFrom.get(inheritFromNum);
-
-                if (featureIds.contains(inheritFrom)) {
-                    // Cannot inherit from a feature that is still defined
-                    return false;
-                }
-
-                boolean wasAdded = inheritFromFeatureIds.add(inheritFrom);
-                if (!wasAdded) {
-                    // inheritFrom is not unique
-                    return false;
-                }
-            }
-        }
-
-        return true;
-    }
-
-
-
-    // Code below generated by codegen v1.0.14.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedFeature.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    @android.annotation.IntDef(prefix = "MAX_", value = {
-        MAX_FEATURE_ID_LEN,
-        MAX_NUM_FEATURES
-    })
-    @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
-    @DataClass.Generated.Member
-    public @interface Max {}
-
-    @DataClass.Generated.Member
-    public static String maxToString(@Max int value) {
-        switch (value) {
-            case MAX_FEATURE_ID_LEN:
-                    return "MAX_FEATURE_ID_LEN";
-            case MAX_NUM_FEATURES:
-                    return "MAX_NUM_FEATURES";
-            default: return Integer.toHexString(value);
-        }
-    }
-
-    /**
-     * Creates a new ParsedFeature.
-     *
-     * @param id
-     *   Id of the feature
-     * @param label
-     *   User visible label fo the feature
-     * @param inheritFrom
-     *   Ids of previously declared features this feature inherits from
-     */
-    @DataClass.Generated.Member
-    public ParsedFeature(
-            @NonNull String id,
-            @StringRes int label,
-            @NonNull List<String> inheritFrom) {
-        this.id = id;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, id);
-        this.label = label;
-        com.android.internal.util.AnnotationValidations.validate(
-                StringRes.class, null, label);
-        this.inheritFrom = inheritFrom;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, inheritFrom);
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        // You can override field parcelling by defining methods like:
-        // void parcelFieldName(Parcel dest, int flags) { ... }
-
-        dest.writeString(id);
-        dest.writeInt(label);
-        dest.writeStringList(inheritFrom);
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public int describeContents() { return 0; }
-
-    /** @hide */
-    @SuppressWarnings({"unchecked", "RedundantCast"})
-    @DataClass.Generated.Member
-    protected ParsedFeature(@NonNull Parcel in) {
-        // You can override field unparcelling by defining methods like:
-        // static FieldType unparcelFieldName(Parcel in) { ... }
-
-        String _id = in.readString();
-        int _label = in.readInt();
-        List<String> _inheritFrom = new ArrayList<>();
-        in.readStringList(_inheritFrom);
-
-        this.id = _id;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, id);
-        this.label = _label;
-        com.android.internal.util.AnnotationValidations.validate(
-                StringRes.class, null, label);
-        this.inheritFrom = _inheritFrom;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, inheritFrom);
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public static final @NonNull Parcelable.Creator<ParsedFeature> CREATOR
-            = new Parcelable.Creator<ParsedFeature>() {
-        @Override
-        public ParsedFeature[] newArray(int size) {
-            return new ParsedFeature[size];
-        }
-
-        @Override
-        public ParsedFeature createFromParcel(@NonNull Parcel in) {
-            return new ParsedFeature(in);
-        }
-    };
-
-    @DataClass.Generated(
-            time = 1581379861853L,
-            codegenVersion = "1.0.14",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedFeature.java",
-            inputSignatures = "public static final  int MAX_FEATURE_ID_LEN\nprivate static final  int MAX_NUM_FEATURES\npublic final @android.annotation.NonNull java.lang.String id\npublic final @android.annotation.StringRes int label\npublic final @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\npublic static  boolean isCombinationValid(java.util.List<android.content.pm.parsing.component.ParsedFeature>)\nclass ParsedFeature extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=false)")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcess.java b/core/java/android/content/pm/parsing/component/ParsedProcess.java
index da7bf98..3b6020a 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcess.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcess.java
@@ -41,6 +41,9 @@
     @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringSet.class)
     protected Set<String> deniedPermissions = emptySet();
 
+    @Nullable
+    protected Boolean enableGwpAsan = null;
+
     public ParsedProcess() {
     }
 
@@ -71,13 +74,15 @@
     @DataClass.Generated.Member
     public ParsedProcess(
             @NonNull String name,
-            @NonNull Set<String> deniedPermissions) {
+            @NonNull Set<String> deniedPermissions,
+            @Nullable Boolean enableGwpAsan) {
         this.name = name;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, name);
         this.deniedPermissions = deniedPermissions;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, deniedPermissions);
+        this.enableGwpAsan = enableGwpAsan;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -93,6 +98,11 @@
     }
 
     @DataClass.Generated.Member
+    public @Nullable Boolean getEnableGwpAsan() {
+        return enableGwpAsan;
+    }
+
+    @DataClass.Generated.Member
     static Parcelling<Set<String>> sParcellingForDeniedPermissions =
             Parcelling.Cache.get(
                     Parcelling.BuiltIn.ForInternedStringSet.class);
@@ -109,8 +119,12 @@
         // You can override field parcelling by defining methods like:
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
+        byte flg = 0;
+        if (enableGwpAsan != null) flg |= 0x4;
+        dest.writeByte(flg);
         dest.writeString(name);
         sParcellingForDeniedPermissions.parcel(deniedPermissions, dest, flags);
+        if (enableGwpAsan != null) dest.writeBoolean(enableGwpAsan);
     }
 
     @Override
@@ -124,8 +138,10 @@
         // You can override field unparcelling by defining methods like:
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
+        byte flg = in.readByte();
         String _name = in.readString();
         Set<String> _deniedPermissions = sParcellingForDeniedPermissions.unparcel(in);
+        Boolean _enableGwpAsan = (flg & 0x4) == 0 ? null : (Boolean) in.readBoolean();
 
         this.name = _name;
         com.android.internal.util.AnnotationValidations.validate(
@@ -133,6 +149,7 @@
         this.deniedPermissions = _deniedPermissions;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, deniedPermissions);
+        this.enableGwpAsan = _enableGwpAsan;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -152,10 +169,10 @@
     };
 
     @DataClass.Generated(
-            time = 1581452315946L,
+            time = 1582589960479L,
             codegenVersion = "1.0.14",
             sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcess.java",
-            inputSignatures = "protected @android.annotation.NonNull java.lang.String name\nprotected @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\npublic  void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\nclass ParsedProcess extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
+            inputSignatures = "protected @android.annotation.NonNull java.lang.String name\nprotected @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprotected @android.annotation.Nullable java.lang.Boolean enableGwpAsan\npublic  void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\nclass ParsedProcess extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
index 4825066..3820790 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
@@ -103,6 +103,11 @@
             if (proc.name == null || proc.name.length() <= 0) {
                 return input.error("<process> does not specify android:process");
             }
+
+            if (sa.hasValue(R.styleable.AndroidManifestProcess_enableGwpAsan)) {
+                proc.enableGwpAsan =
+                        sa.getBoolean(R.styleable.AndroidManifestProcess_enableGwpAsan, false);
+            }
         } finally {
             sa.recycle();
         }
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index a091f84..972b0f55 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -486,7 +486,7 @@
                             "Camera service is currently unavailable");
                     }
                     cameraUser = cameraService.connectDevice(callbacks, cameraId,
-                            mContext.getOpPackageName(), mContext.getFeatureId(), uid);
+                            mContext.getOpPackageName(), mContext.getAttributionTag(), uid);
                 } else {
                     // Use legacy camera implementation for HAL1 devices
                     int id;
diff --git a/core/java/android/hardware/lights/Light.java b/core/java/android/hardware/lights/Light.java
index c5cb803..e90b57c 100644
--- a/core/java/android/hardware/lights/Light.java
+++ b/core/java/android/hardware/lights/Light.java
@@ -37,7 +37,8 @@
     /**
      * Creates a new light with the given data.
      *
-     * @hide */
+     * @hide
+     */
     public Light(int id, int ordinal, int type) {
         mId = id;
         mOrdinal = ordinal;
@@ -76,8 +77,24 @@
                 }
             };
 
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Light) {
+            Light light = (Light) obj;
+            return mId == light.mId && mOrdinal == light.mOrdinal && mType == light.mType;
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return mId;
+    }
+
     /**
      * Returns the id of the light.
+     *
+     * <p>This is an opaque value used as a unique identifier for the light.
      */
     public int getId() {
         return mId;
@@ -86,11 +103,9 @@
     /**
      * Returns the ordinal of the light.
      *
-     * <p>This represents the physical order of the lights on the device. The exact values are
-     * device-dependent, but for example, if there are lights in a row, sorting the Light objects
-     * by ordinal should match the order in which they appear on the device. If the device has
-     * 4 lights, the ordinals could be [1, 2, 3, 4] or [0, 10, 20, 30] or any other values that
-     * have the same sort order.
+     * <p>This is a sort key that represents the physical order of lights on the device with the
+     * same type. In the case of multiple lights arranged in a line, for example, the ordinals
+     * could be [1, 2, 3, 4], or [0, 10, 20, 30], or any other values that have the same sort order.
      */
     public int getOrdinal() {
         return mOrdinal;
diff --git a/core/java/android/hardware/lights/LightsManager.java b/core/java/android/hardware/lights/LightsManager.java
index 1bc051b..8cd2312 100644
--- a/core/java/android/hardware/lights/LightsManager.java
+++ b/core/java/android/hardware/lights/LightsManager.java
@@ -161,7 +161,7 @@
          * @param request the settings for lights that should change
          */
         @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
-        public void setLights(@NonNull LightsRequest request) {
+        public void requestLights(@NonNull LightsRequest request) {
             Preconditions.checkNotNull(request);
             if (!mClosed) {
                 try {
diff --git a/core/java/android/hardware/lights/LightsRequest.java b/core/java/android/hardware/lights/LightsRequest.java
index a36da4c..5c4fc67 100644
--- a/core/java/android/hardware/lights/LightsRequest.java
+++ b/core/java/android/hardware/lights/LightsRequest.java
@@ -86,7 +86,7 @@
          * Create a LightsRequest object used to override lights on the device.
          *
          * <p>The generated {@link LightsRequest} should be used in
-         * {@link LightsManager.Session#setLights(LightsLightsRequest).
+         * {@link LightsManager.Session#requestLights(LightsLightsRequest).
          */
         public @NonNull LightsRequest build() {
             return new LightsRequest(mChanges);
diff --git a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
index bf641d7..1aeb76a3 100644
--- a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
+++ b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
@@ -28,7 +28,6 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.util.Slog;
 import android.util.Xml;
 
@@ -231,15 +230,7 @@
                     com.android.internal.R.styleable.VoiceEnrollmentApplication);
             keyphraseMetadata = getKeyphraseFromTypedArray(array, packageName, parseErrors);
             array.recycle();
-        } catch (XmlPullParserException e) {
-            String error = "Error parsing keyphrase enrollment meta-data for " + packageName;
-            parseErrors.add(error + ": " + e);
-            Slog.w(TAG, error, e);
-        } catch (IOException e) {
-            String error = "Error parsing keyphrase enrollment meta-data for " + packageName;
-            parseErrors.add(error + ": " + e);
-            Slog.w(TAG, error, e);
-        } catch (PackageManager.NameNotFoundException e) {
+        } catch (XmlPullParserException | PackageManager.NameNotFoundException | IOException e) {
             String error = "Error parsing keyphrase enrollment meta-data for " + packageName;
             parseErrors.add(error + ": " + e);
             Slog.w(TAG, error, e);
@@ -390,7 +381,6 @@
      *         False if not.
      */
     public boolean isUidSupportedEnrollmentApplication(int uid) {
-        Log.d(TAG, "isUidSupportedEnrollmentApplication: " + toString());
         return mEnrollmentApplicationUids.contains(uid);
     }
 
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index 0513fee..6efd03c 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -28,6 +28,7 @@
 import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
+import android.view.View;
 import android.view.WindowManager;
 
 import java.lang.annotation.Retention;
@@ -94,6 +95,13 @@
                 lp.token = token;
                 getWindow().setAttributes(lp);
                 updateWindowState(SoftInputWindowState.TOKEN_SET);
+
+                // As soon as we have a token, make sure the window is added (but not shown) by
+                // setting visibility to INVISIBLE and calling show() on Dialog. Note that
+                // WindowInsetsController.OnControllableInsetsChangedListener relies on the window
+                // being added to function.
+                getWindow().getDecorView().setVisibility(View.INVISIBLE);
+                show();
                 return;
             case SoftInputWindowState.TOKEN_SET:
             case SoftInputWindowState.SHOWN_AT_LEAST_ONCE:
diff --git a/core/java/android/os/ConfigUpdate.java b/core/java/android/os/ConfigUpdate.java
index 9c999b2..590fbb3 100644
--- a/core/java/android/os/ConfigUpdate.java
+++ b/core/java/android/os/ConfigUpdate.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 
 /**
@@ -114,20 +115,36 @@
             = "android.os.action.UPDATE_CARRIER_ID_DB";
 
     /**
-    * Broadcast intent action indicating that the updated emergency number database is available.
-    * <p>Extra: "VERSION" the numeric version of the new data. Devices should only install if the
-    * update version is newer than the current one.
-    * <p>Extra: "REQUIRED_HASH" the hash of the current update data.
-    * <p>Input: {@link android.content.Intent#getData} is URI of downloaded emergency number file.
-    * Devices should pick up the downloaded file and persist to the database
-    * {@code com.android.internal.telephony.emergency.EmergencyNumberTracker}.
+    * Update the emergency number database into the devices.
+    * <p>Extra: {@link #EXTRA_VERSION} the numeric version of the database.
+    * <p>Extra: {@link #EXTRA_REQUIRED_HASH} the hash of the database.
+    * <p>Input: {@link android.content.Intent#getData} the URI to download emergency number
+    * database.
     *
     * @hide
     */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.UPDATE_CONFIG)
     public static final String ACTION_UPDATE_EMERGENCY_NUMBER_DB =
             "android.os.action.UPDATE_EMERGENCY_NUMBER_DB";
 
+    /**
+     * An integer to indicate the numeric version of the new data. Devices should only install
+     * if the update version is newer than the current one.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_VERSION = "android.os.extra.VERSION";
+
+    /**
+     * A string to indicate the hash of the data.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_REQUIRED_HASH = "android.os.extra.REQUIRED_HASH";
+
     private ConfigUpdate() {
     }
 }
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index b478dbe..a7e3263 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -77,6 +77,7 @@
     @UnsupportedAppUsage
     final MessageQueue mQueue;
     final Thread mThread;
+    private boolean mInLoop;
 
     @UnsupportedAppUsage
     private Printer mLogging;
@@ -155,6 +156,12 @@
         if (me == null) {
             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
         }
+        if (me.mInLoop) {
+            Slog.w(TAG, "Loop again would have the queued messages be executed"
+                    + " before this one completed.");
+        }
+
+        me.mInLoop = true;
         final MessageQueue queue = me.mQueue;
 
         // Make sure the identity of this thread is that of the local process,
diff --git a/core/java/android/os/Users.md b/core/java/android/os/Users.md
new file mode 100644
index 0000000..3bbbe54
--- /dev/null
+++ b/core/java/android/os/Users.md
@@ -0,0 +1,109 @@
+<!--
+  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
+  -->
+
+# Users for system developers
+
+## Concepts
+
+### User
+
+A user of a device e.g. usually a human being. Each user has its own home screen.
+
+#### User Profile
+
+A user can have multiple profiles. E.g. one for the private life and one for work. Each profile
+has a different set of apps and accounts but they share one home screen. All profiles of a
+profile group can be active at the same time.
+
+Each profile has a separate [`userId`](#int-userid). Unless needed user profiles are treated as
+completely separate users.
+
+#### Profile Group
+
+All user profiles that share a home screen. You can list the profiles of a user via
+`UserManager#getEnabledProfiles` (you usually don't deal with disabled profiles)
+
+#### Foreground user vs background user
+
+Only a single user profile group can be in the foreground. This is the user profile the user
+currently interacts with.
+
+#### Parent user (profile)
+
+The main profile of a profile group, usually the personal (as opposed to work) profile. Get this via
+`UserManager#getProfileParent` (returns `null` if the user does not have profiles)
+
+#### Managed user (profile)
+
+The other profiles of a profile group. The name comes from the fact that these profiles are usually
+managed by a device policy controller app. You can create a managed profile from within the device
+policy controller app on your phone.
+
+#### Account
+
+An account of a user profile with a (usually internet based) service. E.g. aname@gmail.com or
+aname@yahoo.com. Each profile can have multiple accounts. A profile does not have to have a
+account.
+
+## Data types
+
+### int userId
+
+... usually marked as `@UserIdInt`
+
+The id of a user profile. List all users via `adb shell dumpsys user`. There is no data type for a
+user, all you can do is using the user id of the parent profile as a proxy for the user.
+
+### int uid
+
+Identity of an app. This is the same as a Linux uid, but in Android there is one uid per package,
+per user.
+
+It is highly discouraged, but uids can be shared between multiple packages using the
+`android:sharedUserId` manifest attribute.
+
+### class UserHandle
+
+A wrapper for userId. Used esp. in public APIs instead of `int userId` as it clearly distinguishes
+from uid.
+
+## Security model
+
+Multiple packages can share an uid by using `android:sharedUserId` manifest attribute. If packages
+share a uid they can run in the same process via `android:process` manifest attribute. Further file
+level access is also tracked by uid. Hence any security or privacy mechanism needs to be built on
+a uid granularity.
+
+On the other hand apps belonging to the same user cannot see each others files. They can only
+interact via activity launches, broadcasts, providers, and service bindings. All of them can be be
+protected by [permissions](../permission/Permissions.md). Hence any new general communication
+mechanism should be access controlled by permissions.
+
+## Lifecycle
+
+A system service should deal with users being started and stopped by overriding
+`SystemService.onSwitchUser` and `SystemService.onStopUser`.
+
+If users profiles become inactive the system should stop all apps of this profile from interacting
+with other apps or the system.
+
+Another important lifecycle event is `onUnlockUser`. Only for unlocked user profiles you can access
+all data, e.g. which packages are installed.
+
+You only want to deal with user profiles that
+
+- are in the profile group of the foreground user
+- the user profile is unlocked and not yet stopped
diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl
index 0483514..f011395 100644
--- a/core/java/android/permission/IPermissionController.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -42,6 +42,6 @@
     void setRuntimePermissionGrantStateByDeviceAdmin(String callerPackageName, String packageName,
                 String permission, int grantState, in AndroidFuture callback);
     void grantOrUpgradeDefaultRuntimePermissions(in AndroidFuture callback);
-    void updateUserSensitive(in AndroidFuture callback);
     void notifyOneTimePermissionSessionTimeout(String packageName);
+    void updateUserSensitiveForApp(int uid, in AndroidFuture callback);
 }
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index 2a1857f..f08e3d25 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -46,6 +46,7 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Process;
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -626,14 +627,26 @@
     }
 
     /**
-     * @see PermissionControllerService#onUpdateUserSensitive()
+     * @see PermissionControllerManager#updateUserSensitiveForApp
      * @hide
      */
     public void updateUserSensitive() {
+        updateUserSensitiveForApp(Process.INVALID_UID);
+    }
+
+    /**
+     * @see PermissionControllerService#onUpdateUserSensitiveForApp
+     * @hide
+     */
+    public void updateUserSensitiveForApp(int uid) {
         mRemoteService.postAsync(service -> {
             AndroidFuture<Void> future = new AndroidFuture<>();
-            service.updateUserSensitive(future);
+            service.updateUserSensitiveForApp(uid, future);
             return future;
+        }).whenComplete((res, err) -> {
+            if (err != null) {
+                Log.e(TAG, "Error updating user_sensitive flags for uid " + uid, err);
+            }
         });
     }
 
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index 263b2c7..4a42230 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -218,11 +218,14 @@
      * Called by system to update the
      * {@link PackageManager}{@code .FLAG_PERMISSION_USER_SENSITIVE_WHEN_*} flags for permissions.
      * <p>
-     * This is typically when creating a new user or upgrading either system or
-     * permission controller package.
+     *
+     * If uid is -1, updates the permission flags for all packages.
+     *
+     * Typically called by the system when a new app is installed or updated or when creating a
+     * new user or upgrading either system or permission controller package.
      */
     @BinderThread
-    public void onUpdateUserSensitivePermissionFlags() {
+    public void onUpdateUserSensitivePermissionFlags(int uid, @NonNull Runnable callback) {
         throw new AbstractMethodError("Must be overridden in implementing class");
     }
 
@@ -459,11 +462,14 @@
             }
 
             @Override
-            public void updateUserSensitive(AndroidFuture callback) {
+            public void updateUserSensitiveForApp(int uid, @NonNull AndroidFuture callback) {
                 Preconditions.checkNotNull(callback, "callback cannot be null");
 
-                onUpdateUserSensitivePermissionFlags();
-                callback.complete(null);
+                try {
+                    onUpdateUserSensitivePermissionFlags(uid, () -> callback.complete(null));
+                } catch (Exception e) {
+                    callback.completeExceptionally(e);
+                }
             }
 
             @Override
diff --git a/core/java/android/permission/Permissions.md b/core/java/android/permission/Permissions.md
new file mode 100644
index 0000000..e62bd0a
--- /dev/null
+++ b/core/java/android/permission/Permissions.md
@@ -0,0 +1,832 @@
+<!--
+  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
+  -->
+
+# Android permissions for system developers
+
+This document targets system developers. App developers should refer to the [public
+ documentation](https://developer.android.com/guide/topics/permissions/overview).
+
+## Definitions
+
+Each app (often called package) has a unique name called package name. Each package has a manifest
+file describing properties of the package. The android system server is in a special package named
+"android".
+
+When a package gets installed the package is (usually) assigned a unique identifier called [uid
+](../os/Users.md#int-uid).
+This is the same as a uid in Linux, but this often leads to confusion. It is easiest to see a uid
+as a unique identifier for a package.
+
+Usually an app is running in a container called a process. Each process has a unique id called
+pid, but unlike the uid the pid changes each time the process is restarted and app that are not
+currently running don't have a pid. The process container makes sure that other apps cannot
+negatively interact with an app. Processes can only interact via controlled interactions called
+remote procedure calls (RPCs). Android’s RPC mechanism is called _Binder_.
+
+As no app code can be trusted the permission need to be checked on the receiving side of the
+Binder call.
+
+For more details please take a look at [Android's security model](../os/Users.md#security-model).
+
+## Permissions for regular apps
+
+### Install time permissions
+
+The purpose of install time permissions is to control access to APIs where it does not makes sense
+to involve the user. This can be either because the API is not sensitive, or because additional
+checks exist.
+
+#### Defining a permission
+
+Any package can define a permission. For that it simply adds an entry in the manifest file
+`<permission android:name="com.example.myapp.myfirstpermission" />`
+
+Any package can do this, including the system package. When talking about [permissions for system
+ apps](#permissions-for-system-apps) we will see that it is important which package defines a
+permission.
+
+It is common good practice to prefix the permission name with the package name to avoid collisions.
+
+#### Requesting a permission
+
+Any app can request any permission via adding an entry in the manifest file like
+`<uses-permission android:name="com.example.myapp.myfirstpermission" />`
+
+A requested permission does not necessarily mean that the permission is granted. When and how a
+permission is granted depends on the protection level of the permission. If no protection level is
+set, the permission will always be granted. Such "normal" permissions can still be useful as it
+will be easy to find apps using a certain functionality on app stores and by checking `dumpsys
+package`.
+
+#### Checking a permission
+
+`Context.checkPermission(permission, pid, uid)` returns if the pid/uid has the permission. By far
+the most common case is to check the permission on the receiving end of a binder call. In this case
+the pid can be read as `Binder.callingPid()` and the uid as `Binder.callingUid()`. The uid is a
+mandatory argument as permissions are maintained per uid. The pid can be set to -1
+if not pid is available. The context class contains handy wrappers for `checkPermission`, such as
+`enforeCallingPermission` which calls checkPermission with `Binder.callingPid`/`Binder.callingUid`
+and throws a SecurityException when the permission is not granted.
+
+#### Verifying an app has an install time permission
+
+In `dumpsys package my.package.name` there are two sections. In requested permissions all
+permissions of the `uses-permission` tags are listed. In install permission the permissions with
+their grant state are listed. If an install time permission is not listed here, it is not granted.
+
+```
+Packages:
+  Package [com.android.packageinstaller] (2eb7062):
+    userId=10071
+    [...]
+    requested permissions:
+      android.permission.MANAGE_USERS
+      android.permission.INSTALL_PACKAGES
+      android.permission.DELETE_PACKAGES
+      android.permission.READ_INSTALL_SESSIONS
+      android.permission.RECEIVE_BOOT_COMPLETED
+      android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS
+      android.permission.USE_RESERVED_DISK
+      android.permission.UPDATE_APP_OPS_STATS
+      android.permission.MANAGE_APP_OPS_MODES
+      android.permission.INTERACT_ACROSS_USERS_FULL
+      android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME
+      android.permission.PACKAGE_USAGE_STATS
+    install permissions:
+      android.permission.USE_RESERVED_DISK: granted=true
+      android.permission.INSTALL_PACKAGES: granted=true
+      android.permission.RECEIVE_BOOT_COMPLETED: granted=true
+      android.permission.INTERACT_ACROSS_USERS_FULL: granted=true
+      android.permission.PACKAGE_USAGE_STATS: granted=true
+      android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME: granted=true
+      android.permission.READ_INSTALL_SESSIONS: granted=true
+      android.permission.MANAGE_USERS: granted=true
+      android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS: granted=true
+      android.permission.MANAGE_APP_OPS_MODES: granted=true
+      android.permission.UPDATE_APP_OPS_STATS: granted=true
+      android.permission.DELETE_PACKAGES: granted=true
+```
+
+#### End-to-end: Protecting an RPC call via a permission
+
+##### Service Manifest
+
+```xml
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.example.myservice">
+    <!-- Define a permission -->
+    <permission android:name="com.android.example.myservice.MY_PERMISSION" />
+    <application>
+        <service android:name=".MyService" android:exported="true" />
+    </application>
+</manifest>
+```
+
+##### Service code
+
+```kotlin
+class MyService : Service() {
+    override fun onBind(intent: Intent?): IBinder? {
+        return object : IMyService.Stub() {
+            override fun doSomething() {
+                // Verify that calling UID has the permission
+                enforceCallingPermission(
+                    "com.android.example.myservice.MY_PERMISSION",
+                    "Need to hold permission"
+                )
+                // do something
+            }
+        }.asBinder()
+    }
+}
+```
+
+##### Caller Manifest
+
+```xml
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.example.myapp">
+    <!-- request a permission -->
+    <uses-permission android:name="com.android.example.myservice.MY_PERMISSION" />
+    <application />
+</manifest>
+```
+
+### Runtime permissions
+
+Runtime permission must be granted by the user during runtime. This is needed if the API protects
+data or functionality that is sensitive for the user. E.g. the users current location is protected
+by a runtime permission.
+
+Users want a system that is secure and privacy focused by default. User can also often not make a
+good choice when asked at the wrong time without enough context. Hence in general runtime
+permissions should be avoided and the API should be built in a way where no private data needs to be
+leaked.
+
+#### Defining a runtime permission
+
+Runtime permissions are defined in the same way as install time permissions. To tag them as runtime
+permissions the `protectionLevel` needs to be set to dangerous. Dangerous is a synonym for
+runtime permissions in the Android platform.
+
+```xml
+<uses-permission android:name="com.example.myapp.myfirstruntimepermission"
+    android:protectionLevel="dangerous" />
+```
+
+#### Requesting a runtime permission
+
+Similar to install time permissions any app can request a runtime permission by adding the 
+`<uses-permission android:name="com.example.myapp.myfirstruntimepermission" />`
+to the manifest.
+
+By default runtime permissions are not granted. The app needs to call `Activity.requestPermissions`
+during runtime to ask the user for the permission. The user might then grant or deny and once the
+decision is made the activity is called by via `Activity.onPermissionGranted`.
+
+During development and testing a runtime permission can be granted via the `pm` shell command or by
+using the `UiAutomator.grantRuntimePermission` API call. Please note that this does _not_ grant the
+[app-op](#runtime-permissions-and-app-ops) synchronously. Unless the app needs to test the actual
+permission grant flow it is recommended to grant the runtime permissions during install using
+`adb install -g /my/package.apk`.
+
+#### Checking a runtime permission
+
+For runtime permissions defined by a 3rd party apps it is fine to check a runtime
+permission like an install time permission. For system defined permissions you need to check all
+runtime permissions by using the `PermissionChecker` utility. It is good practice to use the tool
+anywhere possible.
+
+The permission checker might return `PERMISSION_DENIED_APP_OP` which should lead to a silent
+failure. This can only happen for system defined runtime permissions.
+
+##### Runtime permissions and app-ops
+
+> See [App-ops](../app/AppOps.md).
+
+The PermissionChecker code fundamentally looks like this:
+
+```kotlin
+class PermissionChecker {
+    fun checkCallingPermission(context: Context, permission: String) {
+        if (isRuntimePermission(permission)) {
+            if (context.checkPermission(uid, permission) == DENIED) {
+                 return PERMISSION_DENIED
+            }
+
+            val appOpOfPermission = AppOpsManager.permissionToOp(permission)
+            if (appOpOfPermission == null) {
+                // not platform defined
+                return PERMISSION_GRANTED
+            }
+
+            val appOpMode = appOpsManager.noteOp(appOpOfPermission)
+            if (appOpMode == AppOpsManager.MODE_ALLOWED) {
+                return PERMISSION_GRANTED
+            } else {
+                return PERMISSION_DENIED_APP_OP
+            }
+        } else {
+            return PERMISSION_DENIED
+        }
+    }
+}
+```
+
+For each platform defined runtime permission there is a matching app-op. When calling
+`AppOpsManager.noteOp` this returns either `MODE_ALLOWED` or `MODE_IGNORED`.
+
+This value is then used to decide between `PERMISSION_DENIED_APP_OP` and `PERMISSION_GRANTED`.
+
+The primary purpose of the special `PERMISSION_DENIED_APP_OP` state was to support apps targeting an
+SDK lower than 23. These apps do not understand the concept of denied runtime permissions. Hence
+they would crash when getting a `SecurityException`. To protect the users' privacy while still not
+crashing the app the special `PERMISSION_DENIED_APP_OP` mandates that the API should somehow
+silently fail.
+
+A secondary use case of the `AppOpsManager.noteOp` calls is to
+[track](../app/AppOps.md#Appops-for-tracking) which apps perform what runtime protected actions.
+
+#### Verifying an app has a runtime time permission
+
+In `dumpsys package my.package.name` the runtime permissions are listed per uid. I.e. different
+users might have different runtime permission grants and shared uids share a grant-set. If a runtime
+permission is listed as requested but not in the runtime permission section it is in it’s initial
+state, i.e. not granted.
+
+```
+Packages:
+  Package [com.google.android.GoogleCamera] (ccb6af):
+    userId=10181
+    [...]
+    requested permissions:
+      android.permission.ACCESS_COARSE_LOCATION
+      android.permission.ACCESS_FINE_LOCATION
+      android.permission.ACCESS_NETWORK_STATE
+      android.permission.ACCESS_NOTIFICATION_POLICY
+      android.permission.ACCESS_WIFI_STATE
+      android.permission.BIND_WALLPAPER
+      android.permission.CAMERA
+      android.permission.CHANGE_WIFI_STATE
+      android.permission.INTERNET
+      android.permission.GET_PACKAGE_SIZE
+      android.permission.NFC
+      android.permission.READ_SYNC_SETTINGS
+      android.permission.RECEIVE_BOOT_COMPLETED
+      android.permission.RECORD_AUDIO
+      android.permission.SET_WALLPAPER
+      android.permission.USE_CREDENTIALS
+      android.permission.VIBRATE
+      android.permission.WAKE_LOCK
+      android.permission.WRITE_EXTERNAL_STORAGE [ ... ]
+      android.permission.WRITE_SETTINGS
+      android.permission.WRITE_SYNC_SETTINGS
+      com.google.android.elmyra.permission.CONFIGURE_ASSIST_GESTURE
+      com.google.android.providers.gsf.permission.READ_GSERVICES
+      android.permission.FOREGROUND_SERVICE
+      com.google.android.googlequicksearchbox.permission.LENSVIEW_BROADCAST
+      android.permission.READ_EXTERNAL_STORAGE [ ... ]
+    [...]
+    User 0: [ ... ]
+    overlay paths:
+      runtime permissions:
+        android.permission.ACCESS_FINE_LOCATION: granted=false [ ... ]
+        android.permission.READ_EXTERNAL_STORAGE: granted=true [ ... ]
+        android.permission.ACCESS_COARSE_LOCATION: granted=false [ ... ]
+        android.permission.CAMERA: granted=true [ ... ]
+        android.permission.WRITE_EXTERNAL_STORAGE: granted=true [ ... ]
+        android.permission.RECORD_AUDIO: granted=true[ ... ]
+```
+
+#### End-to-end: Protecting an RPC call via a runtime permission
+
+##### Service Manifest
+
+```xml
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.example.myservice">
+    <!-- Define a runtime permission -->
+    <permission android:name="com.android.example.myservice.MY_RUNTIME_PERMISSION"
+        android:protectionLevel="dangerous" />
+    <application>
+        <service android:name=".MyService" android:exported="true" />
+    </application>
+</manifest>
+```
+
+##### Service code
+
+```kotlin
+class MyService : Service() {
+    override fun onBind(intent: Intent?): IBinder? {
+        return object : IMyService.Stub() {
+            override fun doSomething(callingPackage: String?, callingFeatureId: String?) {
+                Objects.requireNonNull(callingPackageName)
+
+                // Verify that calling UID has the permission
+                when (run {
+                    PermissionChecker.checkCallingPermission(
+                        this@MyService,
+                         "com.android.example.myservice.MY_RUNTIME_PERMISSION",
+                        callingPackageName,
+                        callingFeatureId,
+                        "Did something"
+                    )
+                }) {
+                    PERMISSION_GRANTED -> /* do something */
+                    PERMISSION_DENIED_APP_OP -> /* silent failure, do nothing */
+                    else -> throw SecurityException(
+                        "Cannot do something as caller is missing "
+                                + "com.android.example.myservice.MY_RUNTIME_PERMISSION"
+                    )
+                }
+            }
+        }.asBinder()
+    }
+}
+```
+
+##### Caller Manifest
+
+```xml
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.example.myapp">
+    <!-- request a permission -->
+    <uses-permission android:name="com.android.example.myservice.MY_RUNTIME_PERMISSION" />
+    <application />
+</manifest>
+```
+
+##### Caller code
+
+```kotlin
+class MyActivity : Activity {
+    fun callDoSomething() {
+        if (checkSelfPermission("com.android.example.myservice.MY_RUNTIME_PERMISSION") == PERMISSION_DENIED) {
+            // Interrupt operation and request permission
+            requestPermissions(arrayOf("com.android.example.myservice.MY_RUNTIME_PERMISSION"), 23)
+        } else {
+            myService.doSomething(this@MyActivity.opPackageName, this@MyActivity.featureId)
+        }
+    }
+
+    override fun onRequestPermissionsResult(
+        requestCode: Int,
+        permissions: Array<String>,
+        grantResults: IntArray
+    ) {
+        if (requestCode == 23 && grantResults[0] == PERMISSION_GRANTED) {
+            // Finish operation
+            callDoSomething()
+        }
+    }
+}
+```
+
+#### Restricted permissions
+
+Some runtime permissions are restricted. They are annotated in the platforms `AndroidManifest.xml`
+has `hardRestricted` or `softRestricted`.
+
+Restricted permissions behave uncommon when not whitelisted. When whitelisted the permissions
+behave normally. What uncommon means depends on the whether they are hard or soft restricted.
+
+They can either be whitelisted during upgrade P->Q, but the system or need to be whitelisted by the
+installer via `PackageInstaller.SessionParams.setWhitelistedRestrictedPermissions`. If this method
+is not used all permissions will be whitelisted.
+
+Afterwards the app that originally installed the app can change the whitelisting state via
+`PackageManager.addWhitelistedRestrictedPermission` and
+`PackageManager.removeWhitelistedRestrictedPermission`.
+
+The system tracks the source of the whitelisting by having three different flags
+ `RESTRICTION_SYSTEM_EXEMPT`, `RESTRICTION_UPGRADE_EXEMPT`, and `RESTRICTION_INSTALLER_EXEMPT`,
+
+The flags can be checked in `dumpsys package my.package.name`
+
+```
+User 0:
+  [...]
+  runtime permissions:
+    android.permission.READ_EXTERNAL_STORAGE: granted=false, flags=[ RESTRICTION_UPGRADE_EXEMPT ]
+    android.permission.ACCESS_FINE_LOCATION: granted=true, flags=[ RESTRICTION_SYSTEM_EXEMPT|RESTRICTION_UPGRADE_EXEMPT ]
+```
+
+##### Hard restricted
+
+Hard restricted permissions need to be whitelisted to be grant-able.
+
+##### Soft restricted
+
+The behavior of non-whitelisted soft restricted permissions is not uniform. The behavior is
+defined in the `SoftRestrictedPermissionPolicy`.
+
+#### System fixed permission
+
+Some runtime permissions are required for normal operation of the device. In this case the system
+can grant the permission as `SYSTEM_FIXED`. In this case the permission can be seen in the
+[permission management settings](#settings) but cannot be revoked by the user.
+
+The flag can be checked in `dumpsys package my.package.name`
+```
+User 0:
+  [...]
+  runtime permissions:
+    android.permission.READ_EXTERNAL_STORAGE: granted=true, flags=[ SYSTEM_FIXED|GRANTED_BY_DEFAULT ]
+```
+
+#### Background access
+
+Whether the app is currently visible to the user is reflected in the `ActivityManager`'s proc state.
+There is a lot of granularity to this, but runtime permissions are using the [app-ops services'
+](../app/AppOps.md) definition of foreground and background.
+
+Most runtime permissions are not affected by foreground/background-ness. Microphone and Camera are
+foreground-only while Location is usually foreground-only, but background access can be added by
+granting the `ACCESS_BACKGROUND_LOCATION` modifier runtime permission.
+
+##### Microphone and Camera
+
+Currently these only allow access while in the app is in foreground. There is a manual whitelist
+for e.g. the voice interaction service.
+
+This is currently (Mar 2020) reworked and will behave like [location](#location) soon.
+
+##### Location
+
+As described [above](#runtime-permissions-and-app-ops) the app-op mode for granted permissions is
+`MODE_ALLOWED` to allow access or `MODE_IGNORED` to suppress access.
+
+The important case is the case where the permission is granted and the app-op is `MODE_IGNORED`. In
+the case of location this state causes the `LocationManagerService` to stop delivering locations to
+the app. This is not a breaking behavior as the same scenarios happens if e.g. no satellites
+could be found.
+
+This behavior is used to implement the foregound/background behavior for location. If the app is
+in the foreground the app-op mode is `MODE_ALLOWED` and works normally. If the app goes into
+background the app-op mode changes to `MODE_IGNORED`. This means that locations are delivered while
+the app is in foreground and while the app is background, the app won't get any locations.
+
+The automatic switching between `MODE_ALLOWED` and `MODE_IGNORED` is done inside of
+ [`AppOpsManager`](../app/AppOps.md#foreground).
+
+Background access can be enabled by also granting the `ACCESS_BACKGROUND_LOCATION` to the app. In
+this case the app-op mode will always be `MODE_ALLOWED`.
+
+#### UI
+
+##### Granting
+
+An app following the best practices does not ask for any runtime permissions until absolutely
+needed. Once needed the request should be made in context. I.e. the user should understand from the
+current state of the app and the user's action why the request is made. E.g. if the user presses
+a "show me the next ATM"-button the user is most likely expecting a request for the location
+permission.
+
+This is central premise to the runtime permission UI. It is the app's responsibility to avoid
+showing permission requests dialogs to the user which might get denied. These dialogs are not
+meant to be user-choices, they are meant to be user-confirmations.
+
+Hence any denied permission dialog is probably due to the app asking for permissions the user
+does not expect. If too many permission requests get denied the app is apparently trying to get
+more than the user wants to give to the app. In this case the permission gets permanently denied
+and all future requests will be denied automatically without showing a UI.
+
+`Context.requestPermission` calls for more than one permission are allowed and might result in
+multiple dialogs in sequence. This might make sense for e.g. getting microphone and camera
+permission when starting a video call.
+
+Each time the the user makes a choice (either to grant or the deny) a permission request the
+permission is marked as `USER_SET`. If a permission gets permanently denied the permission is marked
+as `USER_FIXED`.
+
+This can be found in `dumpsys package my.package.name`
+```
+User 0:
+  [...]
+  runtime permissions:
+    android.permission.READ_EXTERNAL_STORAGE: granted=false, flags=[ USER_SET|USER_FIXED ]
+    android.permission.ACCESS_FINE_LOCATION: granted=true, flags=[ USER_SET ]
+```
+
+##### Settings
+
+By far most interactions with the permission system are via the [permission grant flow](#granting).
+The main purpose of the permission settings is to show the user the previous choices and allow
+the user to revisit previous choices. In reality few users do that.
+
+##### Grouping
+
+There are too many runtime permissions for the user to individually manage. Hence the UI bundles the
+permissions into groups. **Apps should never assume the grouping**. The grouping might change
+with SDK updates, but also at any other time. Certain form factors or locales might use other
+permission models and sometimes some of the permissions of a group cannot be granted for various
+reasons. The grouping is defined inside the permission controller app.
+
+If two permissions belong to a group and the first permission is already granted the second one
+will be granted on request of the app without user interaction. For that reason a permission
+group with at least one individual permission granted will show up as granted in the UI.
+
+##### Alternate permission management
+
+It is not allowed to build alternate permission management UIs. While restricting innovation is not
+a good choice this is a required one to enforce a consistent, predictable, but flexible permission
+model for users and app developers.
+
+Further some data needed for permission management (e.g. the grouping) is not available outside
+the permission controller app.
+
+Hence all permission management UI needs to be integrated with AOSP.
+
+#### Pre granting
+
+Runtime permissions protect user private data. It is a violation of user trust to give the data
+to an app without explicit user consent (i.e. the user [granting](#granting) the permission
+). Still the user expects certain functionality (e.g. receiving a phone call) to work out of the
+box.
+
+Hence the `DefaultPermissionGrantPolicy` and roles allow to grant permission without the user
+. The default permission grant policy grants permissions for three categories of apps
+- Apps running in well defined [uids](../os/Users.md#int-uid) as they are considered as part of
+ the platform
+- Apps that are in certain predefined categories, e.g. the browser and the SMS app. This is
+ meant for the most basic phone functionality, not for all pre-installed apps.
+- Apps that are explicitly mentioned as a pre-grant-exceptions. This is meant to be used for setup
+ and other highly critical use cases, not to improve the user experience. The exceptions are listed
+ in xml files in `etc/` and follow the following syntax
+```xml
+<exceptions>
+    <exception package="my.package.name">
+        <permission name="android.permission.ACCESS_FINE_LOCATION" fixed="false"/>
+    </exception>
+</exceptions>
+```
+
+Pre-granted runtime permissions can still be revoked by the user in [settings](#settings) unless
+they are granted as `SYSTEM_FIXED`.
+
+Whether a permission was granted by the default can be checked in the permission flags of
+`dumpsys package my.package.name`
+
+```
+User 0:
+  [...]
+  runtime permissions:
+    android.permission.ACCESS_FINE_LOCATION: granted=true, flags=[ GRANTED_BY_DEFAULT ]
+```
+
+### Permission restricted components
+
+As [publicly documented](https://developer.android.com/guide/topics/permissions/overview#permission_enforcement)
+it is possible to restrict starting an activity/binding to a service by using permission. It is
+a common pattern to
+
+- define a permission in the platform as `signature`
+- protect a service in an app by this permission using the `android:permission` attribute of the
+ `<service>` tag
+
+Then it is guaranteed that only the system can bind to such service. This is used for services
+that provide extensions to platform functionality, such as auto-fill services, print services, and
+accessibility services.
+
+This does not work for app-op or runtime permissions as the way to check these permissions is
+more complex than install time permissions.
+
+## Permissions for system apps
+
+System apps need to integrate deeper with the system than regular apps. Hence they need to be
+able to call APIs not available to other apps. This is implemented by granting permissions to
+these system apps and then enforcing the permissions in the API similar to other [install time
+permissions](#checking-a-permission).
+
+System apps are not different from regular apps, but the protection levels (e.g.
+[privileged](#privileged-permissions), [preinstalled](#preinstalled-permissions)) mentioned in this
+section are more commonly used by system apps.
+
+### Multiple permission levels
+
+It is possible to assign multiple protection levels to a permission. Very common combinations are
+for example adding `signature` to all permissions to make sure the platform signed apps can be
+granted the permission, e.g. `privileged|signature`.
+
+The permission will be granted if the app qualifies for _any_ of the permission levels.
+
+### App-op permissions
+
+> See [App-ops](../app/AppOps.md).
+
+App-op permissions are user-switchable permissions that are not runtime permissions. This should
+be used for permissions that are really only meant to be ever granted to a very small amount of
+apps. Traditionally granting these permissions is intentionally very heavy weight so that the
+user really needs to understand the use case. For example one use case is the
+`INTERACT_ACROSS_PROFILES` permission that allows apps of different
+[user profiles](../os/Users.md#user-profile) to interact. Of course this is breaking a very basic
+security container and hence should only every be granted with a lot of care.
+
+**Warning:** Most app-op permissions follow this logic, but most of them also have exceptions
+and special behavior. Hence this section is a guideline, not a rule.
+
+#### Defining an app-op permission
+
+Only the platform can reasonably define an app-op permission. The permission is defined in the
+platforms manifest using the `appop` protection level
+
+```xml
+<manifest package="android">
+    <permission android:name="android.permission.MY_APPOP_PERMISSION"
+        android:protectionLevel="appop|signature" />
+</manifest>
+```
+
+Almost always the protection level is app-op | something else, like
+[signature](#signature-permissions) (in the case above) or [privileged](#privileged-permissions).
+
+#### Checking a app-op permission
+
+The `PermissionChecker` utility can check app-op permissions with the [same syntax as runtime
+permissions](#checking-a-runtime-permission).
+
+The permission checker internally follows this flow
+
+```kotlin
+class PermissionChecker {
+    fun checkCallingPermission(context: Context, permission: String) {
+        if (isAppOpPermission(permission)) {
+            val appopOfPermission = AppOpsManager.permissionToOp(permission)
+            if (appopOfPermission == null) {
+                // not platform defined
+                return PERMISSION_DENIED
+            }
+
+            val appopMode = appOpsManager.noteOp(appopOfPermission)
+            when (appopMode) {
+                AppOpsManager.MODE_ALLOWED -> return PERMISSION_GRANTED
+                AppOpsManager.MODE_IGNORED -> return PERMISSION_DENIED
+                AppOpsManager.MODE_DEFAULT -> {
+                    if (context.checkPermission(uid, permission) == GRANTED) {
+                        return PERMISSION_GRANTED
+                    } else {
+                        return PERMISSION_DENIED
+                    }
+                }
+            }
+        } else {
+            return PERMISSION_DENIED
+        }
+    }
+}
+```
+
+#### Granting a app-op permission
+
+The permission's grant state is only considered if the app-op's mode is `MODE_DEFAULT`. This
+allows to have default grants while still being overridden by the app-op.
+
+The permission is then granted by setting the app-op mode. This is usually done via dedicated APIs
+for each use cases. Similarly whether and how an app can request the permission is different for
+each app-op permission.
+
+When implementing a new app-op permission, make sure to set the app-op mode using `AppOpsManager
+.setUidMode` to make sure the permission is granted on the uid as this is the security domain.
+
+During development app-ops can be grated to app via the `appops set` shell command. E.g.
+
+```
+adb shell appops set 10187 INTERACT_ACROSS_PROFILES allow
+```
+
+sets the `INTERACT_ACROSS_PROFILES` app-op for uid 10187 to allow thereby granting apps in this
+uid the ability to interact across profiles.
+
+##### UI
+
+Most UIs for app-op permissions are in the "Special app access" section of the settings app.
+
+In most cases the permission should only be granted with the user's explicit agreement, usually by
+allowing the app to directly open the "Special app access" page for this permission and app.
+
+To repeat: this is a guideline for app-op permissions and there are many exceptions.
+
+### Signature permissions
+
+Only apps signed with the defining app's certificate will be granted the permission. This is
+used to restrict APIs to apps of the same developer.
+
+This is frequently used to restrict permissions defined by the platform to apps also signed with
+the platform's certificate. As this is a very tight restriction this is recommended for
+permissions that are only used by apps built out of AOSP which are signed with the platform
+certificate.
+
+Please note that OEMs sign their platform them self. I.e. OEMs can implement new apps using these
+permissions. It is unlikely that 3rd party apps will be able to use APIs protected by signature
+permissions as they are usually not signed with the platform certificate.
+
+Such permissions are defined and checked like an install time permission.
+
+### Preinstalled permissions
+
+This means that the app has to be pre-installed. There is no restriction what apps are pre-installed
+on a particular device install there. Hence it can be really any app including 3rd party apps.
+
+Hence this permission level is discouraged unless there are
+[further restrictions](#restricted-by-tests).
+
+Such permissions are defined and checked like an install time permission.
+
+### Privileged permissions
+
+This means that the app has to be pre-installed and in the `system/priv` directory in the
+filesystem. There is no restriction what apps are in this directory on a particular device
+install there. Hence it can be really any app including 3rd party apps.
+
+An app is only ever granted privileged permissions requested by the pre-installed apk. I.e.
+privileged permissions added in updates will never be granted.
+
+Hence this permission level is discouraged unless there are
+[further restrictions](#restricted-by-tests).
+
+Such permissions are defined and checked like an install time permission.
+
+#### Restricted by tests
+
+As all apps that might get preinstalled or privilidged permissions need to be pre-installed and new
+images need to pass compliance tests it is possible to use a test to whitelist the apps that can
+request the permission.
+
+Example of such a test:
+```kotlin
+/* Add new whitelisted packages to this list */
+private val whitelistedPkgs = listOf("my.whitelisted.package")
+
+@Test
+fun onlySomeAppsAreAllowedToHavePermissionGranted() {
+    assertThat(whitelistedPkgs).containsAllIn(
+            context.packageManager.getInstalledPackages(MATCH_ALL)
+                    .filter { pkg ->
+                        context.checkPermission(android.Manifest.permission.MY_PRIVILEGED_PERMISSION, -1,
+                                pkg.applicationInfo.uid) == PERMISSION_GRANTED
+                    /* The permission is defined by the system and hence granted to it */
+                    }.filter { pkg -> pkg.applicationInfo.uid != SYSTEM_UID }
+                    .map { it.packageName }
+    )
+}
+```
+
+#### Whitelist
+
+As mentioned above it is not suggested, but still common practice to install 3rd party apps as
+privilidged. To verify and restrict which privilidged permissions those apps get granted all
+privilidged permissions need to be explicitly whitelisted in a file `/etc`.
+
+```xml
+<permissions>
+    <privapp-permissions package="my.privileged.package">
+        <!-- allow the app to request a permission -->
+        <permission name="android.permission.MY_PRIVILEGED_PERMISSION"/>
+
+        <!-- Even though the app requests the permission, do not grant it -->
+        <deny-permission name="android.permission.MY_OTHER_PRIVILEGED_PERMISSION"/>
+    </privapp-permissions>
+</permissions>
+```
+
+If the pre-installed apk of app requests a privileged permission that is not mentioned in any
+whitelist or that is not denied the system will refuse to boot. As mentioned above privileged
+permissions added in updates to the pre-installed app will never be granted.
+
+### Limited permissions
+
+E.g. installer, wellbeing, documenter, etc... This allows the system to restrict the permission to a
+well defined app or set of apps. It is possible to add new types in `PackageManagerService`.
+
+Which apps qualify for such a permission level is flexible and custom for each such level. Usually
+they refer to a single or small set of apps, usually - but not always - apps defined in AOSP.
+
+These permissions are defined and checked like an install time permission.
+
+### Development permissions
+
+> Not recommended
+
+By adding the `development` protection level to any permissions the permission can be granted via
+the `pm grant` shell command. This appears to be useful for development and testing, but it is very
+highly discouraged. Any user can grant them permanently via adb, hence adding this tag removes
+all guarantees the permission might otherwise provide.
+
+### Other protection levels
+
+There are other levels (such as `runtime`) but they are for special purposes on should not be
+used by platform developers.
diff --git a/core/java/android/provider/BlockedNumberContract.java b/core/java/android/provider/BlockedNumberContract.java
index 1eb7664..dd2ea81 100644
--- a/core/java/android/provider/BlockedNumberContract.java
+++ b/core/java/android/provider/BlockedNumberContract.java
@@ -16,7 +16,6 @@
 package android.provider;
 
 import android.annotation.IntDef;
-import android.annotation.SystemApi;
 import android.annotation.WorkerThread;
 import android.content.Context;
 import android.net.Uri;
@@ -240,7 +239,6 @@
      * blocked.
      * @hide
      */
-    @SystemApi
     public static final int STATUS_NOT_BLOCKED = 0;
 
     /**
@@ -248,7 +246,6 @@
      * because it is in the list of blocked numbers maintained by the provider.
      * @hide
      */
-    @SystemApi
     public static final int STATUS_BLOCKED_IN_LIST = 1;
 
     /**
@@ -256,7 +253,6 @@
      * because it is from a restricted number.
      * @hide
      */
-    @SystemApi
     public static final int STATUS_BLOCKED_RESTRICTED = 2;
 
     /**
@@ -264,7 +260,6 @@
      * because it is from an unknown number.
      * @hide
      */
-    @SystemApi
     public static final int STATUS_BLOCKED_UNKNOWN_NUMBER = 3;
 
     /**
@@ -272,7 +267,6 @@
      * because it is from a pay phone.
      * @hide
      */
-    @SystemApi
     public static final int STATUS_BLOCKED_PAYPHONE = 4;
 
     /**
@@ -280,14 +274,12 @@
      * because it is from a number not in the users contacts.
      * @hide
      */
-    @SystemApi
     public static final int STATUS_BLOCKED_NOT_IN_CONTACTS = 5;
 
     /**
      * Integer reason indicating whether a call was blocked, and if so why.
      * @hide
      */
-    @SystemApi
     public static final String RES_BLOCK_STATUS = "block_status";
 
     /** @hide */
@@ -298,31 +290,6 @@
             "can_current_user_block_numbers";
 
     /** @hide */
-    @SystemApi
-    public static final String METHOD_NOTIFY_EMERGENCY_CONTACT = "notify_emergency_contact";
-
-    /** @hide */
-    public static final String METHOD_END_BLOCK_SUPPRESSION = "end_block_suppression";
-
-    /** @hide */
-    @SystemApi
-    public static final String METHOD_SHOULD_SYSTEM_BLOCK_NUMBER = "should_system_block_number";
-
-    /** @hide */
-    public static final String METHOD_GET_BLOCK_SUPPRESSION_STATUS =
-            "get_block_suppression_status";
-
-    /** @hide */
-    public static final String METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION =
-            "should_show_emergency_call_notification";
-
-    /** @hide */
-    public static final String METHOD_GET_ENHANCED_BLOCK_SETTING = "get_enhanced_block_setting";
-
-    /** @hide */
-    public static final String METHOD_SET_ENHANCED_BLOCK_SETTING = "set_enhanced_block_setting";
-
-    /** @hide */
     public static final String RES_CAN_BLOCK_NUMBERS = "can_block";
 
     /** @hide */
@@ -439,11 +406,26 @@
         public static final String ACTION_BLOCK_SUPPRESSION_STATE_CHANGED =
                 "android.provider.action.BLOCK_SUPPRESSION_STATE_CHANGED";
 
+        public static final String METHOD_NOTIFY_EMERGENCY_CONTACT = "notify_emergency_contact";
+
+        public static final String METHOD_END_BLOCK_SUPPRESSION = "end_block_suppression";
+
+        public static final String METHOD_SHOULD_SYSTEM_BLOCK_NUMBER = "should_system_block_number";
+
+        public static final String METHOD_GET_BLOCK_SUPPRESSION_STATUS =
+                "get_block_suppression_status";
+
+        public static final String METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION =
+                "should_show_emergency_call_notification";
+
         public static final String RES_IS_BLOCKING_SUPPRESSED = "blocking_suppressed";
 
         public static final String RES_BLOCKING_SUPPRESSED_UNTIL_TIMESTAMP =
                 "blocking_suppressed_until_timestamp";
 
+        public static final String METHOD_GET_ENHANCED_BLOCK_SETTING = "get_enhanced_block_setting";
+        public static final String METHOD_SET_ENHANCED_BLOCK_SETTING = "set_enhanced_block_setting";
+
         /* Preference key of block numbers not in contacts setting. */
         public static final String ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED =
                 "block_numbers_not_in_contacts_setting";
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index aa511cc..fb81d67 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -399,6 +399,13 @@
     public static final String NAMESPACE_CONNECTIVITY_THERMAL_POWER_MANAGER =
             "connectivity_thermal_power_manager";
 
+    /**
+     * Namespace for configuration related features.
+     *
+     * @hide
+     */
+    public static final String NAMESPACE_CONFIGURATION = "configuration";
+
     private static final Object sLock = new Object();
     @GuardedBy("sLock")
     private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index a80153d..327bca2 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -1081,7 +1081,7 @@
             // signed with platform signature can hold MANAGE_DOCUMENTS, we are going to check for
             // MANAGE_DOCUMENTS or associated URI permission here instead
             final Uri rootUri = extras.getParcelable(DocumentsContract.EXTRA_URI);
-            enforceWritePermissionInner(rootUri, getCallingPackage(), getCallingFeatureId(),
+            enforceWritePermissionInner(rootUri, getCallingPackage(), getCallingAttributionTag(),
                     null);
 
             final String rootId = DocumentsContract.getRootId(rootUri);
@@ -1103,8 +1103,8 @@
         enforceTree(documentUri);
 
         if (METHOD_IS_CHILD_DOCUMENT.equals(method)) {
-            enforceReadPermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
-                    null);
+            enforceReadPermissionInner(documentUri, getCallingPackage(),
+                    getCallingAttributionTag(), null);
 
             final Uri childUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI);
             final String childAuthority = childUri.getAuthority();
@@ -1116,8 +1116,8 @@
                             && isChildDocument(documentId, childId));
 
         } else if (METHOD_CREATE_DOCUMENT.equals(method)) {
-            enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
-                    null);
+            enforceWritePermissionInner(documentUri, getCallingPackage(),
+                    getCallingAttributionTag(), null);
 
             final String mimeType = extras.getString(Document.COLUMN_MIME_TYPE);
             final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
@@ -1131,8 +1131,8 @@
             out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
 
         } else if (METHOD_CREATE_WEB_LINK_INTENT.equals(method)) {
-            enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
-                    null);
+            enforceWritePermissionInner(documentUri, getCallingPackage(),
+                    getCallingAttributionTag(), null);
 
             final Bundle options = extras.getBundle(DocumentsContract.EXTRA_OPTIONS);
             final IntentSender intentSender = createWebLinkIntent(documentId, options);
@@ -1140,8 +1140,8 @@
             out.putParcelable(DocumentsContract.EXTRA_RESULT, intentSender);
 
         } else if (METHOD_RENAME_DOCUMENT.equals(method)) {
-            enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
-                    null);
+            enforceWritePermissionInner(documentUri, getCallingPackage(),
+                    getCallingAttributionTag(), null);
 
             final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
             final String newDocumentId = renameDocument(documentId, displayName);
@@ -1165,8 +1165,8 @@
             }
 
         } else if (METHOD_DELETE_DOCUMENT.equals(method)) {
-            enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
-                    null);
+            enforceWritePermissionInner(documentUri, getCallingPackage(),
+                    getCallingAttributionTag(), null);
             deleteDocument(documentId);
 
             // Document no longer exists, clean up any grants.
@@ -1176,9 +1176,9 @@
             final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI);
             final String targetId = DocumentsContract.getDocumentId(targetUri);
 
-            enforceReadPermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
-                    null);
-            enforceWritePermissionInner(targetUri, getCallingPackage(), getCallingFeatureId(),
+            enforceReadPermissionInner(documentUri, getCallingPackage(),
+                    getCallingAttributionTag(), null);
+            enforceWritePermissionInner(targetUri, getCallingPackage(), getCallingAttributionTag(),
                     null);
 
             final String newDocumentId = copyDocument(documentId, targetId);
@@ -1202,11 +1202,11 @@
             final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI);
             final String targetId = DocumentsContract.getDocumentId(targetUri);
 
-            enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
-                    null);
-            enforceReadPermissionInner(parentSourceUri, getCallingPackage(), getCallingFeatureId(),
-                    null);
-            enforceWritePermissionInner(targetUri, getCallingPackage(), getCallingFeatureId(),
+            enforceWritePermissionInner(documentUri, getCallingPackage(),
+                    getCallingAttributionTag(), null);
+            enforceReadPermissionInner(parentSourceUri, getCallingPackage(),
+                    getCallingAttributionTag(), null);
+            enforceWritePermissionInner(targetUri, getCallingPackage(), getCallingAttributionTag(),
                     null);
 
             final String newDocumentId = moveDocument(documentId, parentSourceId, targetId);
@@ -1228,10 +1228,10 @@
             final Uri parentSourceUri = extras.getParcelable(DocumentsContract.EXTRA_PARENT_URI);
             final String parentSourceId = DocumentsContract.getDocumentId(parentSourceUri);
 
-            enforceReadPermissionInner(parentSourceUri, getCallingPackage(), getCallingFeatureId(),
-                    null);
-            enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
-                    null);
+            enforceReadPermissionInner(parentSourceUri, getCallingPackage(),
+                    getCallingAttributionTag(), null);
+            enforceWritePermissionInner(documentUri, getCallingPackage(),
+                    getCallingAttributionTag(), null);
             removeDocument(documentId, parentSourceId);
 
             // It's responsibility of the provider to revoke any grants, as the document may be
@@ -1240,8 +1240,8 @@
             final boolean isTreeUri = isTreeUri(documentUri);
 
             if (isTreeUri) {
-                enforceReadPermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
-                        null);
+                enforceReadPermissionInner(documentUri, getCallingPackage(),
+                        getCallingAttributionTag(), null);
             } else {
                 getContext().enforceCallingPermission(Manifest.permission.MANAGE_DOCUMENTS, null);
             }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 641de4a..ce9b449 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -98,7 +98,8 @@
  * The Settings provider contains global system-level device preferences.
  */
 public final class Settings {
-    private static final boolean DEFAULT_OVERRIDEABLE_BY_RESTORE = false;
+    /** @hide */
+    public static final boolean DEFAULT_OVERRIDEABLE_BY_RESTORE = false;
 
     // Intent actions for Settings
 
@@ -709,10 +710,7 @@
      * If not specified, the default behavior is
      * {@link android.hardware.biometrics.BiometricManager.Authenticators#BIOMETRIC_WEAK}.
      * <p>
-     * Output: Returns {@link android.app.Activity#RESULT_CANCELED} if the user already has an
-     * authenticator that meets the requirements, or if the device cannot fulfill the request
-     * (e.g. does not have biometric hardware). Returns {@link android.app.Activity#RESULT_OK}
-     * otherwise. Note that callers should still check
+     * Output: Nothing. Note that callers should still check
      * {@link android.hardware.biometrics.BiometricManager#canAuthenticate(int)}
      * afterwards to ensure that the user actually completed enrollment.
      */
@@ -2626,7 +2624,7 @@
                     arg.putBoolean(CALL_METHOD_OVERRIDEABLE_BY_RESTORE_KEY, true);
                 }
                 IContentProvider cp = mProviderHolder.getProvider(cr);
-                cp.call(cr.getPackageName(), cr.getFeatureId(),
+                cp.call(cr.getPackageName(), cr.getAttributionTag(),
                         mProviderHolder.mUri.getAuthority(), mCallSetCommand, name, arg);
             } catch (RemoteException e) {
                 Log.w(TAG, "Can't set key " + name + " in " + mUri, e);
@@ -2646,7 +2644,7 @@
                 args.putString(CALL_METHOD_PREFIX_KEY, prefix);
                 args.putSerializable(CALL_METHOD_FLAGS_KEY, keyValues);
                 IContentProvider cp = mProviderHolder.getProvider(cr);
-                Bundle bundle = cp.call(cr.getPackageName(), cr.getFeatureId(),
+                Bundle bundle = cp.call(cr.getPackageName(), cr.getAttributionTag(),
                         mProviderHolder.mUri.getAuthority(),
                         mCallSetAllCommand, null, args);
                 return bundle.getBoolean(KEY_CONFIG_SET_RETURN);
@@ -2721,14 +2719,14 @@
                     if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {
                         final long token = Binder.clearCallingIdentity();
                         try {
-                            b = cp.call(cr.getPackageName(), cr.getFeatureId(),
+                            b = cp.call(cr.getPackageName(), cr.getAttributionTag(),
                                     mProviderHolder.mUri.getAuthority(), mCallGetCommand, name,
                                     args);
                         } finally {
                             Binder.restoreCallingIdentity(token);
                         }
                     } else {
-                        b = cp.call(cr.getPackageName(), cr.getFeatureId(),
+                        b = cp.call(cr.getPackageName(), cr.getAttributionTag(),
                                 mProviderHolder.mUri.getAuthority(), mCallGetCommand, name, args);
                     }
                     if (b != null) {
@@ -2798,13 +2796,13 @@
                 if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {
                     final long token = Binder.clearCallingIdentity();
                     try {
-                        c = cp.query(cr.getPackageName(), cr.getFeatureId(), mUri,
+                        c = cp.query(cr.getPackageName(), cr.getAttributionTag(), mUri,
                                 SELECT_VALUE_PROJECTION, queryArgs, null);
                     } finally {
                         Binder.restoreCallingIdentity(token);
                     }
                 } else {
-                    c = cp.query(cr.getPackageName(), cr.getFeatureId(), mUri,
+                    c = cp.query(cr.getPackageName(), cr.getAttributionTag(), mUri,
                             SELECT_VALUE_PROJECTION, queryArgs, null);
                 }
                 if (c == null) {
@@ -2897,7 +2895,7 @@
                 }
 
                 // Fetch all flags for the namespace at once for caching purposes
-                Bundle b = cp.call(cr.getPackageName(), cr.getFeatureId(),
+                Bundle b = cp.call(cr.getPackageName(), cr.getAttributionTag(),
                         mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args);
                 if (b == null) {
                     // Invalid response, return an empty map
@@ -5542,7 +5540,7 @@
                 }
                 arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode);
                 IContentProvider cp = sProviderHolder.getProvider(resolver);
-                cp.call(resolver.getPackageName(), resolver.getFeatureId(),
+                cp.call(resolver.getPackageName(), resolver.getAttributionTag(),
                         sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_SECURE, null, arg);
             } catch (RemoteException e) {
                 Log.w(TAG, "Can't reset do defaults for " + CONTENT_URI, e);
@@ -6092,10 +6090,7 @@
          * device is removed from this mode.
          * <p>
          * Type: int (0 for false, 1 for true)
-         *
-         * @hide
          */
-        @SystemApi
         public static final String SECURE_FRP_MODE = "secure_frp_mode";
 
         /**
@@ -13388,7 +13383,7 @@
                 }
                 arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode);
                 IContentProvider cp = sProviderHolder.getProvider(resolver);
-                cp.call(resolver.getPackageName(), resolver.getFeatureId(),
+                cp.call(resolver.getPackageName(), resolver.getAttributionTag(),
                         sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_GLOBAL, null, arg);
             } catch (RemoteException e) {
                 Log.w(TAG, "Can't reset do defaults for " + CONTENT_URI, e);
@@ -14363,7 +14358,7 @@
                     arg.putString(Settings.CALL_METHOD_PREFIX_KEY, createPrefix(namespace));
                 }
                 IContentProvider cp = sProviderHolder.getProvider(resolver);
-                cp.call(resolver.getPackageName(), resolver.getFeatureId(),
+                cp.call(resolver.getPackageName(), resolver.getAttributionTag(),
                         sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_CONFIG, null, arg);
             } catch (RemoteException e) {
                 Log.w(TAG, "Can't reset to defaults for " + DeviceConfig.CONTENT_URI, e);
@@ -14392,7 +14387,7 @@
                 arg.putInt(CALL_METHOD_USER_KEY, userHandle);
                 arg.putParcelable(CALL_METHOD_MONITOR_CALLBACK_KEY, callback);
                 IContentProvider cp = sProviderHolder.getProvider(resolver);
-                cp.call(resolver.getPackageName(), resolver.getFeatureId(),
+                cp.call(resolver.getPackageName(), resolver.getAttributionTag(),
                         sProviderHolder.mUri.getAuthority(),
                         CALL_METHOD_REGISTER_MONITOR_CALLBACK_CONFIG, null, arg);
             } catch (RemoteException e) {
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 629dc8b..e7b360d 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -3551,7 +3551,6 @@
          * can manage DPC-owned APNs.
          * @hide
          */
-        @SystemApi
         public static final @NonNull Uri DPC_URI = Uri.parse("content://telephony/carriers/dpc");
 
         /**
@@ -3735,7 +3734,6 @@
          * @deprecated this column is no longer supported, use {@link #NETWORK_TYPE_BITMASK} instead
          */
         @Deprecated
-        @SystemApi
         public static final String BEARER_BITMASK = "bearer_bitmask";
 
         /**
@@ -3785,7 +3783,6 @@
          * <p>Type: INTEGER</p>
          *@hide
          */
-        @SystemApi
         public static final String PROFILE_ID = "profile_id";
 
         /**
@@ -3866,7 +3863,6 @@
          * Integer value denoting an invalid APN id
          * @hide
          */
-        @SystemApi
         public static final int INVALID_APN_ID = -1;
 
         /**
@@ -3986,7 +3982,6 @@
          *
          * @hide
          */
-        @SystemApi
         public static final String SKIP_464XLAT = "skip_464xlat";
 
         /**
@@ -3995,7 +3990,6 @@
          *
          * @hide
          */
-        @SystemApi
         public static final int SKIP_464XLAT_DEFAULT = -1;
 
         /**
@@ -4004,7 +3998,6 @@
          *
          * @hide
          */
-        @SystemApi
         public static final int SKIP_464XLAT_DISABLE = 0;
 
         /**
@@ -4013,7 +4006,6 @@
          *
          * @hide
          */
-        @SystemApi
         public static final int SKIP_464XLAT_ENABLE = 1;
 
 
@@ -4392,6 +4384,7 @@
          * Indicates that whether the message has been broadcasted to the application.
          * <P>Type: BOOLEAN</P>
          */
+        // TODO: deprecate this in S.
         public static final String MESSAGE_BROADCASTED = "message_broadcasted";
 
         /**
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 886b433..08aa534 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -232,22 +232,6 @@
          * Creates a new builder.
          *
          * @param presentation The presentation used to visualize this dataset.
-         * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset
-         *              as inline suggestions. If the dataset supports inline suggestions,
-         *              this should not be null.
-         */
-        public Builder(@NonNull RemoteViews presentation,
-                @NonNull InlinePresentation inlinePresentation) {
-            Preconditions.checkNotNull(presentation, "presentation must be non-null");
-            Preconditions.checkNotNull(inlinePresentation, "inlinePresentation must be non-null");
-            mPresentation = presentation;
-            mInlinePresentation = inlinePresentation;
-        }
-
-        /**
-         * Creates a new builder.
-         *
-         * @param presentation The presentation used to visualize this dataset.
          */
         public Builder(@NonNull RemoteViews presentation) {
             Preconditions.checkNotNull(presentation, "presentation must be non-null");
@@ -282,6 +266,22 @@
         }
 
         /**
+         * Sets the {@link InlinePresentation} used to visualize this dataset as inline suggestions.
+         * If the dataset supports inline suggestions this should not be null.
+         *
+         * @throws IllegalStateException if {@link #build()} was already called.
+         *
+         * @return this builder.
+         */
+        public @NonNull Builder setInlinePresentation(
+                @NonNull InlinePresentation inlinePresentation) {
+            throwIfDestroyed();
+            Preconditions.checkNotNull(inlinePresentation, "inlinePresentation must be non-null");
+            mInlinePresentation = inlinePresentation;
+            return this;
+        }
+
+        /**
          * Triggers a custom UI before before autofilling the screen with the contents of this
          * dataset.
          *
@@ -600,7 +600,7 @@
          */
         @SystemApi
         @TestApi
-        public @NonNull Builder setInlinePresentation(@NonNull AutofillId id,
+        public @NonNull Builder setFieldInlinePresentation(@NonNull AutofillId id,
                 @Nullable AutofillValue value, @Nullable Pattern filter,
                 @NonNull InlinePresentation inlinePresentation) {
             throwIfDestroyed();
@@ -700,7 +700,7 @@
             final Builder builder = presentation != null
                     ? inlinePresentation == null
                             ? new Builder(presentation)
-                            : new Builder(presentation, inlinePresentation)
+                            : new Builder(presentation).setInlinePresentation(inlinePresentation)
                     : inlinePresentation == null
                             ? new Builder()
                             : new Builder(inlinePresentation);
diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java
index 72e9ad0..8f858d5 100644
--- a/core/java/android/service/autofill/FillRequest.java
+++ b/core/java/android/service/autofill/FillRequest.java
@@ -77,6 +77,15 @@
      */
     public static final @RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST = 0x2;
 
+    /**
+     * Indicates the request came from a password field.
+     *
+     * (TODO: b/141703197) Temporary fix for augmented autofill showing passwords.
+     *
+     * @hide
+     */
+    public static final @RequestFlags int FLAG_PASSWORD_INPUT_TYPE = 0x4;
+
     /** @hide */
     public static final int INVALID_REQUEST_ID = Integer.MIN_VALUE;
 
@@ -149,7 +158,8 @@
     /** @hide */
     @IntDef(flag = true, prefix = "FLAG_", value = {
         FLAG_MANUAL_REQUEST,
-        FLAG_COMPATIBILITY_MODE_REQUEST
+        FLAG_COMPATIBILITY_MODE_REQUEST,
+        FLAG_PASSWORD_INPUT_TYPE
     })
     @Retention(RetentionPolicy.SOURCE)
     @DataClass.Generated.Member
@@ -169,6 +179,8 @@
                     return "FLAG_MANUAL_REQUEST";
             case FLAG_COMPATIBILITY_MODE_REQUEST:
                     return "FLAG_COMPATIBILITY_MODE_REQUEST";
+            case FLAG_PASSWORD_INPUT_TYPE:
+                    return "FLAG_PASSWORD_INPUT_TYPE";
             default: return Integer.toHexString(value);
         }
     }
@@ -223,7 +235,8 @@
         Preconditions.checkFlagsArgument(
                 mFlags,
                 FLAG_MANUAL_REQUEST
-                        | FLAG_COMPATIBILITY_MODE_REQUEST);
+                        | FLAG_COMPATIBILITY_MODE_REQUEST
+                        | FLAG_PASSWORD_INPUT_TYPE);
         this.mInlineSuggestionsRequest = inlineSuggestionsRequest;
 
         onConstructed();
@@ -352,7 +365,8 @@
         Preconditions.checkFlagsArgument(
                 mFlags,
                 FLAG_MANUAL_REQUEST
-                        | FLAG_COMPATIBILITY_MODE_REQUEST);
+                        | FLAG_COMPATIBILITY_MODE_REQUEST
+                        | FLAG_PASSWORD_INPUT_TYPE);
         this.mInlineSuggestionsRequest = inlineSuggestionsRequest;
 
         onConstructed();
@@ -373,10 +387,10 @@
     };
 
     @DataClass.Generated(
-            time = 1575928271155L,
+            time = 1583196707026L,
             codegenVersion = "1.0.14",
             sourceFile = "frameworks/base/core/java/android/service/autofill/FillRequest.java",
-            inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final  int INVALID_REQUEST_ID\nprivate final  int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate  void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
+            inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final  int INVALID_REQUEST_ID\nprivate final  int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate  void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
index 1011651..1bcc76b 100644
--- a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
+++ b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
@@ -25,7 +25,8 @@
  * @hide
  */
 oneway interface IInlineSuggestionUiCallback {
-    void onAutofill();
+    void onClick();
+    void onLongClick();
     void onContent(in SurfaceControlViewHost.SurfacePackage surface);
     void onError();
     void onTransferTouchFocusToImeWindow(in IBinder sourceInputToken, int displayId);
diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java
index ee152837..b6cc62d 100644
--- a/core/java/android/service/autofill/InlineSuggestionRenderService.java
+++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java
@@ -97,12 +97,21 @@
             host.addView(suggestionRoot, lp);
             suggestionRoot.setOnClickListener((v) -> {
                 try {
-                    callback.onAutofill();
+                    callback.onClick();
                 } catch (RemoteException e) {
-                    Log.w(TAG, "RemoteException calling onAutofill()");
+                    Log.w(TAG, "RemoteException calling onClick()");
                 }
             });
 
+            suggestionRoot.setOnLongClickListener((v) -> {
+                try {
+                    callback.onLongClick();
+                } catch (RemoteException e) {
+                    Log.w(TAG, "RemoteException calling onLongClick()");
+                }
+                return true;
+            });
+
             sendResult(callback, host.getSurfacePackage());
         } finally {
             updateDisplay(Display.DEFAULT_DISPLAY);
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index ed27dd5..5b08ae20 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -488,7 +488,8 @@
                 ids.add(pair.first);
                 values.add(pair.second);
             }
-            mClient.autofill(mSessionId, ids, values);
+            final boolean hideHighlight = size == 1 && ids.get(0).equals(mFocusedId);
+            mClient.autofill(mSessionId, ids, values, hideHighlight);
         }
 
         public void setFillWindow(@NonNull FillWindow fillWindow) {
diff --git a/core/java/android/service/dataloader/DataLoaderService.java b/core/java/android/service/dataloader/DataLoaderService.java
index 0b9a8af..d4db79e 100644
--- a/core/java/android/service/dataloader/DataLoaderService.java
+++ b/core/java/android/service/dataloader/DataLoaderService.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.Intent;
@@ -206,6 +207,7 @@
          * @throws IOException if trouble opening the file for writing, such as lack of disk space
          *                     or unavailable media.
          */
+        @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES)
         public void writeData(@NonNull String name, long offsetBytes, long lengthBytes,
                 @NonNull ParcelFileDescriptor incomingFd) throws IOException {
             try {
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 002d4b8..e70311f 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -609,9 +609,7 @@
      *
      * @hide
      *
-     * TODO: Remove @UnsupportedAppUsage.
      */
-    @UnsupportedAppUsage
     public void setWindowless(boolean windowless) {
         mWindowless = windowless;
     }
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 8e6f77b..4a0dd87 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -1314,7 +1314,8 @@
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(mContext);
             int res = mSystemService.startVoiceActivity(mToken, intent,
-                    intent.resolveType(mContext.getContentResolver()), mContext.getFeatureId());
+                    intent.resolveType(mContext.getContentResolver()),
+                    mContext.getAttributionTag());
             Instrumentation.checkStartActivityResult(res, intent);
         } catch (RemoteException e) {
         }
@@ -1342,7 +1343,8 @@
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(mContext);
             int res = mSystemService.startAssistantActivity(mToken, intent,
-                    intent.resolveType(mContext.getContentResolver()), mContext.getFeatureId());
+                    intent.resolveType(mContext.getContentResolver()),
+                    mContext.getAttributionTag());
             Instrumentation.checkStartActivityResult(res, intent);
         } catch (RemoteException e) {
         }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index bf3c088..f531e12 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -124,8 +124,6 @@
     private static final int MSG_REQUEST_WALLPAPER_COLORS = 10050;
     private static final int MSG_SCALE = 10100;
 
-    private static final float MAX_SCALE = 1.15f;
-
     private static final int NOTIFY_COLORS_RATE_LIMIT_MS = 1000;
 
     private final ArrayList<Engine> mActiveEngines
@@ -351,7 +349,7 @@
 
             @Override
             public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
-                    boolean sync) {
+                    float zoom, boolean sync) {
                 synchronized (mLock) {
                     if (DEBUG) Log.v(TAG, "Dispatch wallpaper offsets: " + x + ", " + y);
                     mPendingXOffset = x;
@@ -366,6 +364,8 @@
                         Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS);
                         mCaller.sendMessage(msg);
                     }
+                    Message msg = mCaller.obtainMessageI(MSG_SCALE, Float.floatToIntBits(zoom));
+                    mCaller.sendMessage(msg);
                 }
             }
 
@@ -462,6 +462,18 @@
         }
 
         /**
+         * This will be called when the wallpaper is first started. If true is returned, the system
+         * will zoom in the wallpaper by default and zoom it out as the user interacts,
+         * to create depth. Otherwise, zoom will have to be handled manually
+         * in {@link #onZoomChanged(float)}.
+         *
+         * @hide
+         */
+        public boolean shouldZoomOutWallpaper() {
+            return false;
+        }
+
+        /**
          * Control whether this wallpaper will receive raw touch events
          * from the window manager as the user interacts with the window
          * that is currently displaying the wallpaper.  By default they
@@ -870,6 +882,7 @@
                             Log.w(TAG, "Failed to add window while updating wallpaper surface.");
                             return;
                         }
+                        mSession.setShouldZoomOutWallpaper(mWindow, shouldZoomOutWallpaper());
                         mCreated = true;
 
                         mInputEventReceiver = new WallpaperInputEventReceiver(
@@ -964,7 +977,6 @@
                                     c.surfaceCreated(mSurfaceHolder);
                                 }
                             }
-                            onZoomChanged(0f);
                         }
 
                         redrawNeeded |= creating || (relayoutResult
@@ -1125,7 +1137,6 @@
                 mIsInAmbientMode = inAmbientMode;
                 if (mCreated) {
                     onAmbientModeChanged(inAmbientMode, animationDuration);
-                    setZoom(0);
                 }
             }
         }
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index e93ba16..92f3538 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -342,7 +342,7 @@
         }
         try {
             mService.startListening(recognizerIntent, mListener, mContext.getOpPackageName(),
-                    mContext.getFeatureId());
+                    mContext.getAttributionTag());
             if (DBG) Log.d(TAG, "service start listening command succeded");
         } catch (final RemoteException e) {
             Log.e(TAG, "startListening() failed", e);
@@ -357,7 +357,7 @@
         }
         try {
             mService.stopListening(mListener, mContext.getOpPackageName(),
-                    mContext.getFeatureId());
+                    mContext.getAttributionTag());
             if (DBG) Log.d(TAG, "service stop listening command succeded");
         } catch (final RemoteException e) {
             Log.e(TAG, "stopListening() failed", e);
@@ -371,7 +371,7 @@
             return;
         }
         try {
-            mService.cancel(mListener, mContext.getOpPackageName(), mContext.getFeatureId());
+            mService.cancel(mListener, mContext.getOpPackageName(), mContext.getAttributionTag());
             if (DBG) Log.d(TAG, "service cancel command succeded");
         } catch (final RemoteException e) {
             Log.e(TAG, "cancel() failed", e);
@@ -400,7 +400,8 @@
     public void destroy() {
         if (mService != null) {
             try {
-                mService.cancel(mListener, mContext.getOpPackageName(), mContext.getFeatureId());
+                mService.cancel(mListener, mContext.getOpPackageName(),
+                        mContext.getAttributionTag());
             } catch (final RemoteException e) {
                 // Not important
             }
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 7238b12..ab9df56 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -116,7 +116,7 @@
         mSubscriptionChangedListenerMap.put(listener, callback);
         try {
             sRegistry.addOnSubscriptionsChangedListener(mContext.getOpPackageName(),
-                    mContext.getFeatureId(), callback);
+                    mContext.getAttributionTag(), callback);
         } catch (RemoteException ex) {
             // system server crash
         }
@@ -175,7 +175,7 @@
         mOpportunisticSubscriptionChangedListenerMap.put(listener, callback);
         try {
             sRegistry.addOnOpportunisticSubscriptionsChangedListener(mContext.getOpPackageName(),
-                    mContext.getFeatureId(), callback);
+                    mContext.getAttributionTag(), callback);
         } catch (RemoteException ex) {
             // system server crash
         }
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index 2aca36a..82c7ea7 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityThread;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.telephony.PhoneNumberUtils;
@@ -663,9 +664,11 @@
     private static void gatherTelLinks(ArrayList<LinkSpec> links, Spannable s,
             @Nullable Context context) {
         PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
+        final Context ctx = (context != null) ? context : ActivityThread.currentApplication();
+        final String regionCode = (ctx != null) ? ctx.getSystemService(TelephonyManager.class).
+                getSimCountryIso().toUpperCase(Locale.US) : Locale.getDefault().getCountry();
         Iterable<PhoneNumberMatch> matches = phoneUtil.findNumbers(s.toString(),
-                TelephonyManager.getDefaultSimCountryIso().toUpperCase(Locale.US),
-                Leniency.POSSIBLE, Long.MAX_VALUE);
+                regionCode, Leniency.POSSIBLE, Long.MAX_VALUE);
         for (PhoneNumberMatch match : matches) {
             LinkSpec spec = new LinkSpec();
             spec.url = "tel:" + PhoneNumberUtils.normalizeNumber(match.rawString());
diff --git a/core/java/android/util/apk/SourceStampVerifier.java b/core/java/android/util/apk/SourceStampVerifier.java
index 759c864..70e4a51 100644
--- a/core/java/android/util/apk/SourceStampVerifier.java
+++ b/core/java/android/util/apk/SourceStampVerifier.java
@@ -82,25 +82,34 @@
     public static SourceStampVerificationResult verify(String apkFile) {
         try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
             return verify(apk);
-        } catch (Exception e) {
-            // Any exception in the SourceStamp verification returns a non-verified SourceStamp
-            // outcome without affecting the outcome of any of the other signature schemes.
-            return SourceStampVerificationResult.notVerified();
+        } catch (IOException e) {
+            // Any exception in reading the APK returns a non-present SourceStamp outcome
+            // without affecting the outcome of any of the other signature schemes.
+            return SourceStampVerificationResult.notPresent();
         }
     }
 
-    private static SourceStampVerificationResult verify(RandomAccessFile apk)
-            throws IOException, SignatureNotFoundException {
-        byte[] sourceStampCertificateDigest = getSourceStampCertificateDigest(apk);
-        if (sourceStampCertificateDigest == null) {
-            // SourceStamp certificate hash file not found, which means that there is not
-            // SourceStamp present.
+    private static SourceStampVerificationResult verify(RandomAccessFile apk) {
+        byte[] sourceStampCertificateDigest;
+        try {
+            sourceStampCertificateDigest = getSourceStampCertificateDigest(apk);
+            if (sourceStampCertificateDigest == null) {
+                // SourceStamp certificate hash file not found, which means that there is not
+                // SourceStamp present.
+                return SourceStampVerificationResult.notPresent();
+            }
+        } catch (IOException e) {
             return SourceStampVerificationResult.notPresent();
         }
-        SignatureInfo signatureInfo =
-                ApkSigningBlockUtils.findSignature(apk, SOURCE_STAMP_BLOCK_ID);
-        Map<Integer, byte[]> apkContentDigests = getApkContentDigests(apk);
-        return verify(signatureInfo, apkContentDigests, sourceStampCertificateDigest);
+
+        try {
+            SignatureInfo signatureInfo =
+                    ApkSigningBlockUtils.findSignature(apk, SOURCE_STAMP_BLOCK_ID);
+            Map<Integer, byte[]> apkContentDigests = getApkContentDigests(apk);
+            return verify(signatureInfo, apkContentDigests, sourceStampCertificateDigest);
+        } catch (IOException | SignatureNotFoundException e) {
+            return SourceStampVerificationResult.notVerified();
+        }
     }
 
     private static SourceStampVerificationResult verify(
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 3927ebf..9f2d36d 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -102,9 +102,9 @@
     void closeSystemDialogs(String reason);
 
     /**
-     * Called for wallpaper windows when their offsets change.
+     * Called for wallpaper windows when their offsets or zoom level change.
      */
-    void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, boolean sync);
+    void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, float zoom, boolean sync);
 
     void dispatchWallpaperCommand(String action, int x, int y,
             int z, in Bundle extras, boolean sync);
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 91000a9..dfe89a3 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -221,6 +221,19 @@
      */
     void setWallpaperPosition(IBinder windowToken, float x, float y, float xstep, float ystep);
 
+    /**
+     * For wallpaper windows, sets the scale of the wallpaper based on
+     * SystemUI behavior.
+     */
+    void setWallpaperZoomOut(IBinder windowToken, float scale);
+
+    /**
+     * For wallpaper windows, sets whether the wallpaper should actually be
+     * scaled when setWallpaperZoomOut is called. If set to false, the WallpaperService will
+     * receive the zoom out value but the surface won't be scaled.
+     */
+    void setShouldZoomOutWallpaper(IBinder windowToken, boolean shouldZoom);
+
     @UnsupportedAppUsage
     void wallpaperOffsetsComplete(IBinder window);
 
diff --git a/core/java/android/view/InsetsAnimationControlCallbacks.java b/core/java/android/view/InsetsAnimationControlCallbacks.java
index 5d5edec..4227f78 100644
--- a/core/java/android/view/InsetsAnimationControlCallbacks.java
+++ b/core/java/android/view/InsetsAnimationControlCallbacks.java
@@ -16,7 +16,6 @@
 
 package android.view;
 
-import android.view.InsetsController.LayoutInsetsDuringAnimation;
 import android.view.WindowInsetsAnimation.Bounds;
 
 /**
@@ -37,7 +36,7 @@
     void startAnimation(InsetsAnimationControlImpl controller,
             WindowInsetsAnimationControlListener listener, int types,
             WindowInsetsAnimation animation,
-            Bounds bounds, @LayoutInsetsDuringAnimation int layoutDuringAnimation);
+            Bounds bounds);
 
     /**
      * Schedule the apply by posting the animation callback.
@@ -46,10 +45,10 @@
 
     /**
      * Finish the final steps after the animation.
-     * @param controller The controller used to control the animation.
+     * @param runner The runner used to run the animation.
      * @param shown {@code true} if the insets are shown.
      */
-    void notifyFinished(InsetsAnimationControlImpl controller, boolean shown);
+    void notifyFinished(InsetsAnimationControlRunner runner, boolean shown);
 
     /**
      * Apply the new params to the surface.
@@ -57,4 +56,10 @@
      *               apply.
      */
     void applySurfaceParams(SyncRtSurfaceTransactionApplier.SurfaceParams... params);
+
+    /**
+     * Post a message to release the Surface, guaranteed to happen after all
+     * previous calls to applySurfaceParams.
+     */
+    void releaseSurfaceControlFromRt(SurfaceControl sc);
 }
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index ae509f3..baee412 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -31,9 +31,7 @@
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 import android.util.SparseSetArray;
-import android.view.InsetsController.LayoutInsetsDuringAnimation;
 import android.view.InsetsState.InternalInsetsSide;
-import android.view.InsetsState.InternalInsetsType;
 import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowInsetsAnimation.Bounds;
@@ -49,7 +47,8 @@
  * @hide
  */
 @VisibleForTesting
-public class InsetsAnimationControlImpl implements WindowInsetsAnimationController  {
+public class InsetsAnimationControlImpl implements WindowInsetsAnimationController,
+        InsetsAnimationControlRunner {
 
     private final Rect mTmpFrame = new Rect();
 
@@ -84,8 +83,7 @@
             InsetsState state, WindowInsetsAnimationControlListener listener,
             @InsetsType int types,
             InsetsAnimationControlCallbacks controller, long durationMs, Interpolator interpolator,
-            boolean fade, @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
-            @AnimationType int animationType) {
+            boolean fade, @AnimationType int animationType) {
         mControls = controls;
         mListener = listener;
         mTypes = types;
@@ -105,7 +103,7 @@
         mAnimation.setAlpha(getCurrentAlpha());
         mAnimationType = animationType;
         mController.startAnimation(this, listener, types, mAnimation,
-                new Bounds(mHiddenInsets, mShownInsets), layoutInsetsDuringAnimation);
+                new Bounds(mHiddenInsets, mShownInsets));
     }
 
     @Override
@@ -133,11 +131,8 @@
         return mTypes;
     }
 
-    boolean controlsInternalType(@InternalInsetsType int type) {
-        return InsetsState.toInternalType(mTypes).contains(type);
-    }
-
-    @AnimationType int getAnimationType() {
+    @Override
+    public @AnimationType int getAnimationType() {
         return mAnimationType;
     }
 
@@ -185,10 +180,19 @@
         mAnimation.setAlpha(mPendingAlpha);
         if (mFinished) {
             mController.notifyFinished(this, mShownOnFinish);
+            releaseLeashes();
         }
         return mFinished;
     }
 
+    private void releaseLeashes() {
+        for (int i = mControls.size() - 1; i >= 0; i--) {
+            final InsetsSourceControl c = mControls.valueAt(i);
+            if (c == null) continue;
+            c.release(mController::releaseSurfaceControlFromRt);
+        }
+    }
+
     @Override
     public void finish(boolean shown) {
         if (mCancelled || mFinished) {
@@ -196,6 +200,7 @@
         }
         setInsetsAndAlpha(shown ? mShownInsets : mHiddenInsets, 1f /* alpha */, 1f /* fraction */);
         mFinished = true;
+
         mShownOnFinish = shown;
     }
 
@@ -205,19 +210,23 @@
         return mAnimation.getFraction();
     }
 
-    public void onCancelled() {
+    @Override
+    public void cancel() {
         if (mFinished) {
             return;
         }
         mCancelled = true;
         mListener.onCancelled();
+
+        releaseLeashes();
     }
 
     public boolean isCancelled() {
         return mCancelled;
     }
 
-    WindowInsetsAnimation getAnimation() {
+    @Override
+    public WindowInsetsAnimation getAnimation() {
         return mAnimation;
     }
 
@@ -225,6 +234,10 @@
         return mListener;
     }
 
+    SparseArray<InsetsSourceControl> getControls() {
+        return mControls;
+    }
+
     private Insets calculateInsets(InsetsState state, Rect frame,
             SparseArray<InsetsSourceControl> controls, boolean shown,
             @Nullable @InternalInsetsSide SparseIntArray typeSideMap) {
diff --git a/core/java/android/view/InsetsAnimationControlRunner.java b/core/java/android/view/InsetsAnimationControlRunner.java
new file mode 100644
index 0000000..0711c3e
--- /dev/null
+++ b/core/java/android/view/InsetsAnimationControlRunner.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.view.InsetsController.AnimationType;
+import android.view.InsetsState.InternalInsetsType;
+import android.view.WindowInsets.Type.InsetsType;
+
+/**
+ * Interface representing a runner for an insets animation.
+ *
+ * @hide
+ */
+public interface InsetsAnimationControlRunner {
+
+    /**
+     * @return The {@link InsetsType} the animation of this runner is controlling.
+     */
+    @InsetsType int getTypes();
+
+    /**
+     * Cancels the animation.
+     */
+    void cancel();
+
+    /**
+     * @return The animation this runner is running.
+     */
+    WindowInsetsAnimation getAnimation();
+
+    /**
+     * @return Whether {@link #getTypes()} maps to a specific {@link InternalInsetsType}.
+     */
+    default boolean controlsInternalType(@InternalInsetsType int type) {
+        return InsetsState.toInternalType(getTypes()).contains(type);
+    }
+
+    /**
+     * @return The animation type this runner is running.
+     */
+    @AnimationType int getAnimationType();
+}
diff --git a/core/java/android/view/InsetsAnimationThread.java b/core/java/android/view/InsetsAnimationThread.java
new file mode 100644
index 0000000..cdf9733
--- /dev/null
+++ b/core/java/android/view/InsetsAnimationThread.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Trace;
+
+/**
+ * Thread to be used for inset animations to be running off the main thread.
+ * @hide
+ */
+public class InsetsAnimationThread extends HandlerThread {
+
+    private static InsetsAnimationThread sInstance;
+    private static Handler sHandler;
+
+    private InsetsAnimationThread() {
+        // TODO: Should this use higher priority?
+        super("InsetsAnimations");
+    }
+
+    private static void ensureThreadLocked() {
+        if (sInstance == null) {
+            sInstance = new InsetsAnimationThread();
+            sInstance.start();
+            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_VIEW);
+            sHandler = new Handler(sInstance.getLooper());
+        }
+    }
+
+    public static void release() {
+        synchronized (InsetsAnimationThread.class) {
+            if (sInstance == null) {
+                return;
+            }
+            sInstance.getLooper().quitSafely();
+            sInstance = null;
+            sHandler = null;
+        }
+    }
+
+    public static InsetsAnimationThread get() {
+        synchronized (InsetsAnimationThread.class) {
+            ensureThreadLocked();
+            return sInstance;
+        }
+    }
+
+    public static Handler getHandler() {
+        synchronized (InsetsAnimationThread.class) {
+            ensureThreadLocked();
+            return sHandler;
+        }
+    }
+}
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
new file mode 100644
index 0000000..13b4cd8
--- /dev/null
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.view.SyncRtSurfaceTransactionApplier.applyParams;
+
+import android.annotation.UiThread;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.util.SparseArray;
+import android.view.InsetsController.AnimationType;
+import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
+import android.view.WindowInsets.Type.InsetsType;
+import android.view.WindowInsetsAnimation.Bounds;
+import android.view.animation.Interpolator;
+
+/**
+ * Insets animation runner that uses {@link InsetsAnimationThread} to run the animation off from the
+ * main thread.
+ *
+ * @hide
+ */
+public class InsetsAnimationThreadControlRunner implements InsetsAnimationControlRunner {
+
+    private final InsetsAnimationControlImpl mControl;
+    private final InsetsAnimationControlCallbacks mOuterCallbacks;
+    private final Handler mMainThreadHandler;
+    private final InsetsState mState = new InsetsState();
+    private final InsetsAnimationControlCallbacks mCallbacks =
+            new InsetsAnimationControlCallbacks() {
+
+        private final float[] mTmpFloat9 = new float[9];
+
+        @Override
+        @UiThread
+        public void startAnimation(InsetsAnimationControlImpl controller,
+                WindowInsetsAnimationControlListener listener, int types,
+                WindowInsetsAnimation animation, Bounds bounds) {
+            // Animation will be started in constructor already.
+        }
+
+        @Override
+        public void scheduleApplyChangeInsets() {
+            mControl.applyChangeInsets(mState);
+        }
+
+        @Override
+        public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) {
+            releaseControls(mControl.getControls());
+            mMainThreadHandler.post(() ->
+                    mOuterCallbacks.notifyFinished(InsetsAnimationThreadControlRunner.this, shown));
+        }
+
+        @Override
+        public void applySurfaceParams(SurfaceParams... params) {
+            SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+            for (int i = params.length - 1; i >= 0; i--) {
+                SyncRtSurfaceTransactionApplier.SurfaceParams surfaceParams = params[i];
+                applyParams(t, surfaceParams, mTmpFloat9);
+            }
+            t.apply();
+            t.close();
+        }
+
+        @Override
+        public void releaseSurfaceControlFromRt(SurfaceControl sc) {
+            // Since we don't push the SurfaceParams to the RT we can release directly
+            sc.release();
+        }
+    };
+
+    @UiThread
+    public InsetsAnimationThreadControlRunner(SparseArray<InsetsSourceControl> controls, Rect frame,
+            InsetsState state, WindowInsetsAnimationControlListener listener,
+            @InsetsType int types,
+            InsetsAnimationControlCallbacks controller, long durationMs, Interpolator interpolator,
+            boolean fade, @AnimationType int animationType, Handler mainThreadHandler) {
+        mMainThreadHandler = mainThreadHandler;
+        mOuterCallbacks = controller;
+        mControl = new InsetsAnimationControlImpl(copyControls(controls), frame, state, listener,
+                types, mCallbacks, durationMs, interpolator, fade, animationType);
+        InsetsAnimationThread.getHandler().post(() -> listener.onReady(mControl, types));
+    }
+
+    private void releaseControls(SparseArray<InsetsSourceControl> controls) {
+        for (int i = controls.size() - 1; i >= 0; i--) {
+            controls.valueAt(i).release(SurfaceControl::release);
+        }
+    }
+
+    private SparseArray<InsetsSourceControl> copyControls(
+            SparseArray<InsetsSourceControl> controls) {
+        SparseArray<InsetsSourceControl> copy = new SparseArray<>(controls.size());
+        for (int i = 0; i < controls.size(); i++) {
+            copy.append(controls.keyAt(i), new InsetsSourceControl(controls.valueAt(i)));
+        }
+        return copy;
+    }
+
+    @Override
+    @UiThread
+    public int getTypes() {
+        return mControl.getTypes();
+    }
+
+    @Override
+    @UiThread
+    public void cancel() {
+        InsetsAnimationThread.getHandler().post(mControl::cancel);
+    }
+
+    @Override
+    @UiThread
+    public WindowInsetsAnimation getAnimation() {
+        return mControl.getAnimation();
+    }
+
+    @Override
+    public int getAnimationType() {
+        return mControl.getAnimationType();
+    }
+}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 302d4f3..88e7f2e 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -23,6 +23,7 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APPEARANCE_CONTROLLED;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED;
 
+import android.animation.AnimationHandler;
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
@@ -52,6 +53,8 @@
 import android.view.animation.PathInterpolator;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -59,6 +62,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.function.BiFunction;
 
 /**
@@ -165,10 +169,22 @@
 
         private WindowInsetsAnimationController mController;
         private ObjectAnimator mAnimator;
-        protected boolean mShow;
+        private final boolean mShow;
+        private final boolean mUseSfVsync;
 
-        public InternalAnimationControlListener(boolean show) {
+        private ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal =
+                new ThreadLocal<AnimationHandler>() {
+            @Override
+            protected AnimationHandler initialValue() {
+                AnimationHandler handler = new AnimationHandler();
+                handler.setProvider(new SfVsyncFrameCallbackProvider());
+                return handler;
+            }
+        };
+
+        public InternalAnimationControlListener(boolean show, boolean useSfVsync) {
             mShow = show;
+            mUseSfVsync = useSfVsync;
         }
 
         @Override
@@ -191,6 +207,9 @@
                     onAnimationFinish();
                 }
             });
+            if (mUseSfVsync) {
+                mAnimator.setAnimationHandler(mSfAnimationHandlerThreadLocal.get());
+            }
             mAnimator.start();
         }
 
@@ -226,12 +245,12 @@
      */
     private static class RunningAnimation {
 
-        RunningAnimation(InsetsAnimationControlImpl control, int type) {
-            this.control = control;
+        RunningAnimation(InsetsAnimationControlRunner runner, int type) {
+            this.runner = runner;
             this.type = type;
         }
 
-        final InsetsAnimationControlImpl control;
+        final InsetsAnimationControlRunner runner;
         final @AnimationType int type;
 
         /**
@@ -250,7 +269,7 @@
         PendingControlRequest(@InsetsType int types, WindowInsetsAnimationControlListener listener,
                 long durationMs, Interpolator interpolator, @AnimationType int animationType,
                 @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
-                CancellationSignal cancellationSignal) {
+                CancellationSignal cancellationSignal, boolean useInsetsAnimationThread) {
             this.types = types;
             this.listener = listener;
             this.durationMs = durationMs;
@@ -258,6 +277,7 @@
             this.animationType = animationType;
             this.layoutInsetsDuringAnimation = layoutInsetsDuringAnimation;
             this.cancellationSignal = cancellationSignal;
+            this.useInsetsAnimationThread = useInsetsAnimationThread;
         }
 
         final @InsetsType int types;
@@ -267,6 +287,7 @@
         final @AnimationType int animationType;
         final @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation;
         final CancellationSignal cancellationSignal;
+        final boolean useInsetsAnimationThread;
     }
 
     private final String TAG = "InsetsControllerImpl";
@@ -306,6 +327,11 @@
     private SyncRtSurfaceTransactionApplier mApplier;
 
     private Runnable mPendingControlTimeout = this::abortPendingImeControlRequest;
+    private final ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners
+            = new ArrayList<>();
+
+    /** Set of inset types for which an animation was started since last resetting this field */
+    private @InsetsType int mLastStartedAnimTypes;
 
     public InsetsController(ViewRootImpl viewRoot) {
         this(viewRoot, (controller, type) -> {
@@ -340,15 +366,20 @@
             InsetsState state = new InsetsState(mState, true /* copySources */);
             for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
                 RunningAnimation runningAnimation = mRunningAnimations.get(i);
-                InsetsAnimationControlImpl control = runningAnimation.control;
+                InsetsAnimationControlRunner runner = runningAnimation.runner;
+                if (runner instanceof InsetsAnimationControlImpl) {
+                    InsetsAnimationControlImpl control = (InsetsAnimationControlImpl) runner;
 
-                // Keep track of running animation to be dispatched. Aggregate it here such that if
-                // it gets finished within applyChangeInsets we still dispatch it to onProgress.
-                if (runningAnimation.startDispatched) {
-                    mTmpRunningAnims.add(control.getAnimation());
-                }
-                if (control.applyChangeInsets(state)) {
-                    mTmpFinishedControls.add(control);
+                    // Keep track of running animation to be dispatched. Aggregate it here such that
+                    // if it gets finished within applyChangeInsets we still dispatch it to
+                    // onProgress.
+                    if (runningAnimation.startDispatched) {
+                        mTmpRunningAnims.add(control.getAnimation());
+                    }
+
+                    if (control.applyChangeInsets(state)) {
+                        mTmpFinishedControls.add(control);
+                    }
                 }
             }
 
@@ -459,6 +490,13 @@
 
         }
         mTmpControlArray.clear();
+
+        // Do not override any animations that the app started in the OnControllableInsetsChanged
+        // listeners.
+        int animatingTypes = invokeControllableInsetsChangedListeners();
+        showTypes[0] &= ~animatingTypes;
+        hideTypes[0] &= ~animatingTypes;
+
         if (showTypes[0] != 0) {
             applyAnimation(showTypes[0], true /* show */, false /* fromIme */);
         }
@@ -485,7 +523,8 @@
                     pendingRequest.listener, mFrame,
                     true /* fromIme */, pendingRequest.durationMs, pendingRequest.interpolator,
                     false /* fade */, pendingRequest.animationType,
-                    pendingRequest.layoutInsetsDuringAnimation);
+                    pendingRequest.layoutInsetsDuringAnimation,
+                    pendingRequest.useInsetsAnimationThread);
             pendingRequest.cancellationSignal.setOnCancelListener(cancellationSignal::cancel);
             return;
         }
@@ -542,23 +581,30 @@
     private CancellationSignal controlWindowInsetsAnimation(@InsetsType int types,
             WindowInsetsAnimationControlListener listener, boolean fromIme, long durationMs,
             @Nullable Interpolator interpolator, @AnimationType int animationType) {
-        // If the frame of our window doesn't span the entire display, the control API makes very
-        // little sense, as we don't deal with negative insets. So just cancel immediately.
-        if (!mState.getDisplayFrame().equals(mFrame)) {
+        if (!checkDisplayFramesForControlling()) {
             listener.onCancelled();
             CancellationSignal cancellationSignal = new CancellationSignal();
             cancellationSignal.cancel();
             return cancellationSignal;
         }
         return controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, interpolator,
-                false /* fade */, animationType, getLayoutInsetsDuringAnimationMode(types));
+                false /* fade */, animationType, getLayoutInsetsDuringAnimationMode(types),
+                false /* useInsetsAnimationThread */);
+    }
+
+    private boolean checkDisplayFramesForControlling() {
+
+        // If the frame of our window doesn't span the entire display, the control API makes very
+        // little sense, as we don't deal with negative insets. So just cancel immediately.
+        return mState.getDisplayFrame().equals(mFrame);
     }
 
     private CancellationSignal controlAnimationUnchecked(@InsetsType int types,
             WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme,
             long durationMs, Interpolator interpolator, boolean fade,
             @AnimationType int animationType,
-            @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
+            @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
+            boolean useInsetsAnimationThread) {
         CancellationSignal cancellationSignal = new CancellationSignal();
         if (types == 0) {
             // nothing to animate.
@@ -567,6 +613,7 @@
             return cancellationSignal;
         }
         cancelExistingControllers(types);
+        mLastStartedAnimTypes |= types;
 
         final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
         final SparseArray<InsetsSourceControl> controls = new SparseArray<>();
@@ -580,7 +627,8 @@
             abortPendingImeControlRequest();
             final PendingControlRequest request = new PendingControlRequest(types,
                     listener, durationMs,
-                    interpolator, animationType, layoutInsetsDuringAnimation, cancellationSignal);
+                    interpolator, animationType, layoutInsetsDuringAnimation, cancellationSignal,
+                    useInsetsAnimationThread);
             mPendingImeControlRequest = request;
             mHandler.postDelayed(mPendingControlTimeout, PENDING_CONTROL_TIMEOUT_MS);
             cancellationSignal.setOnCancelListener(() -> {
@@ -597,11 +645,21 @@
             return cancellationSignal;
         }
 
-        final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(controls,
-                frame, mState, listener, typesReady, this, durationMs, interpolator, fade,
-                layoutInsetsDuringAnimation, animationType);
-        mRunningAnimations.add(new RunningAnimation(controller, animationType));
-        cancellationSignal.setOnCancelListener(controller::onCancelled);
+
+        final InsetsAnimationControlRunner runner = useInsetsAnimationThread
+                ? new InsetsAnimationThreadControlRunner(controls,
+                        frame, mState, listener, typesReady, this, durationMs, interpolator, fade,
+                        animationType, mViewRoot.mHandler)
+                : new InsetsAnimationControlImpl(controls,
+                        frame, mState, listener, typesReady, this, durationMs, interpolator, fade,
+                        animationType);
+        mRunningAnimations.add(new RunningAnimation(runner, animationType));
+        cancellationSignal.setOnCancelListener(runner::cancel);
+        if (layoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN) {
+            showDirectly(types);
+        } else {
+            hideDirectly(types, false /* animationFinished */, animationType);
+        }
         return cancellationSignal;
     }
 
@@ -646,7 +704,7 @@
             }
             final InsetsSourceControl control = consumer.getControl();
             if (control != null) {
-                controls.put(consumer.getType(), control);
+                controls.put(consumer.getType(), new InsetsSourceControl(control));
                 typesReady |= toPublicType(consumer.getType());
             } else if (animationType == ANIMATION_TYPE_SHOW) {
 
@@ -685,7 +743,7 @@
 
     private void cancelExistingControllers(@InsetsType int types) {
         for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
-            InsetsAnimationControlImpl control = mRunningAnimations.get(i).control;
+            InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner;
             if ((control.getTypes() & types) != 0) {
                 cancelAnimation(control, true /* invokeCallback */);
             }
@@ -705,13 +763,13 @@
 
     @VisibleForTesting
     @Override
-    public void notifyFinished(InsetsAnimationControlImpl controller, boolean shown) {
-        cancelAnimation(controller, false /* invokeCallback */);
+    public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) {
+        cancelAnimation(runner, false /* invokeCallback */);
         if (shown) {
-            showDirectly(controller.getTypes());
+            showDirectly(runner.getTypes());
         } else {
-            hideDirectly(controller.getTypes(), true /* animationFinished */,
-                    controller.getAnimationType());
+            hideDirectly(runner.getTypes(), true /* animationFinished */,
+                    runner.getAnimationType());
         }
     }
 
@@ -736,7 +794,7 @@
 
     void notifyControlRevoked(InsetsSourceConsumer consumer) {
         for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
-            InsetsAnimationControlImpl control = mRunningAnimations.get(i).control;
+            InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner;
             if ((control.getTypes() & toPublicType(consumer.getType())) != 0) {
                 cancelAnimation(control, true /* invokeCallback */);
             }
@@ -746,12 +804,12 @@
         }
     }
 
-    private void cancelAnimation(InsetsAnimationControlImpl control, boolean invokeCallback) {
+    private void cancelAnimation(InsetsAnimationControlRunner control, boolean invokeCallback) {
         if (invokeCallback) {
-            control.onCancelled();
+            control.cancel();
         }
         for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
-            if (mRunningAnimations.get(i).control == control) {
+            if (mRunningAnimations.get(i).runner == control) {
                 mRunningAnimations.remove(i);
                 break;
             }
@@ -824,7 +882,7 @@
     @VisibleForTesting
     public @AnimationType int getAnimationType(@InternalInsetsType int type) {
         for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
-            InsetsAnimationControlImpl control = mRunningAnimations.get(i).control;
+            InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner;
             if (control.controlsInternalType(type)) {
                 return mRunningAnimations.get(i).type;
             }
@@ -858,15 +916,24 @@
             return;
         }
 
+        boolean useInsetsAnimationThread = canUseInsetsAnimationThread();
         final InternalAnimationControlListener listener =
-                new InternalAnimationControlListener(show);
+                new InternalAnimationControlListener(show, useInsetsAnimationThread);
         // Show/hide animations always need to be relative to the display frame, in order that shown
         // and hidden state insets are correct.
         controlAnimationUnchecked(
                 types, listener, mState.getDisplayFrame(), fromIme, listener.getDurationMs(),
                 INTERPOLATOR, true /* fade */, show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE,
                 show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
-                        : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN);
+                        : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN,
+                useInsetsAnimationThread);
+    }
+
+    private boolean canUseInsetsAnimationThread() {
+        if (mViewRoot.mView == null) {
+            return true;
+        }
+        return !mViewRoot.mView.hasWindowInsetsAnimationCallback();
     }
 
     private void hideDirectly(
@@ -901,13 +968,7 @@
     @Override
     public void startAnimation(InsetsAnimationControlImpl controller,
             WindowInsetsAnimationControlListener listener, int types,
-            WindowInsetsAnimation animation, Bounds bounds, int layoutDuringAnimation) {
-        if (layoutDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN) {
-            showDirectly(types);
-        } else {
-            hideDirectly(types, false /* animationFinished */, controller.getAnimationType());
-        }
-
+            WindowInsetsAnimation animation, Bounds bounds) {
         if (mViewRoot.mView == null) {
             return;
         }
@@ -921,7 +982,7 @@
                 }
                 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
                     RunningAnimation runningAnimation = mRunningAnimations.get(i);
-                    if (runningAnimation.control == controller) {
+                    if (runningAnimation.runner == controller) {
                         runningAnimation.startDispatched = true;
                     }
                 }
@@ -994,6 +1055,48 @@
         return mViewRoot.mWindowAttributes.insetsFlags.behavior;
     }
 
+    private @InsetsType int calculateControllableTypes() {
+        if (!checkDisplayFramesForControlling()) {
+            return 0;
+        }
+        @InsetsType int result = 0;
+        for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
+            InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
+            if (consumer.getControl() != null) {
+                result |= toPublicType(consumer.mType);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @return The types that are now animating due to a listener invoking control/show/hide
+     */
+    private @InsetsType int invokeControllableInsetsChangedListeners() {
+        mLastStartedAnimTypes = 0;
+        @InsetsType int types = calculateControllableTypes();
+        int size = mControllableInsetsChangedListeners.size();
+        for (int i = 0; i < size; i++) {
+            mControllableInsetsChangedListeners.get(i).onControllableInsetsChanged(this, types);
+        }
+        return mLastStartedAnimTypes;
+    }
+
+    @Override
+    public void addOnControllableInsetsChangedListener(
+            OnControllableInsetsChangedListener listener) {
+        Objects.requireNonNull(listener);
+        mControllableInsetsChangedListeners.add(listener);
+        listener.onControllableInsetsChanged(this, calculateControllableTypes());
+    }
+
+    @Override
+    public void removeOnControllableInsetsChangedListener(
+            OnControllableInsetsChangedListener listener) {
+        Objects.requireNonNull(listener);
+        mControllableInsetsChangedListeners.remove(listener);
+    }
+
     /**
      * At the time we receive new leashes (e.g. InsetsSourceConsumer is processing
      * setControl) we need to release the old leash. But we may have already scheduled
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 252fc0c..3325734 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -117,7 +117,7 @@
             }
         }
         if (lastControl != null) {
-            lastControl.release(mController);
+            lastControl.release(mController::releaseSurfaceControlFromRt);
         }
     }
 
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index 75f6eab..f3ec65f 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -22,6 +22,8 @@
 import android.os.Parcelable;
 import android.view.InsetsState.InternalInsetsType;
 
+import java.util.function.Consumer;
+
 /**
  * Represents a parcelable object to allow controlling a single {@link InsetsSource}.
  * @hide
@@ -94,9 +96,9 @@
         dest.writeParcelable(mSurfacePosition, 0 /* flags*/);
     }
 
-    public void release(InsetsController controller) {
+    public void release(Consumer<SurfaceControl> surfaceReleaseConsumer) {
         if (mLeash != null) {
-            controller.releaseSurfaceControlFromRt(mLeash);
+            surfaceReleaseConsumer.accept(mLeash);
         }
     }
 
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 8ec5df8..18e0132 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -35,6 +35,7 @@
 
 import com.android.internal.R;
 import com.android.internal.widget.CachingIconView;
+import com.android.internal.widget.NotificationExpandButton;
 
 import java.util.ArrayList;
 
@@ -56,7 +57,7 @@
     private OnClickListener mAppOpsListener;
     private HeaderTouchListener mTouchListener = new HeaderTouchListener();
     private LinearLayout mTransferChip;
-    private ImageView mExpandButton;
+    private NotificationExpandButton mExpandButton;
     private CachingIconView mIcon;
     private View mProfileBadge;
     private View mOverlayIcon;
@@ -65,7 +66,6 @@
     private View mAppOps;
     private View mAudiblyAlertedIcon;
     private int mIconColor;
-    private int mOriginalNotificationColor;
     private boolean mExpanded;
     private boolean mShowExpandButtonAtEnd;
     private boolean mShowWorkBadgeAtEnd;
@@ -324,13 +324,8 @@
         return mIconColor;
     }
 
-    @RemotableViewMethod
-    public void setOriginalNotificationColor(int color) {
-        mOriginalNotificationColor = color;
-    }
-
     public int getOriginalNotificationColor() {
-        return mOriginalNotificationColor;
+        return mExpandButton.getOriginalNotificationColor();
     }
 
     @RemotableViewMethod
@@ -371,7 +366,7 @@
             contentDescriptionId = R.string.expand_button_content_description_collapsed;
         }
         mExpandButton.setImageDrawable(getContext().getDrawable(drawableId));
-        mExpandButton.setColorFilter(mOriginalNotificationColor);
+        mExpandButton.setColorFilter(getOriginalNotificationColor());
         mExpandButton.setContentDescription(mContext.getText(contentDescriptionId));
     }
 
diff --git a/core/java/android/view/PendingInsetsController.java b/core/java/android/view/PendingInsetsController.java
index c0ed935..7f36418 100644
--- a/core/java/android/view/PendingInsetsController.java
+++ b/core/java/android/view/PendingInsetsController.java
@@ -38,6 +38,8 @@
     private @Behavior int mBehavior = KEEP_BEHAVIOR;
     private final InsetsState mDummyState = new InsetsState();
     private InsetsController mReplayedInsetsController;
+    private ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners
+            = new ArrayList<>();
 
     @Override
     public void show(int types) {
@@ -112,6 +114,27 @@
         return mDummyState;
     }
 
+    @Override
+    public void addOnControllableInsetsChangedListener(
+            OnControllableInsetsChangedListener listener) {
+        if (mReplayedInsetsController != null) {
+            mReplayedInsetsController.addOnControllableInsetsChangedListener(listener);
+        } else {
+            mControllableInsetsChangedListeners.add(listener);
+            listener.onControllableInsetsChanged(this, 0);
+        }
+    }
+
+    @Override
+    public void removeOnControllableInsetsChangedListener(
+            OnControllableInsetsChangedListener listener) {
+        if (mReplayedInsetsController != null) {
+            mReplayedInsetsController.removeOnControllableInsetsChangedListener(listener);
+        } else {
+            mControllableInsetsChangedListeners.remove(listener);
+        }
+    }
+
     /**
      * Replays the commands on {@code controller} and attaches it to this instance such that any
      * calls will be forwarded to the real instance in the future.
@@ -128,9 +151,15 @@
         for (int i = 0; i < size; i++) {
             mRequests.get(i).replay(controller);
         }
+        size = mControllableInsetsChangedListeners.size();
+        for (int i = 0; i < size; i++) {
+            controller.addOnControllableInsetsChangedListener(
+                    mControllableInsetsChangedListeners.get(i));
+        }
 
         // Reset all state so it doesn't get applied twice just in case
         mRequests.clear();
+        mControllableInsetsChangedListeners.clear();
         mBehavior = KEEP_BEHAVIOR;
         mAppearance = 0;
         mAppearanceMask = 0;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 6236e6e..708a094 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3318,7 +3318,7 @@
      * Flag indicating that the view is autofilled
      *
      * @see #isAutofilled()
-     * @see #setAutofilled(boolean)
+     * @see #setAutofilled(boolean, boolean)
      */
     private static final int PFLAG3_IS_AUTOFILLED = 0x10000;
 
@@ -3428,6 +3428,7 @@
      *                         1        PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE
      *                         11       PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK
      *                        1         PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS
+     *                       1          PFLAG4_AUTOFILL_HIDE_HIGHLIGHT
      * |-------|-------|-------|-------|
      */
 
@@ -3470,6 +3471,11 @@
      */
     static final int PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS = 0x000000100;
 
+    /**
+     * Flag indicating the field should not have yellow highlight when autofilled.
+     */
+    private static final int PFLAG4_AUTOFILL_HIDE_HIGHLIGHT = 0x100;
+
     /* End of masks for mPrivateFlags4 */
 
     /** @hide */
@@ -9170,6 +9176,13 @@
     }
 
     /**
+     * @hide
+     */
+    public boolean hideAutofillHighlight() {
+        return (mPrivateFlags4 & PFLAG4_AUTOFILL_HIDE_HIGHLIGHT) != 0;
+    }
+
+    /**
      * Gets the {@link View}'s current autofill value.
      *
      * <p>By default returns {@code null}, but subclasses should override it and return an
@@ -11227,6 +11240,15 @@
     }
 
     /**
+     * @return {@code true} if any {@link WindowInsetsAnimation.Callback} is registered on the view
+     *         or view tree of the sub-hierarchy {@code false} otherwise.
+     * @hide
+     */
+    public boolean hasWindowInsetsAnimationCallback() {
+        return getListenerInfo().mWindowInsetsAnimationCallback != null;
+    }
+
+    /**
      * Dispatches {@link WindowInsetsAnimation.Callback#onPrepare(WindowInsetsAnimation)}
      * when Window Insets animation is being prepared.
      * @param animation current animation
@@ -11741,7 +11763,7 @@
      * @hide
      */
     @TestApi
-    public void setAutofilled(boolean isAutofilled) {
+    public void setAutofilled(boolean isAutofilled, boolean hideHighlight) {
         boolean wasChanged = isAutofilled != isAutofilled();
 
         if (wasChanged) {
@@ -11751,6 +11773,12 @@
                 mPrivateFlags3 &= ~PFLAG3_IS_AUTOFILLED;
             }
 
+            if (hideHighlight) {
+                mPrivateFlags4 |= PFLAG4_AUTOFILL_HIDE_HIGHLIGHT;
+            } else {
+                mPrivateFlags4 &= ~PFLAG4_AUTOFILL_HIDE_HIGHLIGHT;
+            }
+
             invalidate();
         }
     }
@@ -20569,6 +20597,7 @@
 
             state.mStartActivityRequestWhoSaved = mStartActivityRequestWho;
             state.mIsAutofilled = isAutofilled();
+            state.mHideHighlight = hideAutofillHighlight();
             state.mAutofillViewId = mAutofillViewId;
             return state;
         }
@@ -20645,7 +20674,7 @@
                 mStartActivityRequestWho = baseState.mStartActivityRequestWhoSaved;
             }
             if ((baseState.mSavedData & BaseSavedState.IS_AUTOFILLED) != 0) {
-                setAutofilled(baseState.mIsAutofilled);
+                setAutofilled(baseState.mIsAutofilled, baseState.mHideHighlight);
             }
             if ((baseState.mSavedData & BaseSavedState.AUTOFILL_ID) != 0) {
                 // It can happen that views have the same view id and the restoration path will not
@@ -24078,12 +24107,13 @@
     }
 
     /**
-     * Draw {@link View#isAutofilled()} highlight over view if the view is autofilled.
+     * Draw {@link View#isAutofilled()} highlight over view if the view is autofilled, unless
+     * {@link #PFLAG4_AUTOFILL_HIDE_HIGHLIGHT} is enabled.
      *
      * @param canvas The canvas to draw on
      */
     private void drawAutofilledHighlight(@NonNull Canvas canvas) {
-        if (isAutofilled()) {
+        if (isAutofilled() && !hideAutofillHighlight()) {
             Drawable autofilledHighlight = getAutofilledDrawable();
 
             if (autofilledHighlight != null) {
@@ -28526,6 +28556,7 @@
         int mSavedData;
         String mStartActivityRequestWhoSaved;
         boolean mIsAutofilled;
+        boolean mHideHighlight;
         int mAutofillViewId;
 
         /**
@@ -28549,6 +28580,7 @@
             mSavedData = source.readInt();
             mStartActivityRequestWhoSaved = source.readString();
             mIsAutofilled = source.readBoolean();
+            mHideHighlight = source.readBoolean();
             mAutofillViewId = source.readInt();
         }
 
@@ -28568,6 +28600,7 @@
             out.writeInt(mSavedData);
             out.writeString(mStartActivityRequestWhoSaved);
             out.writeBoolean(mIsAutofilled);
+            out.writeBoolean(mHideHighlight);
             out.writeInt(mAutofillViewId);
         }
 
@@ -29066,8 +29099,33 @@
             mTreeObserver = new ViewTreeObserver(context);
         }
 
+        @Nullable
+        ContentCaptureManager getContentCaptureManager(@NonNull Context context) {
+            if (mContentCaptureManager != null) {
+                return mContentCaptureManager;
+            }
+            mContentCaptureManager = context.getSystemService(ContentCaptureManager.class);
+            return mContentCaptureManager;
+        }
+
+        void delayNotifyContentCaptureInsetsEvent(@NonNull Insets insets) {
+            if (mContentCaptureManager == null) {
+                return;
+            }
+
+            ArrayList<Object> events = ensureEvents(
+                        mContentCaptureManager.getMainContentCaptureSession());
+            events.add(insets);
+        }
+
         private void delayNotifyContentCaptureEvent(@NonNull ContentCaptureSession session,
                 @NonNull View view, boolean appeared) {
+            ArrayList<Object> events = ensureEvents(session);
+            events.add(appeared ? view : view.getAutofillId());
+        }
+
+        @NonNull
+        private ArrayList<Object> ensureEvents(@NonNull ContentCaptureSession session) {
             if (mContentCaptureEvents == null) {
                 // Most of the time there will be just one session, so intial capacity is 1
                 mContentCaptureEvents = new SparseArray<>(1);
@@ -29079,16 +29137,8 @@
                 events = new ArrayList<>();
                 mContentCaptureEvents.put(sessionId, events);
             }
-            events.add(appeared ? view : view.getAutofillId());
-        }
 
-        @Nullable
-        ContentCaptureManager getContentCaptureManager(@NonNull Context context) {
-            if (mContentCaptureManager != null) {
-                return mContentCaptureManager;
-            }
-            mContentCaptureManager = context.getSystemService(ContentCaptureManager.class);
-            return mContentCaptureManager;
+            return events;
         }
     }
 
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 7d4ec3d..e34e84c 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -7258,6 +7258,34 @@
                 : DISPATCH_MODE_CONTINUE_ON_SUBTREE;
     }
 
+    /**
+     * @hide
+     */
+    @Override
+    public boolean hasWindowInsetsAnimationCallback() {
+        if (super.hasWindowInsetsAnimationCallback()) {
+            return true;
+        }
+
+        // If we are root-level content view that fits insets, we imitate consuming behavior, so
+        // no child will retrieve window insets animation callback.
+        // See dispatchWindowInsetsAnimationPrepare.
+        boolean isOptionalFitSystemWindows = (mViewFlags & OPTIONAL_FITS_SYSTEM_WINDOWS) != 0
+                || isFrameworkOptionalFitsSystemWindows();
+        if (isOptionalFitSystemWindows && mAttachInfo != null
+                && mAttachInfo.mContentOnApplyWindowInsetsListener != null) {
+            return false;
+        }
+
+        final int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            if (getChildAt(i).hasWindowInsetsAnimationCallback()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     @Override
     public void dispatchWindowInsetsAnimationPrepare(
             @NonNull WindowInsetsAnimation animation) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9228fbd..dd34bcb 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -81,6 +81,7 @@
 import android.graphics.Color;
 import android.graphics.FrameInfo;
 import android.graphics.HardwareRenderer.FrameDrawingCallback;
+import android.graphics.Insets;
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
@@ -2254,6 +2255,7 @@
             insets = insets.consumeDisplayCutout();
         }
         host.dispatchApplyWindowInsets(insets);
+        mAttachInfo.delayNotifyContentCaptureInsetsEvent(insets.getInsets(Type.all()));
         Trace.traceEnd(Trace.TRACE_TAG_VIEW);
     }
 
@@ -3118,6 +3120,8 @@
                         ViewStructure structure = session.newViewStructure(view);
                         view.onProvideContentCaptureStructure(structure, /* flags= */ 0);
                         session.notifyViewAppeared(structure);
+                    } else if (event instanceof Insets) {
+                        mainSession.notifyViewInsetsChanged(sessionId, (Insets) event);
                     } else {
                         Log.w(mTag, "invalid content capture event: " + event);
                     }
@@ -5699,9 +5703,9 @@
                 mTranslator.translateEventInScreenToAppWindow(event);
             }
 
-            // Enter touch mode if event is coming from a touch screen device.
+            // Enter touch mode on down or scroll from any type of a device.
             final int action = event.getAction();
-            if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
+            if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) {
                 ensureTouchMode(true);
             }
 
@@ -9054,7 +9058,7 @@
 
         @Override
         public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
-                boolean sync) {
+                float zoom, boolean sync) {
             if (sync) {
                 try {
                     mWindowSession.wallpaperOffsetsComplete(asBinder());
diff --git a/core/java/android/view/WindowContainerTransaction.java b/core/java/android/view/WindowContainerTransaction.java
index e05c374..9c16e13 100644
--- a/core/java/android/view/WindowContainerTransaction.java
+++ b/core/java/android/view/WindowContainerTransaction.java
@@ -131,6 +131,31 @@
     }
 
     /**
+     * Set the windowing mode of children of a given root task, without changing
+     * the windowing mode of the Task itself. This can be used during transitions
+     * for example to make the activity render it's fullscreen configuration
+     * while the Task is still in PIP, so you can complete the animation.
+     *
+     * TODO(b/134365562): Can be removed once TaskOrg drives full-screen
+     */
+    public WindowContainerTransaction setActivityWindowingMode(IWindowContainer container,
+            int windowingMode) {
+        Change chg = getOrCreateChange(container.asBinder());
+        chg.mActivityWindowingMode = windowingMode;
+        return this;
+    }
+
+    /**
+     * Sets the windowing mode of the given container.
+     */
+    public WindowContainerTransaction setWindowingMode(IWindowContainer container,
+            int windowingMode) {
+        Change chg = getOrCreateChange(container.asBinder());
+        chg.mWindowingMode = windowingMode;
+        return this;
+    }
+
+    /**
      * Sets whether a container or any of its children can be focusable. When {@code false}, no
      * child can be focused; however, when {@code true}, it is still possible for children to be
      * non-focusable due to WM policy.
@@ -235,6 +260,9 @@
         private Rect mPinnedBounds = null;
         private SurfaceControl.Transaction mBoundsChangeTransaction = null;
 
+        private int mActivityWindowingMode = -1;
+        private int mWindowingMode = -1;
+
         public Change() {}
 
         protected Change(Parcel in) {
@@ -251,6 +279,17 @@
                 mBoundsChangeTransaction =
                     SurfaceControl.Transaction.CREATOR.createFromParcel(in);
             }
+
+            mWindowingMode = in.readInt();
+            mActivityWindowingMode = in.readInt();
+        }
+
+        public int getWindowingMode() {
+            return mWindowingMode;
+        }
+
+        public int getActivityWindowingMode() {
+            return mActivityWindowingMode;
         }
 
         public Configuration getConfiguration() {
@@ -340,6 +379,9 @@
             if (mBoundsChangeTransaction != null) {
                 mBoundsChangeTransaction.writeToParcel(dest, flags);
             }
+
+            dest.writeInt(mWindowingMode);
+            dest.writeInt(mActivityWindowingMode);
         }
 
         @Override
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index b7ca037..2ad557e 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -20,7 +20,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Insets;
+import android.inputmethodservice.InputMethodService;
 import android.os.CancellationSignal;
+import android.view.WindowInsets.Type;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.animation.Interpolator;
 
@@ -212,4 +214,55 @@
      * @hide
      */
     InsetsState getState();
+
+    /**
+     * Adds a {@link OnControllableInsetsChangedListener} to the window insets controller.
+     *
+     * @param listener The listener to add.
+     *
+     * @see OnControllableInsetsChangedListener
+     * @see #removeOnControllableInsetsChangedListener(OnControllableInsetsChangedListener)
+     */
+    void addOnControllableInsetsChangedListener(
+            @NonNull OnControllableInsetsChangedListener listener);
+
+    /**
+     * Removes a {@link OnControllableInsetsChangedListener} from the window insets controller.
+     *
+     * @param listener The listener to remove.
+     *
+     * @see OnControllableInsetsChangedListener
+     * @see #addOnControllableInsetsChangedListener(OnControllableInsetsChangedListener)
+     */
+    void removeOnControllableInsetsChangedListener(
+            @NonNull OnControllableInsetsChangedListener listener);
+
+    /**
+     * Listener to be notified when the set of controllable {@link InsetsType} controlled by a
+     * {@link WindowInsetsController} changes.
+     * <p>
+     * Once a {@link InsetsType} becomes controllable, the app will be able to control the window
+     * that is causing this type of insets by calling {@link #controlWindowInsetsAnimation}.
+     * <p>
+     * Note: When listening to controllability of the {@link Type#ime},
+     * {@link #controlWindowInsetsAnimation} may still fail in case the {@link InputMethodService}
+     * decides to cancel the show request. This could happen when there is a hardware keyboard
+     * attached.
+     *
+     * @see #addOnControllableInsetsChangedListener(OnControllableInsetsChangedListener)
+     * @see #removeOnControllableInsetsChangedListener(OnControllableInsetsChangedListener)
+     */
+    interface OnControllableInsetsChangedListener {
+
+        /**
+         * Called when the set of controllable {@link InsetsType} changes.
+         *
+         * @param controller The controller for which the set of controllable {@link InsetsType}s
+         *                   are changing.
+         * @param typeMask Bitwise type-mask of the {@link InsetsType}s the controller is currently
+         *                 able to control.
+         */
+        void onControllableInsetsChanged(@NonNull WindowInsetsController controller,
+                @InsetsType int typeMask);
+    }
 }
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 867b648..c5fa3c8 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -805,6 +805,7 @@
                 @ViewDebug.IntToString(from = TYPE_APPLICATION_OVERLAY,
                         to = "APPLICATION_OVERLAY")
         })
+        @WindowType
         public int type;
 
         /**
@@ -1244,13 +1245,47 @@
         public static final int INVALID_WINDOW_TYPE = -1;
 
         /**
+         * @hide
+         */
+        @IntDef(prefix = "TYPE_", value = {
+                TYPE_ACCESSIBILITY_OVERLAY,
+                TYPE_APPLICATION,
+                TYPE_APPLICATION_ATTACHED_DIALOG,
+                TYPE_APPLICATION_MEDIA,
+                TYPE_APPLICATION_OVERLAY,
+                TYPE_APPLICATION_PANEL,
+                TYPE_APPLICATION_STARTING,
+                TYPE_APPLICATION_SUB_PANEL,
+                TYPE_BASE_APPLICATION,
+                TYPE_DRAWN_APPLICATION,
+                TYPE_INPUT_METHOD,
+                TYPE_INPUT_METHOD_DIALOG,
+                TYPE_KEYGUARD,
+                TYPE_KEYGUARD_DIALOG,
+                TYPE_PHONE,
+                TYPE_PRIORITY_PHONE,
+                TYPE_PRIVATE_PRESENTATION,
+                TYPE_SEARCH_BAR,
+                TYPE_STATUS_BAR,
+                TYPE_STATUS_BAR_PANEL,
+                TYPE_SYSTEM_ALERT,
+                TYPE_SYSTEM_DIALOG,
+                TYPE_SYSTEM_ERROR,
+                TYPE_SYSTEM_OVERLAY,
+                TYPE_TOAST,
+                TYPE_WALLPAPER,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface WindowType {}
+
+        /**
          * Return true if the window type is an alert window.
          *
          * @param type The window type.
          * @return If the window type is an alert window.
          * @hide
          */
-        public static boolean isSystemAlertWindowType(int type) {
+        public static boolean isSystemAlertWindowType(@WindowType int type) {
             switch (type) {
                 case TYPE_PHONE:
                 case TYPE_PRIORITY_PHONE:
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 01a1c77..410d9af 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -504,6 +504,7 @@
     }
 
     void doRemoveView(ViewRootImpl root) {
+        boolean allViewsRemoved;
         synchronized (mLock) {
             final int index = mRoots.indexOf(root);
             if (index >= 0) {
@@ -512,10 +513,17 @@
                 final View view = mViews.remove(index);
                 mDyingViews.remove(view);
             }
+            allViewsRemoved = mRoots.isEmpty();
         }
         if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
             doTrimForeground();
         }
+
+        // If we don't have any views anymore in our process, we no longer need the
+        // InsetsAnimationThread to save some resources.
+        if (allViewsRemoved) {
+            InsetsAnimationThread.release();
+        }
     }
 
     private int findViewLocked(View view, boolean required) {
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 87dcba0..144f8e3 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -312,6 +312,14 @@
     }
 
     @Override
+    public void setWallpaperZoomOut(android.os.IBinder windowToken, float zoom) {
+    }
+
+    @Override
+    public void setShouldZoomOutWallpaper(android.os.IBinder windowToken, boolean shouldZoom) {
+    }
+
+    @Override
     public void wallpaperOffsetsComplete(android.os.IBinder window) {
     }
 
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index ce7cfa7..39a9ed4 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -17,6 +17,7 @@
 package android.view.autofill;
 
 import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
+import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE;
 import static android.view.autofill.Helper.sDebug;
 import static android.view.autofill.Helper.sVerbose;
 import static android.view.autofill.Helper.toList;
@@ -63,6 +64,7 @@
 import android.view.accessibility.AccessibilityNodeProvider;
 import android.view.accessibility.AccessibilityWindowInfo;
 import android.widget.EditText;
+import android.widget.TextView;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
@@ -983,6 +985,10 @@
             if (!isClientDisablingEnterExitEvent()) {
                 final AutofillValue value = view.getAutofillValue();
 
+                if (view instanceof TextView && ((TextView) view).isAnyPasswordInputType()) {
+                    flags |= FLAG_PASSWORD_INPUT_TYPE;
+                }
+
                 if (!isActiveLocked()) {
                     // Starts new session.
                     startSessionLocked(id, null, value, flags);
@@ -1149,6 +1155,10 @@
         } else {
             // don't notify entered when Activity is already in background
             if (!isClientDisablingEnterExitEvent()) {
+                if (view instanceof TextView && ((TextView) view).isAnyPasswordInputType()) {
+                    flags |= FLAG_PASSWORD_INPUT_TYPE;
+                }
+
                 if (!isActiveLocked()) {
                     // Starts new session.
                     startSessionLocked(id, bounds, null, flags);
@@ -1226,7 +1236,7 @@
             // If the session is gone some fields might still be highlighted, hence we have to
             // remove the isAutofilled property even if no sessions are active.
             if (mLastAutofilledData == null) {
-                view.setAutofilled(false);
+                view.setAutofilled(false, false);
             } else {
                 id = view.getAutofillId();
                 if (mLastAutofilledData.containsKey(id)) {
@@ -1234,13 +1244,13 @@
                     valueWasRead = true;
 
                     if (Objects.equals(mLastAutofilledData.get(id), value)) {
-                        view.setAutofilled(true);
+                        view.setAutofilled(true, false);
                     } else {
-                        view.setAutofilled(false);
+                        view.setAutofilled(false, false);
                         mLastAutofilledData.remove(id);
                     }
                 } else {
-                    view.setAutofilled(false);
+                    view.setAutofilled(false, false);
                 }
             }
 
@@ -2156,7 +2166,8 @@
      * @param view The view that is to be autofilled
      * @param targetValue The value we want to fill into view
      */
-    private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
+    private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue,
+            boolean hideHighlight) {
         AutofillValue currentValue = view.getAutofillValue();
         if (Objects.equals(currentValue, targetValue)) {
             synchronized (mLock) {
@@ -2165,11 +2176,12 @@
                 }
                 mLastAutofilledData.put(view.getAutofillId(), targetValue);
             }
-            view.setAutofilled(true);
+            view.setAutofilled(true, hideHighlight);
         }
     }
 
-    private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
+    private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values,
+            boolean hideHighlight) {
         synchronized (mLock) {
             if (sessionId != mSessionId) {
                 return;
@@ -2228,7 +2240,7 @@
                     // synchronously.
                     // If autofill happens async, the view is set to autofilled in
                     // notifyValueChanged.
-                    setAutofilledIfValuesIs(view, value);
+                    setAutofilledIfValuesIs(view, value, hideHighlight);
 
                     numApplied++;
                 }
@@ -3246,10 +3258,11 @@
         }
 
         @Override
-        public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
+        public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values,
+                boolean hideHighlight) {
             final AutofillManager afm = mAfm.get();
             if (afm != null) {
-                afm.post(() -> afm.autofill(sessionId, ids, values));
+                afm.post(() -> afm.autofill(sessionId, ids, values, hideHighlight));
             }
         }
 
@@ -3387,10 +3400,11 @@
         }
 
         @Override
-        public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
+        public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values,
+                boolean hideHighlight) {
             final AutofillManager afm = mAfm.get();
             if (afm != null) {
-                afm.post(() -> afm.autofill(sessionId, ids, values));
+                afm.post(() -> afm.autofill(sessionId, ids, values, hideHighlight));
             }
         }
 
diff --git a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
index 03054df..8526c1e 100644
--- a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
@@ -38,7 +38,8 @@
     /**
      * Autofills the activity with the contents of the values.
      */
-    void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values);
+    void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values,
+            boolean hideHighlight);
 
     /**
       * Requests showing the fill UI.
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 3903665..4371b3c 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -44,7 +44,8 @@
     /**
       * Autofills the activity with the contents of a dataset.
       */
-    void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values);
+    void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values,
+            boolean hideHighlight);
 
     /**
       * Authenticates a fill response or a data set.
diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
index 7487ec4..44b4353 100644
--- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.graphics.Insets;
 import android.view.autofill.AutofillId;
 import android.view.contentcapture.ViewNode.ViewStructureImpl;
 
@@ -84,6 +85,11 @@
     }
 
     @Override
+    void internalNotifyViewInsetsChanged(@NonNull Insets viewInsets) {
+        getMainCaptureSession().notifyViewInsetsChanged(mId, viewInsets);
+    }
+
+    @Override
     public void internalNotifyViewTreeEvent(boolean started) {
         getMainCaptureSession().notifyViewTreeEvent(mId, started);
     }
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index c29d251..ea34d94 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -23,6 +23,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.graphics.Insets;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
@@ -112,6 +113,11 @@
      */
     public static final int TYPE_SESSION_PAUSED = 8;
 
+    /**
+     * Called when the view's insets are changed. The new insets associated with the
+     * event may then be retrieved by calling {@link #getInsets()}
+     */
+    public static final int TYPE_VIEW_INSETS_CHANGED = 9;
 
     /** @hide */
     @IntDef(prefix = { "TYPE_" }, value = {
@@ -122,7 +128,8 @@
             TYPE_VIEW_TREE_APPEARED,
             TYPE_CONTEXT_UPDATED,
             TYPE_SESSION_PAUSED,
-            TYPE_SESSION_RESUMED
+            TYPE_SESSION_RESUMED,
+            TYPE_VIEW_INSETS_CHANGED
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventType{}
@@ -136,6 +143,7 @@
     private @Nullable CharSequence mText;
     private int mParentSessionId = NO_SESSION_ID;
     private @Nullable ContentCaptureContext mClientContext;
+    private @Nullable Insets mInsets;
 
     /** @hide */
     public ContentCaptureEvent(int sessionId, int type, long eventTime) {
@@ -242,6 +250,13 @@
         return this;
     }
 
+    /** @hide */
+    @NonNull
+    public ContentCaptureEvent setInsets(@NonNull Insets insets) {
+        mInsets = insets;
+        return this;
+    }
+
     /**
      * Gets the type of the event.
      *
@@ -305,6 +320,16 @@
     }
 
     /**
+     * Gets the rectangle of the insets associated with the event. Valid insets will only be
+     * returned if the type of the event is {@link #TYPE_VIEW_INSETS_CHANGED}, otherwise they
+     * will be null.
+     */
+    @Nullable
+    public Insets getInsets() {
+        return mInsets;
+    }
+
+    /**
      * Merges event of the same type, either {@link #TYPE_VIEW_TEXT_CHANGED}
      * or {@link #TYPE_VIEW_DISAPPEARED}.
      *
@@ -369,7 +394,9 @@
         }
         if (mClientContext != null) {
             pw.print(", context="); mClientContext.dump(pw); pw.println();
-
+        }
+        if (mInsets != null) {
+            pw.print(", insets="); pw.println(mInsets);
         }
     }
 
@@ -401,6 +428,9 @@
         if (mClientContext != null) {
             string.append(", context=").append(mClientContext);
         }
+        if (mInsets != null) {
+            string.append(", insets=").append(mInsets);
+        }
         return string.append(']').toString();
     }
 
@@ -424,6 +454,9 @@
         if (mType == TYPE_SESSION_STARTED || mType == TYPE_CONTEXT_UPDATED) {
             parcel.writeParcelable(mClientContext, flags);
         }
+        if (mType == TYPE_VIEW_INSETS_CHANGED) {
+            parcel.writeParcelable(mInsets, flags);
+        }
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<ContentCaptureEvent> CREATOR =
@@ -455,6 +488,9 @@
             if (type == TYPE_SESSION_STARTED || type == TYPE_CONTEXT_UPDATED) {
                 event.setClientContext(parcel.readParcelable(null));
             }
+            if (type == TYPE_VIEW_INSETS_CHANGED) {
+                event.setInsets(parcel.readParcelable(null));
+            }
             return event;
         }
 
@@ -488,6 +524,8 @@
                 return "VIEW_TREE_APPEARED";
             case TYPE_CONTEXT_UPDATED:
                 return "CONTEXT_UPDATED";
+            case TYPE_VIEW_INSETS_CHANGED:
+                return "VIEW_INSETS_CHANGED";
             default:
                 return "UKNOWN_TYPE: " + type;
         }
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 2134dab..012f5e6 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -23,6 +23,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.graphics.Insets;
 import android.util.DebugUtils;
 import android.util.Log;
 import android.view.View;
@@ -440,6 +441,19 @@
     abstract void internalNotifyViewTextChanged(@NonNull AutofillId id,
             @Nullable CharSequence text);
 
+    /**
+     * Notifies the Intelligence Service that the insets of a view have changed.
+     */
+    public final void notifyViewInsetsChanged(@NonNull Insets viewInsets) {
+        Preconditions.checkNotNull(viewInsets);
+
+        if (!isContentCaptureEnabled()) return;
+
+        internalNotifyViewInsetsChanged(viewInsets);
+    }
+
+    abstract void internalNotifyViewInsetsChanged(@NonNull Insets viewInsets);
+
     /** @hide */
     public abstract void internalNotifyViewTreeEvent(boolean started);
 
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 96f224f..893d38d 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -22,6 +22,7 @@
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_INSETS_CHANGED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TREE_APPEARED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TREE_APPEARING;
@@ -36,6 +37,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
+import android.graphics.Insets;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -578,6 +580,11 @@
     }
 
     @Override
+    void internalNotifyViewInsetsChanged(@NonNull Insets viewInsets) {
+        notifyViewInsetsChanged(mId, viewInsets);
+    }
+
+    @Override
     public void internalNotifyViewTreeEvent(boolean started) {
         notifyViewTreeEvent(mId, started);
     }
@@ -642,6 +649,12 @@
     }
 
     /** Public because is also used by ViewRootImpl */
+    public void notifyViewInsetsChanged(int sessionId, @NonNull Insets viewInsets) {
+        sendEvent(new ContentCaptureEvent(sessionId, TYPE_VIEW_INSETS_CHANGED)
+                .setInsets(viewInsets));
+    }
+
+    /** Public because is also used by ViewRootImpl */
     public void notifyViewTreeEvent(int sessionId, boolean started) {
         final int type = started ? TYPE_VIEW_TREE_APPEARING : TYPE_VIEW_TREE_APPEARED;
         sendEvent(new ContentCaptureEvent(sessionId, type), FORCE_FLUSH);
diff --git a/core/java/android/view/inputmethod/InlineSuggestion.java b/core/java/android/view/inputmethod/InlineSuggestion.java
index 6500613..dd1738a 100644
--- a/core/java/android/view/inputmethod/InlineSuggestion.java
+++ b/core/java/android/view/inputmethod/InlineSuggestion.java
@@ -16,6 +16,7 @@
 
 package android.view.inputmethod;
 
+import android.annotation.BinderThread;
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -94,19 +95,21 @@
     }
 
 
-
     /**
      * Inflates a view with the content of this suggestion at a specific size.
      * The size must be between the {@link InlinePresentationSpec#getMinSize() min size}
      * and the {@link InlinePresentationSpec#getMaxSize() max size} of the presentation
      * spec returned by {@link InlineSuggestionInfo#getPresentationSpec()}.
      *
-     * @param context Context in which to inflate the view.
-     * @param size The size at which to inflate the suggestion.
-     * @param callback Callback for receiving the inflated view.
+     * <p> The caller can attach an {@link View.OnClickListener} and/or an
+     * {@link View.OnLongClickListener} to the view in the {@code callback} to receive click and
+     * long click events on the view.
      *
+     * @param context  Context in which to inflate the view.
+     * @param size     The size at which to inflate the suggestion.
+     * @param callback Callback for receiving the inflated view.
      * @throws IllegalArgumentException If an invalid argument is passed.
-     * @throws IllegalStateException if this method is already called.
+     * @throws IllegalStateException    If this method is already called.
      */
     public void inflate(@NonNull Context context, @NonNull Size size,
             @NonNull @CallbackExecutor Executor callbackExecutor,
@@ -151,12 +154,31 @@
         }
 
         @Override
+        @BinderThread
         public void onContent(SurfaceControlViewHost.SurfacePackage content) {
             final InlineContentCallbackImpl callbackImpl = mCallbackImpl.get();
             if (callbackImpl != null) {
                 callbackImpl.onContent(content);
             }
         }
+
+        @Override
+        @BinderThread
+        public void onClick() {
+            final InlineContentCallbackImpl callbackImpl = mCallbackImpl.get();
+            if (callbackImpl != null) {
+                callbackImpl.onClick();
+            }
+        }
+
+        @Override
+        @BinderThread
+        public void onLongClick() {
+            final InlineContentCallbackImpl callbackImpl = mCallbackImpl.get();
+            if (callbackImpl != null) {
+                callbackImpl.onLongClick();
+            }
+        }
     }
 
     private static final class InlineContentCallbackImpl {
@@ -164,6 +186,7 @@
         private final @NonNull Context mContext;
         private final @NonNull Executor mCallbackExecutor;
         private final @NonNull Consumer<View> mCallback;
+        private @Nullable View mView;
 
         InlineContentCallbackImpl(@NonNull Context context,
                 @NonNull @CallbackExecutor Executor callbackExecutor,
@@ -173,12 +196,27 @@
             mCallback = callback;
         }
 
+        @BinderThread
         public void onContent(SurfaceControlViewHost.SurfacePackage content) {
             if (content == null) {
                 mCallbackExecutor.execute(() -> mCallback.accept(/* view */null));
             } else {
-                mCallbackExecutor.execute(
-                        () -> mCallback.accept(new InlineContentView(mContext, content)));
+                mView = new InlineContentView(mContext, content);
+                mCallbackExecutor.execute(() -> mCallback.accept(mView));
+            }
+        }
+
+        @BinderThread
+        public void onClick() {
+            if (mView != null && mView.hasOnClickListeners()) {
+                mView.callOnClick();
+            }
+        }
+
+        @BinderThread
+        public void onLongClick() {
+            if (mView != null && mView.hasOnLongClickListeners()) {
+                mView.performLongClick();
             }
         }
     }
@@ -201,7 +239,7 @@
 
 
 
-    // Code below generated by codegen v1.0.14.
+    // Code below generated by codegen v1.0.15.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -360,8 +398,8 @@
     };
 
     @DataClass.Generated(
-            time = 1581929285156L,
-            codegenVersion = "1.0.14",
+            time = 1583889058241L,
+            codegenVersion = "1.0.15",
             sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestion.java",
             inputSignatures = "private static final  java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\nprivate @com.android.internal.util.DataClass.ParcelWith(android.view.inputmethod.InlineSuggestion.InlineContentCallbackImplParceling.class) @android.annotation.Nullable android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl mInlineContentCallback\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestion newInlineSuggestion(android.view.inputmethod.InlineSuggestionInfo)\npublic  void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.view.View>)\nprivate synchronized  android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl getInlineContentCallback(android.content.Context,java.util.concurrent.Executor,java.util.function.Consumer<android.view.View>)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
     @Deprecated
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index e50da40..8e8c7df 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -44,15 +44,15 @@
     public static final int SUGGESTION_COUNT_UNLIMITED = Integer.MAX_VALUE;
 
     /**
-     * Max number of suggestions expected from the response. Defaults to {@code
-     * SUGGESTION_COUNT_UNLIMITED} if not set.
+     * Max number of suggestions expected from the response. It must be a positive value.
+     * Defaults to {@code SUGGESTION_COUNT_UNLIMITED} if not set.
      */
     private final int mMaxSuggestionCount;
 
     /**
      * The {@link InlinePresentationSpec} for each suggestion in the response. If the max suggestion
      * count is larger than the number of specs in the list, then the last spec is used for the
-     * remainder of the suggestions.
+     * remainder of the suggestions. The list should not be empty.
      */
     private final @NonNull List<InlinePresentationSpec> mPresentationSpecs;
 
@@ -72,6 +72,7 @@
     /**
      * The extras state propagated from the IME to pass extra data.
      */
+    @DataClass.MaySetToNull
     private @Nullable Bundle mExtras;
 
     /**
@@ -80,6 +81,7 @@
      *
      * @hide
      */
+    @DataClass.MaySetToNull
     private @Nullable IBinder mHostInputToken;
 
     /**
@@ -117,6 +119,7 @@
     }
 
     private void onConstructed() {
+        Preconditions.checkState(!mPresentationSpecs.isEmpty());
         Preconditions.checkState(mMaxSuggestionCount >= mPresentationSpecs.size());
     }
 
@@ -162,7 +165,7 @@
 
 
 
-    // Code below generated by codegen v1.0.14.
+    // Code below generated by codegen v1.0.15.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -202,8 +205,8 @@
     }
 
     /**
-     * Max number of suggestions expected from the response. Defaults to {@code
-     * SUGGESTION_COUNT_UNLIMITED} if not set.
+     * Max number of suggestions expected from the response. It must be a positive value.
+     * Defaults to {@code SUGGESTION_COUNT_UNLIMITED} if not set.
      */
     @DataClass.Generated.Member
     public int getMaxSuggestionCount() {
@@ -213,7 +216,7 @@
     /**
      * The {@link InlinePresentationSpec} for each suggestion in the response. If the max suggestion
      * count is larger than the number of specs in the list, then the last spec is used for the
-     * remainder of the suggestions.
+     * remainder of the suggestions. The list should not be empty.
      */
     @DataClass.Generated.Member
     public @NonNull List<InlinePresentationSpec> getPresentationSpecs() {
@@ -419,7 +422,7 @@
          * @param presentationSpecs
          *   The {@link InlinePresentationSpec} for each suggestion in the response. If the max suggestion
          *   count is larger than the number of specs in the list, then the last spec is used for the
-         *   remainder of the suggestions.
+         *   remainder of the suggestions. The list should not be empty.
          */
         public Builder(
                 @NonNull List<InlinePresentationSpec> presentationSpecs) {
@@ -429,8 +432,8 @@
         }
 
         /**
-         * Max number of suggestions expected from the response. Defaults to {@code
-         * SUGGESTION_COUNT_UNLIMITED} if not set.
+         * Max number of suggestions expected from the response. It must be a positive value.
+         * Defaults to {@code SUGGESTION_COUNT_UNLIMITED} if not set.
          */
         @DataClass.Generated.Member
         public @NonNull Builder setMaxSuggestionCount(int value) {
@@ -443,7 +446,7 @@
         /**
          * The {@link InlinePresentationSpec} for each suggestion in the response. If the max suggestion
          * count is larger than the number of specs in the list, then the last spec is used for the
-         * remainder of the suggestions.
+         * remainder of the suggestions. The list should not be empty.
          */
         @DataClass.Generated.Member
         @Override
@@ -575,10 +578,10 @@
     }
 
     @DataClass.Generated(
-            time = 1582339908980L,
-            codegenVersion = "1.0.14",
+            time = 1583975428858L,
+            codegenVersion = "1.0.15",
             sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
-            inputSignatures = "public static final  int SUGGESTION_COUNT_UNLIMITED\nprivate final  int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.Nullable android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate  int mHostDisplayId\npublic  void setHostInputToken(android.os.IBinder)\nprivate  void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic  void setHostDisplayId(int)\nprivate  void onConstructed()\nprivate static  int defaultMaxSuggestionCount()\nprivate static  java.lang.String defaultHostPackageName()\nprivate static  android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.Nullable android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
+            inputSignatures = "public static final  int SUGGESTION_COUNT_UNLIMITED\nprivate final  int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @com.android.internal.util.DataClass.MaySetToNull @android.annotation.Nullable android.os.Bundle mExtras\nprivate @com.android.internal.util.DataClass.MaySetToNull @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate  int mHostDisplayId\npublic  void setHostInputToken(android.os.IBinder)\nprivate  void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic  void setHostDisplayId(int)\nprivate  void onConstructed()\nprivate static  int defaultMaxSuggestionCount()\nprivate static  java.lang.String defaultHostPackageName()\nprivate static  android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.Nullable android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
index 6246b50..842ba29 100644
--- a/core/java/android/view/textclassifier/ConversationActions.java
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -21,15 +21,12 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringDef;
-import android.annotation.UserIdInt;
 import android.app.Person;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.UserHandle;
 import android.text.SpannedString;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
@@ -317,13 +314,9 @@
         @NonNull
         @Hint
         private final List<String> mHints;
-        @Nullable
-        private String mCallingPackageName;
-        @UserIdInt
-        private int mUserId = UserHandle.USER_NULL;
         @NonNull
         private Bundle mExtras;
-        private boolean mUseDefaultTextClassifier;
+        @Nullable private SystemTextClassifierMetadata mSystemTcMetadata;
 
         private Request(
                 @NonNull List<Message> conversation,
@@ -345,10 +338,8 @@
             int maxSuggestions = in.readInt();
             List<String> hints = new ArrayList<>();
             in.readStringList(hints);
-            String callingPackageName = in.readString();
-            int userId = in.readInt();
             Bundle extras = in.readBundle();
-            boolean useDefaultTextClassifier = in.readBoolean();
+            SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null);
 
             Request request = new Request(
                     conversation,
@@ -356,9 +347,7 @@
                     maxSuggestions,
                     hints,
                     extras);
-            request.setCallingPackageName(callingPackageName);
-            request.setUserId(userId);
-            request.setUseDefaultTextClassifier(useDefaultTextClassifier);
+            request.setSystemTextClassifierMetadata(systemTcMetadata);
             return request;
         }
 
@@ -368,10 +357,8 @@
             parcel.writeParcelable(mTypeConfig, flags);
             parcel.writeInt(mMaxSuggestions);
             parcel.writeStringList(mHints);
-            parcel.writeString(mCallingPackageName);
-            parcel.writeInt(mUserId);
             parcel.writeBundle(mExtras);
-            parcel.writeBoolean(mUseDefaultTextClassifier);
+            parcel.writeParcelable(mSystemTcMetadata, flags);
         }
 
         @Override
@@ -421,62 +408,31 @@
         }
 
         /**
-         * Sets the name of the package that is sending this request.
-         * <p>
-         * Package-private for SystemTextClassifier's use.
-         * @hide
-         */
-        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-        public void setCallingPackageName(@Nullable String callingPackageName) {
-            mCallingPackageName = callingPackageName;
-        }
-
-        /**
          * Returns the name of the package that sent this request.
          * This returns {@code null} if no calling package name is set.
          */
         @Nullable
         public String getCallingPackageName() {
-            return mCallingPackageName;
+            return mSystemTcMetadata != null ? mSystemTcMetadata.getCallingPackageName() : null;
         }
 
         /**
-         * Sets the id of the user that sent this request.
-         * <p>
-         * Package-private for SystemTextClassifier's use.
-         * @hide
-         */
-        void setUserId(@UserIdInt int userId) {
-            mUserId = userId;
-        }
-
-        /**
-         * Returns the id of the user that sent this request.
-         * @hide
-         */
-        @UserIdInt
-        public int getUserId() {
-            return mUserId;
-        }
-
-        /**
-         * Sets whether to use the default text classifier to handle this request.
-         * This will be ignored if it is not the system text classifier to handle this request.
+         * Sets the information about the {@link SystemTextClassifier} that sent this request.
          *
          * @hide
          */
-        void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
-            mUseDefaultTextClassifier = useDefaultTextClassifier;
+        void setSystemTextClassifierMetadata(@Nullable SystemTextClassifierMetadata systemTcData) {
+            mSystemTcMetadata = systemTcData;
         }
 
         /**
-         * Returns whether to use the default text classifier to handle this request. This
-         * will be ignored if it is not the system text classifier to handle this request.
+         * Returns the information about the {@link SystemTextClassifier} that sent this request.
          *
          * @hide
          */
-        public boolean getUseDefaultTextClassifier() {
-            return mUseDefaultTextClassifier;
+        @Nullable
+        public SystemTextClassifierMetadata getSystemTextClassifierMetadata() {
+            return mSystemTcMetadata;
         }
 
         /**
diff --git a/core/java/android/view/textclassifier/SelectionEvent.java b/core/java/android/view/textclassifier/SelectionEvent.java
index e0f29a9..9a54544 100644
--- a/core/java/android/view/textclassifier/SelectionEvent.java
+++ b/core/java/android/view/textclassifier/SelectionEvent.java
@@ -19,10 +19,8 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UserIdInt;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.UserHandle;
 import android.view.textclassifier.TextClassifier.EntityType;
 import android.view.textclassifier.TextClassifier.WidgetType;
 
@@ -129,7 +127,6 @@
     private String mWidgetType = TextClassifier.WIDGET_TYPE_UNKNOWN;
     private @InvocationMethod int mInvocationMethod;
     @Nullable private String mWidgetVersion;
-    private @UserIdInt int mUserId = UserHandle.USER_NULL;
     @Nullable private String mResultId;
     private long mEventTime;
     private long mDurationSinceSessionStart;
@@ -140,7 +137,7 @@
     private int mEnd;
     private int mSmartStart;
     private int mSmartEnd;
-    private boolean mUseDefaultTextClassifier;
+    @Nullable private SystemTextClassifierMetadata mSystemTcMetadata;
 
     SelectionEvent(
             int start, int end,
@@ -161,6 +158,7 @@
         mEventType = in.readInt();
         mEntityType = in.readString();
         mWidgetVersion = in.readInt() > 0 ? in.readString() : null;
+        // TODO: remove mPackageName once aiai does not need it
         mPackageName = in.readString();
         mWidgetType = in.readString();
         mInvocationMethod = in.readInt();
@@ -175,8 +173,7 @@
         mEnd = in.readInt();
         mSmartStart = in.readInt();
         mSmartEnd = in.readInt();
-        mUserId = in.readInt();
-        mUseDefaultTextClassifier = in.readBoolean();
+        mSystemTcMetadata = in.readParcelable(null);
     }
 
     @Override
@@ -189,6 +186,7 @@
         if (mWidgetVersion != null) {
             dest.writeString(mWidgetVersion);
         }
+        // TODO: remove mPackageName once aiai does not need it
         dest.writeString(mPackageName);
         dest.writeString(mWidgetType);
         dest.writeInt(mInvocationMethod);
@@ -205,8 +203,7 @@
         dest.writeInt(mEnd);
         dest.writeInt(mSmartStart);
         dest.writeInt(mSmartEnd);
-        dest.writeInt(mUserId);
-        dest.writeBoolean(mUseDefaultTextClassifier);
+        dest.writeParcelable(mSystemTcMetadata, flags);
     }
 
     @Override
@@ -409,45 +406,26 @@
      */
     @NonNull
     public String getPackageName() {
-        return mPackageName;
+        return mSystemTcMetadata != null ? mSystemTcMetadata.getCallingPackageName() : "";
     }
 
     /**
-     * Sets the id of this event's user.
-     * <p>
-     * Package-private for SystemTextClassifier's use.
-     */
-    void setUserId(@UserIdInt int userId) {
-        mUserId = userId;
-    }
-
-    /**
-     * Returns the id of this event's user.
-     * @hide
-     */
-    @UserIdInt
-    public int getUserId() {
-        return mUserId;
-    }
-
-    /**
-     * Sets whether to use the default text classifier to handle this request.
-     * This will be ignored if it is not the system text classifier to handle this request.
+     * Sets the information about the {@link SystemTextClassifier} that sent this request.
      *
      * @hide
      */
-    void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
-        mUseDefaultTextClassifier = useDefaultTextClassifier;
+    void setSystemTextClassifierMetadata(@Nullable SystemTextClassifierMetadata systemTcMetadata) {
+        mSystemTcMetadata = systemTcMetadata;
     }
 
     /**
-     * Returns whether to use the default text classifier to handle this request. This
-     * will be ignored if it is not the system text classifier to handle this request.
+     * Returns the information about the {@link SystemTextClassifier} that sent this request.
      *
      * @hide
      */
-    public boolean getUseDefaultTextClassifier() {
-        return mUseDefaultTextClassifier;
+    @Nullable
+    public SystemTextClassifierMetadata getSystemTextClassifierMetadata() {
+        return mSystemTcMetadata;
     }
 
     /**
@@ -476,7 +454,7 @@
         mPackageName = context.getPackageName();
         mWidgetType = context.getWidgetType();
         mWidgetVersion = context.getWidgetVersion();
-        mUserId = context.getUserId();
+        mSystemTcMetadata = context.getSystemTextClassifierMetadata();
     }
 
     /**
@@ -663,10 +641,9 @@
     @Override
     public int hashCode() {
         return Objects.hash(mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType,
-                mWidgetVersion, mPackageName, mUserId, mWidgetType, mInvocationMethod, mResultId,
+                mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod, mResultId,
                 mEventTime, mDurationSinceSessionStart, mDurationSincePreviousEvent,
-                mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd,
-                mUseDefaultTextClassifier);
+                mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd, mSystemTcMetadata);
     }
 
     @Override
@@ -685,7 +662,6 @@
                 && Objects.equals(mEntityType, other.mEntityType)
                 && Objects.equals(mWidgetVersion, other.mWidgetVersion)
                 && Objects.equals(mPackageName, other.mPackageName)
-                && mUserId == other.mUserId
                 && Objects.equals(mWidgetType, other.mWidgetType)
                 && mInvocationMethod == other.mInvocationMethod
                 && Objects.equals(mResultId, other.mResultId)
@@ -698,7 +674,7 @@
                 && mEnd == other.mEnd
                 && mSmartStart == other.mSmartStart
                 && mSmartEnd == other.mSmartEnd
-                && mUseDefaultTextClassifier == other.mUseDefaultTextClassifier;
+                && mSystemTcMetadata == other.mSystemTcMetadata;
     }
 
     @Override
@@ -706,15 +682,14 @@
         return String.format(Locale.US,
                 "SelectionEvent {absoluteStart=%d, absoluteEnd=%d, eventType=%d, entityType=%s, "
                         + "widgetVersion=%s, packageName=%s, widgetType=%s, invocationMethod=%s, "
-                        + "userId=%d, resultId=%s, eventTime=%d, durationSinceSessionStart=%d, "
+                        + "resultId=%s, eventTime=%d, durationSinceSessionStart=%d, "
                         + "durationSincePreviousEvent=%d, eventIndex=%d,"
                         + "sessionId=%s, start=%d, end=%d, smartStart=%d, smartEnd=%d, "
-                        + "mUseDefaultTextClassifier=%b}",
+                        + "systemTcMetadata=%s}",
                 mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType,
                 mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod,
-                mUserId, mResultId, mEventTime, mDurationSinceSessionStart,
-                mDurationSincePreviousEvent, mEventIndex,
-                mSessionId, mStart, mEnd, mSmartStart, mSmartEnd, mUseDefaultTextClassifier);
+                mResultId, mEventTime, mDurationSinceSessionStart, mDurationSincePreviousEvent,
+                mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd, mSystemTcMetadata);
     }
 
     public static final @android.annotation.NonNull Creator<SelectionEvent> CREATOR = new Creator<SelectionEvent>() {
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index fe5e8d6..86ef4e1 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UserIdInt;
 import android.annotation.WorkerThread;
 import android.content.Context;
 import android.os.Bundle;
@@ -39,7 +38,8 @@
 import java.util.concurrent.TimeUnit;
 
 /**
- * Proxy to the system's default TextClassifier.
+ * proxy to the request to TextClassifierService via the TextClassificationManagerService.
+ *
  * @hide
  */
 @VisibleForTesting(visibility = Visibility.PACKAGE)
@@ -50,14 +50,19 @@
     private final ITextClassifierService mManagerService;
     private final TextClassificationConstants mSettings;
     private final TextClassifier mFallback;
-    private final String mPackageName;
-    // NOTE: Always set this before sending a request to the manager service otherwise the manager
-    // service will throw a remote exception.
-    @UserIdInt
-    private final int mUserId;
-    private final boolean mUseDefault;
     private TextClassificationSessionId mSessionId;
+    // NOTE: Always set this before sending a request to the manager service otherwise the
+    // manager service will throw a remote exception.
+    @NonNull
+    private final SystemTextClassifierMetadata mSystemTcMetadata;
 
+    /**
+     * Constructor of {@link SystemTextClassifier}
+     *
+     * @param context the context of the request.
+     * @param settings TextClassifier specific settings.
+     * @param useDefault whether to use the default text classifier to handle this request
+     */
     public SystemTextClassifier(
             Context context,
             TextClassificationConstants settings,
@@ -66,9 +71,11 @@
                 ServiceManager.getServiceOrThrow(Context.TEXT_CLASSIFICATION_SERVICE));
         mSettings = Objects.requireNonNull(settings);
         mFallback = TextClassifier.NO_OP;
-        mPackageName = Objects.requireNonNull(context.getOpPackageName());
-        mUserId = context.getUserId();
-        mUseDefault = useDefault;
+        // NOTE: Always set this before sending a request to the manager service otherwise the
+        // manager service will throw a remote exception.
+        mSystemTcMetadata = new SystemTextClassifierMetadata(
+                Objects.requireNonNull(context.getOpPackageName()), context.getUserId(),
+                useDefault);
     }
 
     /**
@@ -80,9 +87,7 @@
         Objects.requireNonNull(request);
         Utils.checkMainThread();
         try {
-            request.setCallingPackageName(mPackageName);
-            request.setUserId(mUserId);
-            request.setUseDefaultTextClassifier(mUseDefault);
+            request.setSystemTextClassifierMetadata(mSystemTcMetadata);
             final BlockingCallback<TextSelection> callback =
                     new BlockingCallback<>("textselection");
             mManagerService.onSuggestSelection(mSessionId, request, callback);
@@ -105,9 +110,7 @@
         Objects.requireNonNull(request);
         Utils.checkMainThread();
         try {
-            request.setCallingPackageName(mPackageName);
-            request.setUserId(mUserId);
-            request.setUseDefaultTextClassifier(mUseDefault);
+            request.setSystemTextClassifierMetadata(mSystemTcMetadata);
             final BlockingCallback<TextClassification> callback =
                     new BlockingCallback<>("textclassification");
             mManagerService.onClassifyText(mSessionId, request, callback);
@@ -137,9 +140,7 @@
         }
 
         try {
-            request.setCallingPackageName(mPackageName);
-            request.setUserId(mUserId);
-            request.setUseDefaultTextClassifier(mUseDefault);
+            request.setSystemTextClassifierMetadata(mSystemTcMetadata);
             final BlockingCallback<TextLinks> callback =
                     new BlockingCallback<>("textlinks");
             mManagerService.onGenerateLinks(mSessionId, request, callback);
@@ -159,8 +160,7 @@
         Utils.checkMainThread();
 
         try {
-            event.setUserId(mUserId);
-            event.setUseDefaultTextClassifier(mUseDefault);
+            event.setSystemTextClassifierMetadata(mSystemTcMetadata);
             mManagerService.onSelectionEvent(mSessionId, event);
         } catch (RemoteException e) {
             Log.e(LOG_TAG, "Error reporting selection event.", e);
@@ -173,12 +173,11 @@
         Utils.checkMainThread();
 
         try {
-            final TextClassificationContext tcContext = event.getEventContext() == null
-                    ? new TextClassificationContext.Builder(mPackageName, WIDGET_TYPE_UNKNOWN)
-                            .build()
-                    : event.getEventContext();
-            tcContext.setUserId(mUserId);
-            tcContext.setUseDefaultTextClassifier(mUseDefault);
+            final TextClassificationContext tcContext =
+                    event.getEventContext() == null ? new TextClassificationContext.Builder(
+                            mSystemTcMetadata.getCallingPackageName(), WIDGET_TYPE_UNKNOWN).build()
+                            : event.getEventContext();
+            tcContext.setSystemTextClassifierMetadata(mSystemTcMetadata);
             event.setEventContext(tcContext);
             mManagerService.onTextClassifierEvent(mSessionId, event);
         } catch (RemoteException e) {
@@ -192,9 +191,7 @@
         Utils.checkMainThread();
 
         try {
-            request.setCallingPackageName(mPackageName);
-            request.setUserId(mUserId);
-            request.setUseDefaultTextClassifier(mUseDefault);
+            request.setSystemTextClassifierMetadata(mSystemTcMetadata);
             final BlockingCallback<TextLanguage> callback =
                     new BlockingCallback<>("textlanguage");
             mManagerService.onDetectLanguage(mSessionId, request, callback);
@@ -214,9 +211,7 @@
         Utils.checkMainThread();
 
         try {
-            request.setCallingPackageName(mPackageName);
-            request.setUserId(mUserId);
-            request.setUseDefaultTextClassifier(mUseDefault);
+            request.setSystemTextClassifierMetadata(mSystemTcMetadata);
             final BlockingCallback<ConversationActions> callback =
                     new BlockingCallback<>("conversation-actions");
             mManagerService.onSuggestConversationActions(mSessionId, request, callback);
@@ -256,10 +251,8 @@
         printWriter.println("SystemTextClassifier:");
         printWriter.increaseIndent();
         printWriter.printPair("mFallback", mFallback);
-        printWriter.printPair("mPackageName", mPackageName);
         printWriter.printPair("mSessionId", mSessionId);
-        printWriter.printPair("mUserId", mUserId);
-        printWriter.printPair("mUseDefault",  mUseDefault);
+        printWriter.printPair("mSystemTcMetadata",  mSystemTcMetadata);
         printWriter.decreaseIndent();
         printWriter.println();
     }
@@ -275,7 +268,7 @@
             @NonNull TextClassificationSessionId sessionId) {
         mSessionId = Objects.requireNonNull(sessionId);
         try {
-            classificationContext.setUserId(mUserId);
+            classificationContext.setSystemTextClassifierMetadata(mSystemTcMetadata);
             mManagerService.onCreateTextClassificationSession(classificationContext, mSessionId);
         } catch (RemoteException e) {
             Log.e(LOG_TAG, "Error starting a new classification session.", e);
diff --git a/core/java/android/view/textclassifier/SystemTextClassifierMetadata.aidl b/core/java/android/view/textclassifier/SystemTextClassifierMetadata.aidl
new file mode 100644
index 0000000..4d4e90a
--- /dev/null
+++ b/core/java/android/view/textclassifier/SystemTextClassifierMetadata.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+parcelable SystemTextClassifierMetadata;
\ No newline at end of file
diff --git a/core/java/android/view/textclassifier/SystemTextClassifierMetadata.java b/core/java/android/view/textclassifier/SystemTextClassifierMetadata.java
new file mode 100644
index 0000000..971e3e2
--- /dev/null
+++ b/core/java/android/view/textclassifier/SystemTextClassifierMetadata.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * SystemTextClassifier specific information.
+ * <p>
+ * This contains information requires for the TextClassificationManagerService to process the
+ * requests from the application, e.g. user id, calling package name and etc. Centrialize the data
+ * into this class helps to extend the scalability if we want to add new fields.
+ * @hide
+ */
+@VisibleForTesting(visibility = Visibility.PACKAGE)
+public final class SystemTextClassifierMetadata implements Parcelable {
+
+    /* The name of the package that sent the TC request. */
+    @NonNull
+    private final String mCallingPackageName;
+    /* The id of the user that sent the TC request. */
+    @UserIdInt
+    private final int mUserId;
+    /* Whether to use the default text classifier to handle the request. */
+    private final boolean mUseDefaultTextClassifier;
+
+    public SystemTextClassifierMetadata(@NonNull String packageName, @UserIdInt int userId,
+            boolean useDefaultTextClassifier) {
+        Objects.requireNonNull(packageName);
+        mCallingPackageName = packageName;
+        mUserId = userId;
+        mUseDefaultTextClassifier = useDefaultTextClassifier;
+    }
+
+    /**
+     * Returns the id of the user that sent the TC request.
+     */
+    @UserIdInt
+    public int getUserId() {
+        return mUserId;
+    }
+
+    /**
+     * Returns the name of the package that sent the TC request.
+     * This returns {@code null} if no calling package name is set.
+     */
+    @NonNull
+    public String getCallingPackageName() {
+        return mCallingPackageName;
+    }
+
+    /**
+     * Returns whether to use the default text classifier to handle TC request.
+     */
+    public boolean useDefaultTextClassifier() {
+        return mUseDefaultTextClassifier;
+    }
+
+    @Override
+    public String toString() {
+        return String.format(Locale.US,
+                "SystemTextClassifierMetadata {callingPackageName=%s, userId=%d, "
+                        + "useDefaultTextClassifier=%b}",
+                mCallingPackageName, mUserId, mUseDefaultTextClassifier);
+    }
+
+    private static SystemTextClassifierMetadata readFromParcel(Parcel in) {
+        final String packageName = in.readString();
+        final int userId = in.readInt();
+        final boolean useDefaultTextClassifier = in.readBoolean();
+        return new SystemTextClassifierMetadata(packageName, userId, useDefaultTextClassifier);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mCallingPackageName);
+        dest.writeInt(mUserId);
+        dest.writeBoolean(mUseDefaultTextClassifier);
+    }
+
+    public static final @NonNull Creator<SystemTextClassifierMetadata> CREATOR =
+            new Creator<SystemTextClassifierMetadata>() {
+        @Override
+        public SystemTextClassifierMetadata createFromParcel(Parcel in) {
+            return readFromParcel(in);
+        }
+
+        @Override
+        public SystemTextClassifierMetadata[] newArray(int size) {
+            return new SystemTextClassifierMetadata[size];
+        }
+    };
+}
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index 00f762b..8b9d129 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -21,7 +21,6 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UserIdInt;
 import android.app.PendingIntent;
 import android.app.RemoteAction;
 import android.content.Context;
@@ -36,7 +35,6 @@
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.UserHandle;
 import android.text.SpannedString;
 import android.util.ArrayMap;
 import android.view.View.OnClickListener;
@@ -552,10 +550,7 @@
         @Nullable private final LocaleList mDefaultLocales;
         @Nullable private final ZonedDateTime mReferenceTime;
         @NonNull private final Bundle mExtras;
-        @Nullable private String mCallingPackageName;
-        @UserIdInt
-        private int mUserId = UserHandle.USER_NULL;
-        private boolean mUseDefaultTextClassifier;
+        @Nullable private SystemTextClassifierMetadata mSystemTcMetadata;
 
         private Request(
                 CharSequence text,
@@ -616,62 +611,33 @@
         }
 
         /**
-         * Sets the name of the package that is sending this request.
-         * <p>
-         * For SystemTextClassifier's use.
-         * @hide
-         */
-        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-        public void setCallingPackageName(@Nullable String callingPackageName) {
-            mCallingPackageName = callingPackageName;
-        }
-
-        /**
          * Returns the name of the package that sent this request.
          * This returns {@code null} if no calling package name is set.
          */
         @Nullable
         public String getCallingPackageName() {
-            return mCallingPackageName;
+            return mSystemTcMetadata != null ? mSystemTcMetadata.getCallingPackageName() : null;
         }
 
         /**
-         * Sets the id of the user that sent this request.
-         * <p>
-         * Package-private for SystemTextClassifier's use.
-         * @hide
-         */
-        void setUserId(@UserIdInt int userId) {
-            mUserId = userId;
-        }
-
-        /**
-         * Returns the id of the user that sent this request.
-         * @hide
-         */
-        @UserIdInt
-        public int getUserId() {
-            return mUserId;
-        }
-
-        /**
-         * Sets whether to use the default text classifier to handle this request.
-         * This will be ignored if it is not the system text classifier to handle this request.
+         * Sets the information about the {@link SystemTextClassifier} that sent this request.
          *
          * @hide
          */
-        void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
-            mUseDefaultTextClassifier = useDefaultTextClassifier;
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+        public void setSystemTextClassifierMetadata(
+                @Nullable SystemTextClassifierMetadata systemTcMetadata) {
+            mSystemTcMetadata = systemTcMetadata;
         }
 
         /**
-         * Returns whether to use the default text classifier to handle this request. This
-         * will be ignored if it is not the system text classifier to handle this request.
+         * Returns the information about the {@link SystemTextClassifier} that sent this request.
          *
          * @hide
          */
-        public boolean getUseDefaultTextClassifier() {
-            return mUseDefaultTextClassifier;
+        @Nullable
+        public SystemTextClassifierMetadata getSystemTextClassifierMetadata() {
+            return mSystemTcMetadata;
         }
 
         /**
@@ -773,10 +739,8 @@
             dest.writeInt(mEndIndex);
             dest.writeParcelable(mDefaultLocales, flags);
             dest.writeString(mReferenceTime == null ? null : mReferenceTime.toString());
-            dest.writeString(mCallingPackageName);
-            dest.writeInt(mUserId);
             dest.writeBundle(mExtras);
-            dest.writeBoolean(mUseDefaultTextClassifier);
+            dest.writeParcelable(mSystemTcMetadata, flags);
         }
 
         private static Request readFromParcel(Parcel in) {
@@ -787,16 +751,12 @@
             final String referenceTimeString = in.readString();
             final ZonedDateTime referenceTime = referenceTimeString == null
                     ? null : ZonedDateTime.parse(referenceTimeString);
-            final String callingPackageName = in.readString();
-            final int userId = in.readInt();
             final Bundle extras = in.readBundle();
-            final boolean useDefaultTextClassifier = in.readBoolean();
+            final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null);
 
             final Request request = new Request(text, startIndex, endIndex,
                     defaultLocales, referenceTime, extras);
-            request.setCallingPackageName(callingPackageName);
-            request.setUserId(userId);
-            request.setUseDefaultTextClassifier(useDefaultTextClassifier);
+            request.setSystemTextClassifierMetadata(systemTcMetadata);
             return request;
         }
 
diff --git a/core/java/android/view/textclassifier/TextClassificationContext.java b/core/java/android/view/textclassifier/TextClassificationContext.java
index d58d175..f2323c6 100644
--- a/core/java/android/view/textclassifier/TextClassificationContext.java
+++ b/core/java/android/view/textclassifier/TextClassificationContext.java
@@ -18,10 +18,8 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UserIdInt;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.UserHandle;
 import android.view.textclassifier.TextClassifier.WidgetType;
 
 import java.util.Locale;
@@ -33,12 +31,11 @@
  */
 public final class TextClassificationContext implements Parcelable {
 
-    private final String mPackageName;
+    // NOTE: Modify packageName only in the constructor or in setSystemTextClassifierMetadata()
+    private String mPackageName;
     private final String mWidgetType;
     @Nullable private final String mWidgetVersion;
-    @UserIdInt
-    private int mUserId = UserHandle.USER_NULL;
-    private boolean mUseDefaultTextClassifier;
+    private SystemTextClassifierMetadata mSystemTcMetadata;
 
     private TextClassificationContext(
             String packageName,
@@ -58,42 +55,26 @@
     }
 
     /**
-     * Sets the id of this context's user.
-     * <p>
-     * Package-private for SystemTextClassifier's use.
+     * Sets the information about the {@link SystemTextClassifier} that sent this request.
+     *
+     * <p><b>NOTE: </b>This will override the value returned in {@link getPackageName()}.
      * @hide
      */
-    void setUserId(@UserIdInt int userId) {
-        mUserId = userId;
+    void setSystemTextClassifierMetadata(@Nullable SystemTextClassifierMetadata systemTcMetadata) {
+        mSystemTcMetadata = systemTcMetadata;
+        if (mSystemTcMetadata != null) {
+            mPackageName = mSystemTcMetadata.getCallingPackageName();
+        }
     }
 
     /**
-     * Returns the id of this context's user.
-     * @hide
-     */
-    @UserIdInt
-    public int getUserId() {
-        return mUserId;
-    }
-
-    /**
-     * Sets whether to use the default text classifier to handle this request.
-     * This will be ignored if it is not the system text classifier to handle this request.
+     * Returns the information about the {@link SystemTextClassifier} that sent this request.
      *
      * @hide
      */
-    void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
-        mUseDefaultTextClassifier = useDefaultTextClassifier;
-    }
-
-    /**
-     * Returns whether to use the default text classifier to handle this request. This
-     * will be ignored if it is not the system text classifier to handle this request.
-     *
-     * @hide
-     */
-    public boolean getUseDefaultTextClassifier() {
-        return mUseDefaultTextClassifier;
+    @Nullable
+    public SystemTextClassifierMetadata getSystemTextClassifierMetadata() {
+        return mSystemTcMetadata;
     }
 
     /**
@@ -118,8 +99,8 @@
     @Override
     public String toString() {
         return String.format(Locale.US, "TextClassificationContext{"
-                + "packageName=%s, widgetType=%s, widgetVersion=%s, userId=%d}",
-                mPackageName, mWidgetType, mWidgetVersion, mUserId);
+                + "packageName=%s, widgetType=%s, widgetVersion=%s, systemTcMetadata=%s}",
+                mPackageName, mWidgetType, mWidgetVersion, mSystemTcMetadata);
     }
 
     /**
@@ -176,16 +157,14 @@
         parcel.writeString(mPackageName);
         parcel.writeString(mWidgetType);
         parcel.writeString(mWidgetVersion);
-        parcel.writeInt(mUserId);
-        parcel.writeBoolean(mUseDefaultTextClassifier);
+        parcel.writeParcelable(mSystemTcMetadata, flags);
     }
 
     private TextClassificationContext(Parcel in) {
         mPackageName = in.readString();
         mWidgetType = in.readString();
         mWidgetVersion = in.readString();
-        mUserId = in.readInt();
-        mUseDefaultTextClassifier = in.readBoolean();
+        mSystemTcMetadata = in.readParcelable(null);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<TextClassificationContext> CREATOR =
diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java
index 58024dc..1e8253d 100644
--- a/core/java/android/view/textclassifier/TextLanguage.java
+++ b/core/java/android/view/textclassifier/TextLanguage.java
@@ -20,12 +20,10 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UserIdInt;
 import android.icu.util.ULocale;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.UserHandle;
 import android.util.ArrayMap;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -227,10 +225,7 @@
 
         private final CharSequence mText;
         private final Bundle mExtra;
-        @Nullable private String mCallingPackageName;
-        @UserIdInt
-        private int mUserId = UserHandle.USER_NULL;
-        private boolean mUseDefaultTextClassifier;
+        @Nullable private SystemTextClassifierMetadata mSystemTcMetadata;
 
         private Request(CharSequence text, Bundle bundle) {
             mText = text;
@@ -246,61 +241,33 @@
         }
 
         /**
-         * Sets the name of the package that is sending this request.
-         * Package-private for SystemTextClassifier's use.
-         * @hide
-         */
-        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-        public void setCallingPackageName(@Nullable String callingPackageName) {
-            mCallingPackageName = callingPackageName;
-        }
-
-        /**
          * Returns the name of the package that sent this request.
          * This returns null if no calling package name is set.
          */
         @Nullable
         public String getCallingPackageName() {
-            return mCallingPackageName;
+            return mSystemTcMetadata != null ? mSystemTcMetadata.getCallingPackageName() : null;
         }
 
         /**
-         * Sets the id of the user that sent this request.
-         * <p>
-         * Package-private for SystemTextClassifier's use.
-         * @hide
-         */
-        void setUserId(@UserIdInt int userId) {
-            mUserId = userId;
-        }
-
-        /**
-         * Returns the id of the user that sent this request.
-         * @hide
-         */
-        @UserIdInt
-        public int getUserId() {
-            return mUserId;
-        }
-
-        /**
-         * Sets whether to use the default text classifier to handle this request.
-         * This will be ignored if it is not the system text classifier to handle this request.
+         * Sets the information about the {@link SystemTextClassifier} that sent this request.
          *
          * @hide
          */
-        void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
-            mUseDefaultTextClassifier = useDefaultTextClassifier;
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+        public void setSystemTextClassifierMetadata(
+                @Nullable SystemTextClassifierMetadata systemTcMetadata) {
+            mSystemTcMetadata = systemTcMetadata;
         }
 
         /**
-         * Returns whether to use the default text classifier to handle this request. This
-         * will be ignored if it is not the system text classifier to handle this request.
+         * Returns the information about the {@link SystemTextClassifier} that sent this request.
          *
          * @hide
          */
-        public boolean getUseDefaultTextClassifier() {
-            return mUseDefaultTextClassifier;
+        @Nullable
+        public SystemTextClassifierMetadata getSystemTextClassifierMetadata() {
+            return mSystemTcMetadata;
         }
 
         /**
@@ -321,23 +288,17 @@
         @Override
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeCharSequence(mText);
-            dest.writeString(mCallingPackageName);
-            dest.writeInt(mUserId);
             dest.writeBundle(mExtra);
-            dest.writeBoolean(mUseDefaultTextClassifier);
+            dest.writeParcelable(mSystemTcMetadata, flags);
         }
 
         private static Request readFromParcel(Parcel in) {
             final CharSequence text = in.readCharSequence();
-            final String callingPackageName = in.readString();
-            final int userId = in.readInt();
             final Bundle extra = in.readBundle();
-            final boolean useDefaultTextClassifier = in.readBoolean();
+            final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null);
 
             final Request request = new Request(text, extra);
-            request.setCallingPackageName(callingPackageName);
-            request.setUserId(userId);
-            request.setUseDefaultTextClassifier(useDefaultTextClassifier);
+            request.setSystemTextClassifierMetadata(systemTcMetadata);
             return request;
         }
 
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index 7430cb3..dea3a90 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -20,13 +20,11 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UserIdInt;
 import android.content.Context;
 import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.UserHandle;
 import android.text.Spannable;
 import android.text.method.MovementMethod;
 import android.text.style.ClickableSpan;
@@ -340,12 +338,9 @@
         @Nullable private final LocaleList mDefaultLocales;
         @Nullable private final EntityConfig mEntityConfig;
         private final boolean mLegacyFallback;
-        @Nullable private String mCallingPackageName;
         private final Bundle mExtras;
         @Nullable private final ZonedDateTime mReferenceTime;
-        @UserIdInt
-        private int mUserId = UserHandle.USER_NULL;
-        private boolean mUseDefaultTextClassifier;
+        @Nullable private SystemTextClassifierMetadata mSystemTcMetadata;
 
         private Request(
                 CharSequence text,
@@ -409,62 +404,33 @@
         }
 
         /**
-         * Sets the name of the package that is sending this request.
-         * <p>
-         * Package-private for SystemTextClassifier's use.
-         * @hide
-         */
-        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-        public void setCallingPackageName(@Nullable String callingPackageName) {
-            mCallingPackageName = callingPackageName;
-        }
-
-        /**
          * Returns the name of the package that sent this request.
          * This returns {@code null} if no calling package name is set.
          */
         @Nullable
         public String getCallingPackageName() {
-            return mCallingPackageName;
+            return mSystemTcMetadata != null ? mSystemTcMetadata.getCallingPackageName() : null;
         }
 
         /**
-         * Sets the id of the user that sent this request.
-         * <p>
-         * Package-private for SystemTextClassifier's use.
-         * @hide
-         */
-        void setUserId(@UserIdInt int userId) {
-            mUserId = userId;
-        }
-
-        /**
-         * Returns the id of the user that sent this request.
-         * @hide
-         */
-        @UserIdInt
-        public int getUserId() {
-            return mUserId;
-        }
-
-        /**
-         * Sets whether to use the default text classifier to handle this request.
-         * This will be ignored if it is not the system text classifier to handle this request.
+         * Sets the information about the {@link SystemTextClassifier} that sent this request.
          *
          * @hide
          */
-        void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
-            mUseDefaultTextClassifier = useDefaultTextClassifier;
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+        public void setSystemTextClassifierMetadata(
+                @Nullable SystemTextClassifierMetadata systemTcMetadata) {
+            mSystemTcMetadata = systemTcMetadata;
         }
 
         /**
-         * Returns whether to use the default text classifier to handle this request. This
-         * will be ignored if it is not the system text classifier to handle this request.
+         * Returns the information about the {@link SystemTextClassifier} that sent this request.
          *
          * @hide
          */
-        public boolean getUseDefaultTextClassifier() {
-            return mUseDefaultTextClassifier;
+        @Nullable
+        public SystemTextClassifierMetadata getSystemTextClassifierMetadata() {
+            return mSystemTcMetadata;
         }
 
         /**
@@ -585,30 +551,24 @@
             dest.writeString(mText.toString());
             dest.writeParcelable(mDefaultLocales, flags);
             dest.writeParcelable(mEntityConfig, flags);
-            dest.writeString(mCallingPackageName);
-            dest.writeInt(mUserId);
             dest.writeBundle(mExtras);
             dest.writeString(mReferenceTime == null ? null : mReferenceTime.toString());
-            dest.writeBoolean(mUseDefaultTextClassifier);
+            dest.writeParcelable(mSystemTcMetadata, flags);
         }
 
         private static Request readFromParcel(Parcel in) {
             final String text = in.readString();
             final LocaleList defaultLocales = in.readParcelable(null);
             final EntityConfig entityConfig = in.readParcelable(null);
-            final String callingPackageName = in.readString();
-            final int userId = in.readInt();
             final Bundle extras = in.readBundle();
             final String referenceTimeString = in.readString();
             final ZonedDateTime referenceTime = referenceTimeString == null
                     ? null : ZonedDateTime.parse(referenceTimeString);
-            final boolean useDefaultTextClassifier = in.readBoolean();
+            final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null);
 
             final Request request = new Request(text, defaultLocales, entityConfig,
                     /* legacyFallback= */ true, referenceTime, extras);
-            request.setCallingPackageName(callingPackageName);
-            request.setUserId(userId);
-            request.setUseDefaultTextClassifier(useDefaultTextClassifier);
+            request.setSystemTextClassifierMetadata(systemTcMetadata);
             return request;
         }
 
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index 575a072..d8a632d 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -20,12 +20,10 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UserIdInt;
 import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.UserHandle;
 import android.text.SpannedString;
 import android.util.ArrayMap;
 import android.view.textclassifier.TextClassifier.EntityType;
@@ -213,10 +211,7 @@
         @Nullable private final LocaleList mDefaultLocales;
         private final boolean mDarkLaunchAllowed;
         private final Bundle mExtras;
-        @Nullable private String mCallingPackageName;
-        @UserIdInt
-        private int mUserId = UserHandle.USER_NULL;
-        private boolean mUseDefaultTextClassifier;
+        @Nullable private SystemTextClassifierMetadata mSystemTcMetadata;
 
         private Request(
                 CharSequence text,
@@ -278,62 +273,33 @@
         }
 
         /**
-         * Sets the name of the package that is sending this request.
-         * <p>
-         * Package-private for SystemTextClassifier's use.
-         * @hide
-         */
-        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-        public void setCallingPackageName(@Nullable String callingPackageName) {
-            mCallingPackageName = callingPackageName;
-        }
-
-        /**
          * Returns the name of the package that sent this request.
          * This returns {@code null} if no calling package name is set.
          */
         @Nullable
         public String getCallingPackageName() {
-            return mCallingPackageName;
+            return mSystemTcMetadata != null ? mSystemTcMetadata.getCallingPackageName() : null;
         }
 
         /**
-         * Sets the id of the user that sent this request.
-         * <p>
-         * Package-private for SystemTextClassifier's use.
-         * @hide
-         */
-        void setUserId(@UserIdInt int userId) {
-            mUserId = userId;
-        }
-
-        /**
-         * Returns the id of the user that sent this request.
-         * @hide
-         */
-        @UserIdInt
-        public int getUserId() {
-            return mUserId;
-        }
-
-        /**
-         * Sets whether to use the default text classifier to handle this request.
-         * This will be ignored if it is not the system text classifier to handle this request.
+         * Sets the information about the {@link SystemTextClassifier} that sent this request.
          *
          * @hide
          */
-        void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
-            mUseDefaultTextClassifier = useDefaultTextClassifier;
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+        public void setSystemTextClassifierMetadata(
+                @Nullable SystemTextClassifierMetadata systemTcMetadata) {
+            mSystemTcMetadata = systemTcMetadata;
         }
 
         /**
-         * Returns whether to use the default text classifier to handle this request. This
-         * will be ignored if it is not the system text classifier to handle this request.
+         * Returns the information about the {@link SystemTextClassifier} that sent this request.
          *
          * @hide
          */
-        public boolean getUseDefaultTextClassifier() {
-            return mUseDefaultTextClassifier;
+        @Nullable
+        public SystemTextClassifierMetadata getSystemTextClassifierMetadata() {
+            return mSystemTcMetadata;
         }
 
         /**
@@ -438,10 +404,8 @@
             dest.writeInt(mStartIndex);
             dest.writeInt(mEndIndex);
             dest.writeParcelable(mDefaultLocales, flags);
-            dest.writeString(mCallingPackageName);
-            dest.writeInt(mUserId);
             dest.writeBundle(mExtras);
-            dest.writeBoolean(mUseDefaultTextClassifier);
+            dest.writeParcelable(mSystemTcMetadata, flags);
         }
 
         private static Request readFromParcel(Parcel in) {
@@ -449,16 +413,12 @@
             final int startIndex = in.readInt();
             final int endIndex = in.readInt();
             final LocaleList defaultLocales = in.readParcelable(null);
-            final String callingPackageName = in.readString();
-            final int userId = in.readInt();
             final Bundle extras = in.readBundle();
-            final boolean systemTextClassifierType = in.readBoolean();
+            final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null);
 
             final Request request = new Request(text, startIndex, endIndex, defaultLocales,
                     /* darkLaunchAllowed= */ false, extras);
-            request.setCallingPackageName(callingPackageName);
-            request.setUserId(userId);
-            request.setUseDefaultTextClassifier(systemTextClassifierType);
+            request.setSystemTextClassifierMetadata(systemTcMetadata);
             return request;
         }
 
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index d165bd0..ffdb89d 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -261,7 +261,7 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {
-                String tz = intent.getStringExtra("time-zone");
+                String tz = intent.getStringExtra(Intent.EXTRA_TIMEZONE);
                 mClock = Clock.system(ZoneId.of(tz));
             }
 
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index 8565493..6432438 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -173,7 +173,7 @@
                 return; // Test disabled the clock ticks
             }
             if (mTimeZone == null && Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
-                final String timeZone = intent.getStringExtra("time-zone");
+                final String timeZone = intent.getStringExtra(Intent.EXTRA_TIMEZONE);
                 createTime(timeZone);
             } else if (!mShouldRunTicker && (Intent.ACTION_TIME_TICK.equals(intent.getAction())
                     || Intent.ACTION_TIME_CHANGED.equals(intent.getAction()))) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0d82065..2168018 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6607,6 +6607,16 @@
         return mTransformation instanceof PasswordTransformationMethod;
     }
 
+    /**
+     * Returns true if the current inputType is any type of password.
+     *
+     * @hide
+     */
+    public boolean isAnyPasswordInputType() {
+        final int inputType = getInputType();
+        return isPasswordInputType(inputType) || isVisiblePasswordInputType(inputType);
+    }
+
     static boolean isPasswordInputType(int inputType) {
         final int variation =
                 inputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION);
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index d50826f..fa567f2 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -452,7 +452,7 @@
             mEmptyStateView = rootView.findViewById(R.id.resolver_empty_state);
         }
 
-        private ViewGroup getEmptyStateView() {
+        protected ViewGroup getEmptyStateView() {
             return mEmptyStateView;
         }
     }
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index c487e96..5620bff 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2398,17 +2398,22 @@
         }
 
         final int availableWidth = right - left - v.getPaddingLeft() - v.getPaddingRight();
-        if (mChooserMultiProfilePagerAdapter.getCurrentUserHandle() != getUser()) {
-            gridAdapter.calculateChooserTargetWidth(availableWidth);
-            return;
-        }
-
-        if (gridAdapter.consumeLayoutRequest()
+        boolean isLayoutUpdated = gridAdapter.consumeLayoutRequest()
                 || gridAdapter.calculateChooserTargetWidth(availableWidth)
                 || recyclerView.getAdapter() == null
-                || mLastNumberOfChildren != recyclerView.getChildCount()
-                || availableWidth != mCurrAvailableWidth) {
+                || availableWidth != mCurrAvailableWidth;
+        if (isLayoutUpdated
+                || mLastNumberOfChildren != recyclerView.getChildCount()) {
             mCurrAvailableWidth = availableWidth;
+            if (isLayoutUpdated
+                    && mChooserMultiProfilePagerAdapter.getCurrentUserHandle() != getUser()) {
+                // This fixes b/150936654 - empty work tab in share sheet when swiping
+                mChooserMultiProfilePagerAdapter.getActiveAdapterView()
+                        .setAdapter(mChooserMultiProfilePagerAdapter.getCurrentRootAdapter());
+                return;
+            } else if (mChooserMultiProfilePagerAdapter.getCurrentUserHandle() != getUser()) {
+                return;
+            }
 
             getMainThreadHandler().post(() -> {
                 if (mResolverDrawerLayout == null || gridAdapter == null) {
@@ -2452,39 +2457,46 @@
                     offset += tabDivider.getHeight();
                 }
 
-                int directShareHeight = 0;
-                rowsToShow = Math.min(4, rowsToShow);
-                mLastNumberOfChildren = recyclerView.getChildCount();
-                for (int i = 0, childCount = recyclerView.getChildCount();
-                        i < childCount && rowsToShow > 0; i++) {
-                    View child = recyclerView.getChildAt(i);
-                    if (((GridLayoutManager.LayoutParams)
-                            child.getLayoutParams()).getSpanIndex() != 0) {
-                        continue;
+                if (recyclerView.getVisibility() == View.VISIBLE) {
+                    int directShareHeight = 0;
+                    rowsToShow = Math.min(4, rowsToShow);
+                    mLastNumberOfChildren = recyclerView.getChildCount();
+                    for (int i = 0, childCount = recyclerView.getChildCount();
+                            i < childCount && rowsToShow > 0; i++) {
+                        View child = recyclerView.getChildAt(i);
+                        if (((GridLayoutManager.LayoutParams)
+                                child.getLayoutParams()).getSpanIndex() != 0) {
+                            continue;
+                        }
+                        int height = child.getHeight();
+                        offset += height;
+
+                        if (gridAdapter.getTargetType(
+                                recyclerView.getChildAdapterPosition(child))
+                                == ChooserListAdapter.TARGET_SERVICE) {
+                            directShareHeight = height;
+                        }
+                        rowsToShow--;
                     }
-                    int height = child.getHeight();
-                    offset += height;
 
-                    if (gridAdapter.getTargetType(
-                            recyclerView.getChildAdapterPosition(child))
-                            == ChooserListAdapter.TARGET_SERVICE) {
-                        directShareHeight = height;
+                    boolean isExpandable = getResources().getConfiguration().orientation
+                            == Configuration.ORIENTATION_PORTRAIT && !isInMultiWindowMode();
+                    if (directShareHeight != 0 && isSendAction(getTargetIntent())
+                            && isExpandable) {
+                        // make sure to leave room for direct share 4->8 expansion
+                        int requiredExpansionHeight =
+                                (int) (directShareHeight / DIRECT_SHARE_EXPANSION_RATE);
+                        int topInset = mSystemWindowInsets != null ? mSystemWindowInsets.top : 0;
+                        int minHeight = bottom - top - mResolverDrawerLayout.getAlwaysShowHeight()
+                                - requiredExpansionHeight - topInset - bottomInset;
+
+                        offset = Math.min(offset, minHeight);
                     }
-                    rowsToShow--;
-                }
-
-                boolean isExpandable = getResources().getConfiguration().orientation
-                        == Configuration.ORIENTATION_PORTRAIT && !isInMultiWindowMode();
-                if (directShareHeight != 0 && isSendAction(getTargetIntent())
-                        && isExpandable) {
-                    // make sure to leave room for direct share 4->8 expansion
-                    int requiredExpansionHeight =
-                            (int) (directShareHeight / DIRECT_SHARE_EXPANSION_RATE);
-                    int topInset = mSystemWindowInsets != null ? mSystemWindowInsets.top : 0;
-                    int minHeight = bottom - top - mResolverDrawerLayout.getAlwaysShowHeight()
-                                        - requiredExpansionHeight - topInset - bottomInset;
-
-                    offset = Math.min(offset, minHeight);
+                } else {
+                    ViewGroup currentEmptyStateView = getCurrentEmptyStateView();
+                    if (currentEmptyStateView.getVisibility() == View.VISIBLE) {
+                        offset += currentEmptyStateView.getHeight();
+                    }
                 }
 
                 mResolverDrawerLayout.setCollapsibleHeightReserved(Math.min(offset, bottom - top));
@@ -2492,6 +2504,11 @@
         }
     }
 
+    private ViewGroup getCurrentEmptyStateView() {
+        int currentPage = mChooserMultiProfilePagerAdapter.getCurrentPage();
+        return mChooserMultiProfilePagerAdapter.getItem(currentPage).getEmptyStateView();
+    }
+
     static class BaseChooserTargetComparator implements Comparator<ChooserTarget> {
         @Override
         public int compare(ChooserTarget lhs, ChooserTarget rhs) {
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 907ea55..9218823 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -34,14 +34,14 @@
     // be kept in sync with frameworks/native/libs/binder/include/binder/IAppOpsService.h
     // and not be reordered
     int checkOperation(int code, int uid, String packageName);
-    int noteOperation(int code, int uid, String packageName, @nullable String featureId,
+    int noteOperation(int code, int uid, String packageName, @nullable String attributionTag,
             boolean shouldCollectAsyncNotedOp, String message);
     int startOperation(IBinder clientId, int code, int uid, String packageName,
-            @nullable String featureId, boolean startIfModeDefault,
+            @nullable String attributionTag, boolean startIfModeDefault,
             boolean shouldCollectAsyncNotedOp, String message);
     @UnsupportedAppUsage
     void finishOperation(IBinder clientId, int code, int uid, String packageName,
-            @nullable String featureId);
+            @nullable String attributionTag);
     void startWatchingMode(int op, String packageName, IAppOpsCallback callback);
     void stopWatchingMode(IAppOpsCallback callback);
     int permissionToOpCode(String permission);
@@ -52,8 +52,8 @@
     // Any new method exposed to native must be added after the last one, do not reorder
 
     int noteProxyOperation(int code, int proxiedUid, String proxiedPackageName,
-            String proxiedFeatureId, int proxyUid, String proxyPackageName,
-            String proxyFeatureId, boolean shouldCollectAsyncNotedOp, String message);
+            String proxiedAttributionTag, int proxyUid, String proxyPackageName,
+            String proxyAttributionTag, boolean shouldCollectAsyncNotedOp, String message);
 
     // Remaining methods are only used in Java.
     int checkPackage(int uid, String packageName);
@@ -64,10 +64,10 @@
     List<AppOpsManager.PackageOps> getPackagesForOps(in int[] ops);
     @UnsupportedAppUsage
     List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops);
-    void getHistoricalOps(int uid, String packageName, String featureId, in List<String> ops,
+    void getHistoricalOps(int uid, String packageName, String attributionTag, in List<String> ops,
             int filter, long beginTimeMillis, long endTimeMillis, int flags,
             in RemoteCallback callback);
-    void getHistoricalOpsFromDiskRaw(int uid, String packageName, String featureId,
+    void getHistoricalOpsFromDiskRaw(int uid, String packageName, String attributionTag,
             in List<String> ops, int filter, long beginTimeMillis, long endTimeMillis, int flags,
             in RemoteCallback callback);
     void offsetHistory(long duration);
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index 4c203d3..523ed6f 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -164,6 +164,30 @@
     boolean clearOverride(long changeId, String packageName);
 
     /**
+     * Enable all compatibility changes which have enabledAfterTargetSdk ==
+     * {@param targetSdkVersion} for an app, subject to the policy. Kills the app to allow the
+     * changes to take effect.
+     *
+     * @param packageName The package name of the app whose compatibility changes will be enabled.
+     * @param targetSdkVersion The targetSdkVersion for filtering the changes to be enabled.
+     *
+     * @return The number of changes that were enabled.
+     */
+    int enableTargetSdkChanges(in String packageName, int targetSdkVersion);
+
+    /**
+     * Disable all compatibility changes which have enabledAfterTargetSdk ==
+     * {@param targetSdkVersion} for an app, subject to the policy. Kills the app to allow the
+     * changes to take effect.
+     *
+     * @param packageName The package name of the app whose compatibility changes will be disabled.
+     * @param targetSdkVersion The targetSdkVersion for filtering the changes to be disabled.
+     *
+     * @return The number of changes that were disabled.
+     */
+    int disableTargetSdkChanges(in String packageName, int targetSdkVersion);
+
+    /**
      * Revert overrides to compatibility changes. Kills the app to allow the changes to take effect.
      *
      * @param packageName The package name of the app whose overrides will be cleared.
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index 73ef8c6..2f048c9 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -430,7 +430,7 @@
             if (shouldHide(file)) continue;
 
             if (file.isDirectory()) {
-                for (File child : file.listFiles()) {
+                for (File child : FileUtils.listFilesOrEmpty(file)) {
                     pending.add(child);
                 }
             }
diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java
index 91ef0b5..1296ddc 100644
--- a/core/java/com/android/internal/notification/SystemNotificationChannels.java
+++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java
@@ -116,6 +116,7 @@
                 NETWORK_STATUS,
                 context.getString(R.string.notification_channel_network_status),
                 NotificationManager.IMPORTANCE_LOW);
+        network.setBlockableSystem(true);
         channelsList.add(network);
 
         final NotificationChannel networkAlertsChannel = new NotificationChannel(
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 94924a5..633aa2c 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -140,6 +140,33 @@
      */
     public static final int MEMORY_TAG_LEVEL_SYNC = 3 << 19;
 
+    /**
+     * A two-bit field for GWP-ASan level of this process. See the possible values below.
+     */
+    public static final int GWP_ASAN_LEVEL_MASK = (1 << 21) | (1 << 22);
+
+    /**
+     * Disable GWP-ASan in this process.
+     * GWP-ASan is a low-overhead memory bug detector using guard pages on a small
+     * subset of heap allocations.
+     */
+    public static final int GWP_ASAN_LEVEL_NEVER = 0 << 21;
+
+    /**
+     * Enable GWP-ASan in this process with a small sampling rate.
+     * With approx. 1% chance GWP-ASan will be activated and apply its protection
+     * to a small subset of heap allocations.
+     * Otherwise (~99% chance) this process is unaffected.
+     */
+    public static final int GWP_ASAN_LEVEL_LOTTERY = 1 << 21;
+
+    /**
+     * Always enable GWP-ASan in this process.
+     * GWP-ASan is activated unconditionally (but still, only a small subset of
+     * allocations is protected).
+     */
+    public static final int GWP_ASAN_LEVEL_ALWAYS = 2 << 21;
+
     /** No external storage should be mounted. */
     public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
     /** Default external storage should be mounted. */
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 54cf693..0bfd659 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -125,12 +125,6 @@
 
     private static boolean sPreloadComplete;
 
-    /**
-     * Cached classloader to use for the system server. Will only be populated in the system
-     * server process.
-     */
-    private static ClassLoader sCachedSystemServerClassLoader = null;
-
     static void preload(TimingsTraceLog bootTimingsTraceLog) {
         Log.d(TAG, "begin preload");
         bootTimingsTraceLog.traceBegin("BeginPreload");
@@ -508,13 +502,7 @@
 
         final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
         if (systemServerClasspath != null) {
-            if (performSystemServerDexOpt(systemServerClasspath)) {
-                // Throw away the cached classloader. If we compiled here, the classloader would
-                // not have had AoT-ed artifacts.
-                // Note: This only works in a very special environment where selinux enforcement is
-                // disabled, e.g., Mac builds.
-                sCachedSystemServerClassLoader = null;
-            }
+            performSystemServerDexOpt(systemServerClasspath);
             // Capturing profiles is only supported for debug or eng builds since selinux normally
             // prevents it.
             if (shouldProfileSystemServer() && (Build.IS_USERDEBUG || Build.IS_ENG)) {
@@ -546,9 +534,10 @@
 
             throw new IllegalStateException("Unexpected return from WrapperInit.execApplication");
         } else {
-            createSystemServerClassLoader();
-            ClassLoader cl = sCachedSystemServerClassLoader;
-            if (cl != null) {
+            ClassLoader cl = null;
+            if (systemServerClasspath != null) {
+                cl = createPathClassLoader(systemServerClasspath, parsedArgs.mTargetSdkVersion);
+
                 Thread.currentThread().setContextClassLoader(cl);
             }
 
@@ -564,24 +553,6 @@
     }
 
     /**
-     * Create the classloader for the system server and store it in
-     * {@link sCachedSystemServerClassLoader}. This function may be called through JNI in
-     * system server startup, when the runtime is in a critically low state. Do not do
-     * extended computation etc here.
-     */
-    private static void createSystemServerClassLoader() {
-        if (sCachedSystemServerClassLoader != null) {
-            return;
-        }
-        final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
-        // TODO: Should we run optimization here?
-        if (systemServerClasspath != null) {
-            sCachedSystemServerClassLoader = createPathClassLoader(systemServerClasspath,
-                    VMRuntime.SDK_VERSION_CUR_DEVELOPMENT);
-        }
-    }
-
-    /**
      * Note that preparing the profiles for system server does not require special selinux
      * permissions. From the installer perspective the system server is a regular package which can
      * capture profile information.
@@ -645,16 +616,15 @@
 
     /**
      * Performs dex-opt on the elements of {@code classPath}, if needed. We choose the instruction
-     * set of the current runtime. If something was compiled, return true.
+     * set of the current runtime.
      */
-    private static boolean performSystemServerDexOpt(String classPath) {
+    private static void performSystemServerDexOpt(String classPath) {
         final String[] classPathElements = classPath.split(":");
         final IInstalld installd = IInstalld.Stub
                 .asInterface(ServiceManager.getService("installd"));
         final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();
 
         String classPathForElement = "";
-        boolean compiledSomething = false;
         for (String classPathElement : classPathElements) {
             // We default to the verify filter because the compilation will happen on /data and
             // system server cannot load executable code outside /system.
@@ -695,7 +665,6 @@
                             uuid, classLoaderContext, seInfo, false /* downgrade */,
                             targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null,
                             "server-dexopt");
-                    compiledSomething = true;
                 } catch (RemoteException | ServiceSpecificException e) {
                     // Ignore (but log), we need this on the classpath for fallback mode.
                     Log.w(TAG, "Failed compiling classpath element for system server: "
@@ -706,8 +675,6 @@
             classPathForElement = encodeSystemServerClassPath(
                     classPathForElement, classPathElement);
         }
-
-        return compiledSomething;
     }
 
     /**
diff --git a/core/java/com/android/internal/util/Parcelling.java b/core/java/com/android/internal/util/Parcelling.java
index 7f567b9..dd64c40 100644
--- a/core/java/com/android/internal/util/Parcelling.java
+++ b/core/java/com/android/internal/util/Parcelling.java
@@ -221,6 +221,33 @@
             }
         }
 
+        class ForInternedStringArraySet implements Parcelling<ArraySet<String>> {
+            @Override
+            public void parcel(ArraySet<String> item, Parcel dest, int parcelFlags) {
+                if (item == null) {
+                    dest.writeInt(-1);
+                } else {
+                    dest.writeInt(item.size());
+                    for (String string : item) {
+                        dest.writeString(string);
+                    }
+                }
+            }
+
+            @Override
+            public ArraySet<String> unparcel(Parcel source) {
+                final int size = source.readInt();
+                if (size < 0) {
+                  return null;
+                }
+                ArraySet<String> set = new ArraySet<>();
+                for (int count = 0; count < size; count++) {
+                    set.add(TextUtils.safeIntern(source.readString()));
+                }
+                return set;
+            }
+        }
+
         class ForBoolean implements Parcelling<Boolean> {
             @Override
             public void parcel(@Nullable Boolean item, Parcel dest, int parcelFlags) {
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 5dd3389b..47f094f 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -116,7 +116,8 @@
     }
 
     @Override
-    public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, boolean sync) {
+    public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, float zoom,
+            boolean sync) {
         if (sync) {
             try {
                 mSession.wallpaperOffsetsComplete(asBinder());
diff --git a/core/java/com/android/internal/view/inline/IInlineContentCallback.aidl b/core/java/com/android/internal/view/inline/IInlineContentCallback.aidl
index 29bdf56..feb3f02 100644
--- a/core/java/com/android/internal/view/inline/IInlineContentCallback.aidl
+++ b/core/java/com/android/internal/view/inline/IInlineContentCallback.aidl
@@ -24,4 +24,6 @@
  */
 oneway interface IInlineContentCallback {
     void onContent(in SurfaceControlViewHost.SurfacePackage content);
+    void onClick();
+    void onLongClick();
 }
diff --git a/core/java/com/android/internal/widget/CachingIconView.java b/core/java/com/android/internal/widget/CachingIconView.java
index 74ad815..bd0623e 100644
--- a/core/java/com/android/internal/widget/CachingIconView.java
+++ b/core/java/com/android/internal/widget/CachingIconView.java
@@ -32,6 +32,7 @@
 import android.widget.RemoteViews;
 
 import java.util.Objects;
+import java.util.function.Consumer;
 
 /**
  * An ImageView for displaying an Icon. Avoids reloading the Icon when possible.
@@ -44,6 +45,7 @@
     private boolean mInternalSetDrawable;
     private boolean mForceHidden;
     private int mDesiredVisibility;
+    private Consumer<Integer> mOnVisibilityChangedListener;
 
     @UnsupportedAppUsage
     public CachingIconView(Context context, @Nullable AttributeSet attrs) {
@@ -198,6 +200,13 @@
     private void updateVisibility() {
         int visibility = mDesiredVisibility == VISIBLE && mForceHidden ? INVISIBLE
                 : mDesiredVisibility;
+        if (mOnVisibilityChangedListener != null) {
+            mOnVisibilityChangedListener.accept(visibility);
+        }
         super.setVisibility(visibility);
     }
+
+    public void setOnVisibilityChangedListener(Consumer<Integer> listener) {
+        mOnVisibilityChangedListener = listener;
+    }
 }
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
new file mode 100644
index 0000000..07be113
--- /dev/null
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -0,0 +1,866 @@
+/*
+ * 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.internal.widget;
+
+import android.annotation.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StyleRes;
+import android.app.Notification;
+import android.app.Person;
+import android.app.RemoteInputHistoryItem;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.RemotableViewMethod;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.RemoteViews;
+import android.widget.TextView;
+
+import com.android.internal.R;
+import com.android.internal.graphics.ColorUtils;
+import com.android.internal.util.ContrastColorUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.regex.Pattern;
+
+/**
+ * A custom-built layout for the Notification.MessagingStyle allows dynamic addition and removal
+ * messages and adapts the layout accordingly.
+ */
+@RemoteViews.RemoteView
+public class ConversationLayout extends FrameLayout
+        implements ImageMessageConsumer, IMessagingLayout {
+
+    public static final boolean CONVERSATION_LAYOUT_ENABLED = true;
+    private static final float COLOR_SHIFT_AMOUNT = 60;
+    /**
+     *  Pattren for filter some ingonable characters.
+     *  p{Z} for any kind of whitespace or invisible separator.
+     *  p{C} for any kind of punctuation character.
+     */
+    private static final Pattern IGNORABLE_CHAR_PATTERN
+            = Pattern.compile("[\\p{C}\\p{Z}]");
+    private static final Pattern SPECIAL_CHAR_PATTERN
+            = Pattern.compile ("[!@#$%&*()_+=|<>?{}\\[\\]~-]");
+    private static final Consumer<MessagingMessage> REMOVE_MESSAGE
+            = MessagingMessage::removeMessage;
+    public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
+    public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
+    public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+    public static final OnLayoutChangeListener MESSAGING_PROPERTY_ANIMATOR
+            = new MessagingPropertyAnimator();
+    private List<MessagingMessage> mMessages = new ArrayList<>();
+    private List<MessagingMessage> mHistoricMessages = new ArrayList<>();
+    private MessagingLinearLayout mMessagingLinearLayout;
+    private boolean mShowHistoricMessages;
+    private ArrayList<MessagingGroup> mGroups = new ArrayList<>();
+    private TextView mTitleView;
+    private int mLayoutColor;
+    private int mSenderTextColor;
+    private int mMessageTextColor;
+    private int mAvatarSize;
+    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+    private Paint mTextPaint = new Paint();
+    private Icon mAvatarReplacement;
+    private boolean mIsOneToOne;
+    private ArrayList<MessagingGroup> mAddedGroups = new ArrayList<>();
+    private Person mUser;
+    private CharSequence mNameReplacement;
+    private boolean mIsCollapsed;
+    private ImageResolver mImageResolver;
+    private ImageView mConversationIcon;
+    private TextView mConversationText;
+    private View mConversationIconBadge;
+    private Icon mLargeIcon;
+    private View mExpandButtonContainer;
+    private ViewGroup mExpandButtonAndContentContainer;
+    private NotificationExpandButton mExpandButton;
+    private int mExpandButtonExpandedTopMargin;
+    private int mBadgedSideMargins;
+    private int mIconSizeBadged;
+    private int mIconSizeCentered;
+    private CachingIconView mIcon;
+    private int mExpandedGroupTopMargin;
+    private int mExpandButtonExpandedSize;
+    private View mConversationFacePile;
+    private int mNotificationBackgroundColor;
+    private CharSequence mFallbackChatName;
+    private CharSequence mFallbackGroupChatName;
+    private CharSequence mConversationTitle;
+
+    public ConversationLayout(@NonNull Context context) {
+        super(context);
+    }
+
+    public ConversationLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public ConversationLayout(@NonNull Context context, @Nullable AttributeSet attrs,
+            @AttrRes int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public ConversationLayout(@NonNull Context context, @Nullable AttributeSet attrs,
+            @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mMessagingLinearLayout = findViewById(R.id.notification_messaging);
+        mMessagingLinearLayout.setMessagingLayout(this);
+        // We still want to clip, but only on the top, since views can temporarily out of bounds
+        // during transitions.
+        DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+        int size = Math.max(displayMetrics.widthPixels, displayMetrics.heightPixels);
+        Rect rect = new Rect(0, 0, size, size);
+        mMessagingLinearLayout.setClipBounds(rect);
+        mTitleView = findViewById(R.id.title);
+        mAvatarSize = getResources().getDimensionPixelSize(R.dimen.messaging_avatar_size);
+        mTextPaint.setTextAlign(Paint.Align.CENTER);
+        mTextPaint.setAntiAlias(true);
+        mConversationIcon = findViewById(R.id.conversation_icon);
+        mIcon = findViewById(R.id.icon);
+        mConversationIconBadge = findViewById(R.id.conversation_icon_badge);
+        mIcon.setOnVisibilityChangedListener((visibility) -> {
+            // Always keep the badge visibility in sync with the icon. This is necessary in cases
+            // Where the icon is being hidden externally like in group children.
+            mConversationIconBadge.setVisibility(visibility);
+        });
+        mConversationText = findViewById(R.id.conversation_text);
+        mExpandButtonContainer = findViewById(R.id.expand_button_container);
+        mExpandButtonAndContentContainer = findViewById(R.id.expand_button_and_content_container);
+        mExpandButton = findViewById(R.id.expand_button);
+        mExpandButtonExpandedTopMargin = getResources().getDimensionPixelSize(
+                R.dimen.conversation_expand_button_top_margin_expanded);
+        mExpandButtonExpandedSize = getResources().getDimensionPixelSize(
+                R.dimen.conversation_expand_button_expanded_size);
+        mBadgedSideMargins = getResources().getDimensionPixelSize(
+                R.dimen.conversation_badge_side_margin);
+        mIconSizeBadged = getResources().getDimensionPixelSize(
+                R.dimen.conversation_icon_size_badged);
+        mIconSizeCentered = getResources().getDimensionPixelSize(
+                R.dimen.conversation_icon_size_centered);
+        mExpandedGroupTopMargin = getResources().getDimensionPixelSize(
+                R.dimen.conversation_icon_margin_top_centered);
+        mConversationFacePile = findViewById(R.id.conversation_face_pile);
+        mFallbackChatName = getResources().getString(
+                R.string.conversation_title_fallback_one_to_one);
+        mFallbackGroupChatName = getResources().getString(
+                R.string.conversation_title_fallback_group_chat);
+    }
+
+    @RemotableViewMethod
+    public void setAvatarReplacement(Icon icon) {
+        mAvatarReplacement = icon;
+    }
+
+    @RemotableViewMethod
+    public void setNameReplacement(CharSequence nameReplacement) {
+        mNameReplacement = nameReplacement;
+    }
+
+    /**
+     * Set this layout to show the collapsed representation.
+     *
+     * @param isCollapsed is it collapsed
+     */
+    @RemotableViewMethod
+    public void setIsCollapsed(boolean isCollapsed) {
+        mIsCollapsed = isCollapsed;
+        mMessagingLinearLayout.setMaxDisplayedLines(isCollapsed ? 1 : Integer.MAX_VALUE);
+        updateExpandButton();
+    }
+
+    @RemotableViewMethod
+    public void setData(Bundle extras) {
+        Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES);
+        List<Notification.MessagingStyle.Message> newMessages
+                = Notification.MessagingStyle.Message.getMessagesFromBundleArray(messages);
+        Parcelable[] histMessages = extras.getParcelableArray(Notification.EXTRA_HISTORIC_MESSAGES);
+        List<Notification.MessagingStyle.Message> newHistoricMessages
+                = Notification.MessagingStyle.Message.getMessagesFromBundleArray(histMessages);
+
+        // mUser now set (would be nice to avoid the side effect but WHATEVER)
+        setUser(extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON));
+
+
+        // Append remote input history to newMessages (again, side effect is lame but WHATEVS)
+        RemoteInputHistoryItem[] history = (RemoteInputHistoryItem[])
+                extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
+        addRemoteInputHistoryToMessages(newMessages, history);
+
+        boolean showSpinner =
+                extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false);
+
+        // bind it, baby
+        bind(newMessages, newHistoricMessages, showSpinner);
+    }
+
+    @Override
+    public void setImageResolver(ImageResolver resolver) {
+        mImageResolver = resolver;
+    }
+
+    private void addRemoteInputHistoryToMessages(
+            List<Notification.MessagingStyle.Message> newMessages,
+            RemoteInputHistoryItem[] remoteInputHistory) {
+        if (remoteInputHistory == null || remoteInputHistory.length == 0) {
+            return;
+        }
+        for (int i = remoteInputHistory.length - 1; i >= 0; i--) {
+            RemoteInputHistoryItem historyMessage = remoteInputHistory[i];
+            Notification.MessagingStyle.Message message = new Notification.MessagingStyle.Message(
+                    historyMessage.getText(), 0, (Person) null, true /* remoteHistory */);
+            if (historyMessage.getUri() != null) {
+                message.setData(historyMessage.getMimeType(), historyMessage.getUri());
+            }
+            newMessages.add(message);
+        }
+    }
+
+    private void bind(List<Notification.MessagingStyle.Message> newMessages,
+            List<Notification.MessagingStyle.Message> newHistoricMessages,
+            boolean showSpinner) {
+        // convert MessagingStyle.Message to MessagingMessage, re-using ones from a previous binding
+        // if they exist
+        List<MessagingMessage> historicMessages = createMessages(newHistoricMessages,
+                true /* isHistoric */);
+        List<MessagingMessage> messages = createMessages(newMessages, false /* isHistoric */);
+
+        // Copy our groups, before they get clobbered
+        ArrayList<MessagingGroup> oldGroups = new ArrayList<>(mGroups);
+
+        // Add our new MessagingMessages to groups
+        List<List<MessagingMessage>> groups = new ArrayList<>();
+        List<Person> senders = new ArrayList<>();
+
+        // Lets first find the groups (populate `groups` and `senders`)
+        findGroups(historicMessages, messages, groups, senders);
+
+        // Let's now create the views and reorder them accordingly
+        //   side-effect: updates mGroups, mAddedGroups
+        createGroupViews(groups, senders, showSpinner);
+
+        // Let's first check which groups were removed altogether and remove them in one animation
+        removeGroups(oldGroups);
+
+        // Let's remove the remaining messages
+        mMessages.forEach(REMOVE_MESSAGE);
+        mHistoricMessages.forEach(REMOVE_MESSAGE);
+
+        mMessages = messages;
+        mHistoricMessages = historicMessages;
+
+        updateHistoricMessageVisibility();
+        updateTitleAndNamesDisplay();
+
+        updateConversationLayout();
+
+    }
+
+    /**
+     * Update the layout according to the data provided (i.e mIsOneToOne, expanded etc);
+     */
+    private void updateConversationLayout() {
+        // TODO: resolve this from shortcuts
+        // Set avatar and name
+        CharSequence conversationText = mConversationTitle;
+        // TODO: display the secondary text somewhere
+        if (mIsOneToOne) {
+            // Let's resolve the icon / text from the last sender
+            mConversationIcon.setVisibility(VISIBLE);
+            mConversationFacePile.setVisibility(GONE);
+            CharSequence userKey = getKey(mUser);
+            for (int i = mGroups.size() - 1; i >= 0; i--) {
+                MessagingGroup messagingGroup = mGroups.get(i);
+                Person messageSender = messagingGroup.getSender();
+                if ((messageSender != null && !TextUtils.equals(userKey, getKey(messageSender)))
+                        || i == 0) {
+                    if (TextUtils.isEmpty(conversationText)) {
+                        // We use the sendername as header text if no conversation title is provided
+                        // (This usually happens for most 1:1 conversations)
+                        conversationText = messagingGroup.getSenderName();
+                    }
+                    Icon avatarIcon = messagingGroup.getAvatarIcon();
+                    if (avatarIcon == null) {
+                        avatarIcon = createAvatarSymbol(conversationText, "", mLayoutColor);
+                    }
+                    mConversationIcon.setImageIcon(avatarIcon);
+                    break;
+                }
+            }
+        } else {
+            if (mIsCollapsed) {
+                if (mLargeIcon != null) {
+                    mConversationIcon.setVisibility(VISIBLE);
+                    mConversationFacePile.setVisibility(GONE);
+                    mConversationIcon.setImageIcon(mLargeIcon);
+                } else {
+                    mConversationIcon.setVisibility(GONE);
+                    // This will also inflate it!
+                    mConversationFacePile.setVisibility(VISIBLE);
+                    mConversationFacePile = findViewById(R.id.conversation_face_pile);
+                    bindFacePile();
+                }
+            } else {
+                mConversationFacePile.setVisibility(GONE);
+                mConversationIcon.setVisibility(GONE);
+            }
+        }
+        if (TextUtils.isEmpty(conversationText)) {
+            conversationText = mIsOneToOne ? mFallbackChatName : mFallbackGroupChatName;
+        }
+        mConversationText.setText(conversationText);
+        // Update if the groups can hide the sender if they are first (applies to 1:1 conversations)
+        // This needs to happen after all of the above o update all of the groups
+        for (int i = mGroups.size() - 1; i >= 0; i--) {
+            MessagingGroup messagingGroup = mGroups.get(i);
+            CharSequence messageSender = messagingGroup.getSenderName();
+            boolean canHide = mIsOneToOne
+                    && TextUtils.equals(conversationText, messageSender);
+            messagingGroup.setCanHideSenderIfFirst(canHide);
+        }
+        updateIconPositionAndSize();
+    }
+
+    private void bindFacePile() {
+        // Let's bind the face pile
+        View bottomBackground = mConversationFacePile.findViewById(
+                R.id.conversation_face_pile_bottom_background);
+        applyNotificationBackgroundColor(bottomBackground);
+        ImageView bottomView = mConversationFacePile.findViewById(
+                R.id.conversation_face_pile_bottom);
+        ImageView topView = mConversationFacePile.findViewById(
+                R.id.conversation_face_pile_top);
+        // Let's find the two last conversations:
+        Icon secondLastIcon = null;
+        CharSequence lastKey = null;
+        Icon lastIcon = null;
+        CharSequence userKey = getKey(mUser);
+        for (int i = mGroups.size() - 1; i >= 0; i--) {
+            MessagingGroup messagingGroup = mGroups.get(i);
+            Person messageSender = messagingGroup.getSender();
+            boolean notUser = messageSender != null
+                    && !TextUtils.equals(userKey, getKey(messageSender));
+            boolean notIncluded = messageSender != null
+                    && !TextUtils.equals(lastKey, getKey(messageSender));
+            if ((notUser && notIncluded)
+                    || (i == 0 && lastKey == null)) {
+                if (lastIcon == null) {
+                    lastIcon = messagingGroup.getAvatarIcon();
+                    lastKey = getKey(messageSender);
+                } else {
+                    secondLastIcon = messagingGroup.getAvatarIcon();
+                    break;
+                }
+            }
+        }
+        if (lastIcon == null) {
+            lastIcon = createAvatarSymbol(" ", "", mLayoutColor);
+        }
+        bottomView.setImageIcon(lastIcon);
+        if (secondLastIcon == null) {
+            secondLastIcon = createAvatarSymbol("", "", mLayoutColor);
+        }
+        topView.setImageIcon(secondLastIcon);
+    }
+
+    /**
+     * update the icon position and sizing
+     */
+    private void updateIconPositionAndSize() {
+        int gravity;
+        int marginStart;
+        int marginTop;
+        int iconSize;
+        if (mIsOneToOne || mIsCollapsed) {
+            // Baded format
+            gravity = Gravity.LEFT;
+            marginStart = mBadgedSideMargins;
+            marginTop = mBadgedSideMargins;
+            iconSize = mIconSizeBadged;
+        } else {
+            gravity = Gravity.CENTER_HORIZONTAL;
+            marginStart = 0;
+            marginTop = mExpandedGroupTopMargin;
+            iconSize = mIconSizeCentered;
+        }
+        LayoutParams layoutParams =
+                (LayoutParams) mConversationIconBadge.getLayoutParams();
+        layoutParams.gravity = gravity;
+        layoutParams.topMargin = marginTop;
+        layoutParams.setMarginStart(marginStart);
+        mConversationIconBadge.setLayoutParams(layoutParams);
+        ViewGroup.LayoutParams iconParams = mIcon.getLayoutParams();
+        iconParams.width = iconSize;
+        iconParams.height = iconSize;
+        mIcon.setLayoutParams(iconParams);
+    }
+
+    @RemotableViewMethod
+    public void setLargeIcon(Icon largeIcon) {
+        mLargeIcon = largeIcon;
+    }
+
+    /**
+     * Sets the conversation title of this conversation.
+     *
+     * @param conversationTitle the conversation title
+     */
+    @RemotableViewMethod
+    public void setConversationTitle(CharSequence conversationTitle) {
+        mConversationTitle = conversationTitle;
+    }
+
+    private void removeGroups(ArrayList<MessagingGroup> oldGroups) {
+        int size = oldGroups.size();
+        for (int i = 0; i < size; i++) {
+            MessagingGroup group = oldGroups.get(i);
+            if (!mGroups.contains(group)) {
+                List<MessagingMessage> messages = group.getMessages();
+                Runnable endRunnable = () -> {
+                    mMessagingLinearLayout.removeTransientView(group);
+                    group.recycle();
+                };
+
+                boolean wasShown = group.isShown();
+                mMessagingLinearLayout.removeView(group);
+                if (wasShown && !MessagingLinearLayout.isGone(group)) {
+                    mMessagingLinearLayout.addTransientView(group, 0);
+                    group.removeGroupAnimated(endRunnable);
+                } else {
+                    endRunnable.run();
+                }
+                mMessages.removeAll(messages);
+                mHistoricMessages.removeAll(messages);
+            }
+        }
+    }
+
+    private void updateTitleAndNamesDisplay() {
+        ArrayMap<CharSequence, String> uniqueNames = new ArrayMap<>();
+        ArrayMap<Character, CharSequence> uniqueCharacters = new ArrayMap<>();
+        for (int i = 0; i < mGroups.size(); i++) {
+            MessagingGroup group = mGroups.get(i);
+            CharSequence senderName = group.getSenderName();
+            if (!group.needsGeneratedAvatar() || TextUtils.isEmpty(senderName)) {
+                continue;
+            }
+            if (!uniqueNames.containsKey(senderName)) {
+                // Only use visible characters to get uniqueNames
+                String pureSenderName = IGNORABLE_CHAR_PATTERN
+                        .matcher(senderName).replaceAll("" /* replacement */);
+                char c = pureSenderName.charAt(0);
+                if (uniqueCharacters.containsKey(c)) {
+                    // this character was already used, lets make it more unique. We first need to
+                    // resolve the existing character if it exists
+                    CharSequence existingName = uniqueCharacters.get(c);
+                    if (existingName != null) {
+                        uniqueNames.put(existingName, findNameSplit((String) existingName));
+                        uniqueCharacters.put(c, null);
+                    }
+                    uniqueNames.put(senderName, findNameSplit((String) senderName));
+                } else {
+                    uniqueNames.put(senderName, Character.toString(c));
+                    uniqueCharacters.put(c, pureSenderName);
+                }
+            }
+        }
+
+        // Now that we have the correct symbols, let's look what we have cached
+        ArrayMap<CharSequence, Icon> cachedAvatars = new ArrayMap<>();
+        for (int i = 0; i < mGroups.size(); i++) {
+            // Let's now set the avatars
+            MessagingGroup group = mGroups.get(i);
+            boolean isOwnMessage = group.getSender() == mUser;
+            CharSequence senderName = group.getSenderName();
+            if (!group.needsGeneratedAvatar() || TextUtils.isEmpty(senderName)
+                    || (mIsOneToOne && mAvatarReplacement != null && !isOwnMessage)) {
+                continue;
+            }
+            String symbol = uniqueNames.get(senderName);
+            Icon cachedIcon = group.getAvatarSymbolIfMatching(senderName,
+                    symbol, mLayoutColor);
+            if (cachedIcon != null) {
+                cachedAvatars.put(senderName, cachedIcon);
+            }
+        }
+
+        for (int i = 0; i < mGroups.size(); i++) {
+            // Let's now set the avatars
+            MessagingGroup group = mGroups.get(i);
+            CharSequence senderName = group.getSenderName();
+            if (!group.needsGeneratedAvatar() || TextUtils.isEmpty(senderName)) {
+                continue;
+            }
+            if (mIsOneToOne && mAvatarReplacement != null && group.getSender() != mUser) {
+                group.setAvatar(mAvatarReplacement);
+            } else {
+                Icon cachedIcon = cachedAvatars.get(senderName);
+                if (cachedIcon == null) {
+                    cachedIcon = createAvatarSymbol(senderName, uniqueNames.get(senderName),
+                            mLayoutColor);
+                    cachedAvatars.put(senderName, cachedIcon);
+                }
+                group.setCreatedAvatar(cachedIcon, senderName, uniqueNames.get(senderName),
+                        mLayoutColor);
+            }
+        }
+    }
+
+    private Icon createAvatarSymbol(CharSequence senderName, String symbol, int layoutColor) {
+        if (symbol.isEmpty() || TextUtils.isDigitsOnly(symbol) ||
+                SPECIAL_CHAR_PATTERN.matcher(symbol).find()) {
+            Icon avatarIcon = Icon.createWithResource(getContext(),
+                    R.drawable.messaging_user);
+            avatarIcon.setTint(findColor(senderName, layoutColor));
+            return avatarIcon;
+        } else {
+            Bitmap bitmap = Bitmap.createBitmap(mAvatarSize, mAvatarSize, Bitmap.Config.ARGB_8888);
+            Canvas canvas = new Canvas(bitmap);
+            float radius = mAvatarSize / 2.0f;
+            int color = findColor(senderName, layoutColor);
+            mPaint.setColor(color);
+            canvas.drawCircle(radius, radius, radius, mPaint);
+            boolean needDarkText = ColorUtils.calculateLuminance(color) > 0.5f;
+            mTextPaint.setColor(needDarkText ? Color.BLACK : Color.WHITE);
+            mTextPaint.setTextSize(symbol.length() == 1 ? mAvatarSize * 0.5f : mAvatarSize * 0.3f);
+            int yPos = (int) (radius - ((mTextPaint.descent() + mTextPaint.ascent()) / 2));
+            canvas.drawText(symbol, radius, yPos, mTextPaint);
+            return Icon.createWithBitmap(bitmap);
+        }
+    }
+
+    private int findColor(CharSequence senderName, int layoutColor) {
+        double luminance = ContrastColorUtil.calculateLuminance(layoutColor);
+        float shift = Math.abs(senderName.hashCode()) % 5 / 4.0f - 0.5f;
+
+        // we need to offset the range if the luminance is too close to the borders
+        shift += Math.max(COLOR_SHIFT_AMOUNT / 2.0f / 100 - luminance, 0);
+        shift -= Math.max(COLOR_SHIFT_AMOUNT / 2.0f / 100 - (1.0f - luminance), 0);
+        return ContrastColorUtil.getShiftedColor(layoutColor,
+                (int) (shift * COLOR_SHIFT_AMOUNT));
+    }
+
+    private String findNameSplit(String existingName) {
+        String[] split = existingName.split(" ");
+        if (split.length > 1) {
+            return Character.toString(split[0].charAt(0))
+                    + Character.toString(split[1].charAt(0));
+        }
+        return existingName.substring(0, 1);
+    }
+
+    @RemotableViewMethod
+    public void setLayoutColor(int color) {
+        mLayoutColor = color;
+    }
+
+    @RemotableViewMethod
+    public void setIsOneToOne(boolean oneToOne) {
+        mIsOneToOne = oneToOne;
+    }
+
+    @RemotableViewMethod
+    public void setSenderTextColor(int color) {
+        mSenderTextColor = color;
+    }
+
+    /**
+     * @param color the color of the notification background
+     */
+    @RemotableViewMethod
+    public void setNotificationBackgroundColor(int color) {
+        mNotificationBackgroundColor = color;
+        applyNotificationBackgroundColor(mConversationIconBadge);
+    }
+
+    private void applyNotificationBackgroundColor(View view) {
+        view.setBackgroundTintList(ColorStateList.valueOf(mNotificationBackgroundColor));
+    }
+
+    @RemotableViewMethod
+    public void setMessageTextColor(int color) {
+        mMessageTextColor = color;
+    }
+
+    private void setUser(Person user) {
+        mUser = user;
+        if (mUser.getIcon() == null) {
+            Icon userIcon = Icon.createWithResource(getContext(),
+                    R.drawable.messaging_user);
+            userIcon.setTint(mLayoutColor);
+            mUser = mUser.toBuilder().setIcon(userIcon).build();
+        }
+    }
+
+    private void createGroupViews(List<List<MessagingMessage>> groups,
+            List<Person> senders, boolean showSpinner) {
+        mGroups.clear();
+        for (int groupIndex = 0; groupIndex < groups.size(); groupIndex++) {
+            List<MessagingMessage> group = groups.get(groupIndex);
+            MessagingGroup newGroup = null;
+            // we'll just take the first group that exists or create one there is none
+            for (int messageIndex = group.size() - 1; messageIndex >= 0; messageIndex--) {
+                MessagingMessage message = group.get(messageIndex);
+                newGroup = message.getGroup();
+                if (newGroup != null) {
+                    break;
+                }
+            }
+            // Create a new group, adding it to the linear layout as well
+            if (newGroup == null) {
+                newGroup = MessagingGroup.createGroup(mMessagingLinearLayout);
+                mAddedGroups.add(newGroup);
+            }
+            newGroup.setDisplayImagesAtEnd(mIsCollapsed);
+            newGroup.setLayoutColor(mLayoutColor);
+            newGroup.setTextColors(mSenderTextColor, mMessageTextColor);
+            Person sender = senders.get(groupIndex);
+            CharSequence nameOverride = null;
+            if (sender != mUser && mNameReplacement != null) {
+                nameOverride = mNameReplacement;
+            }
+            newGroup.setShowingAvatar(!mIsOneToOne && !mIsCollapsed);
+            newGroup.setSingleLine(mIsCollapsed);
+            newGroup.setSender(sender, nameOverride);
+            newGroup.setSending(groupIndex == (groups.size() - 1) && showSpinner);
+            mGroups.add(newGroup);
+
+            // Reposition to the correct place (if we're re-using a group)
+            if (mMessagingLinearLayout.indexOfChild(newGroup) != groupIndex) {
+                mMessagingLinearLayout.removeView(newGroup);
+                mMessagingLinearLayout.addView(newGroup, groupIndex);
+            }
+            newGroup.setMessages(group);
+        }
+    }
+
+    private void findGroups(List<MessagingMessage> historicMessages,
+            List<MessagingMessage> messages, List<List<MessagingMessage>> groups,
+            List<Person> senders) {
+        CharSequence currentSenderKey = null;
+        List<MessagingMessage> currentGroup = null;
+        int histSize = historicMessages.size();
+        for (int i = 0; i < histSize + messages.size(); i++) {
+            MessagingMessage message;
+            if (i < histSize) {
+                message = historicMessages.get(i);
+            } else {
+                message = messages.get(i - histSize);
+            }
+            boolean isNewGroup = currentGroup == null;
+            Person sender = message.getMessage().getSenderPerson();
+            CharSequence key = getKey(sender);
+            isNewGroup |= !TextUtils.equals(key, currentSenderKey);
+            if (isNewGroup) {
+                currentGroup = new ArrayList<>();
+                groups.add(currentGroup);
+                if (sender == null) {
+                    sender = mUser;
+                }
+                senders.add(sender);
+                currentSenderKey = key;
+            }
+            currentGroup.add(message);
+        }
+    }
+
+    private CharSequence getKey(Person person) {
+        return person == null ? null : person.getKey() == null ? person.getName() : person.getKey();
+    }
+
+    /**
+     * Creates new messages, reusing existing ones if they are available.
+     *
+     * @param newMessages the messages to parse.
+     */
+    private List<MessagingMessage> createMessages(
+            List<Notification.MessagingStyle.Message> newMessages, boolean historic) {
+        List<MessagingMessage> result = new ArrayList<>();
+        for (int i = 0; i < newMessages.size(); i++) {
+            Notification.MessagingStyle.Message m = newMessages.get(i);
+            MessagingMessage message = findAndRemoveMatchingMessage(m);
+            if (message == null) {
+                message = MessagingMessage.createMessage(this, m, mImageResolver);
+            }
+            message.setIsHistoric(historic);
+            result.add(message);
+        }
+        return result;
+    }
+
+    private MessagingMessage findAndRemoveMatchingMessage(Notification.MessagingStyle.Message m) {
+        for (int i = 0; i < mMessages.size(); i++) {
+            MessagingMessage existing = mMessages.get(i);
+            if (existing.sameAs(m)) {
+                mMessages.remove(i);
+                return existing;
+            }
+        }
+        for (int i = 0; i < mHistoricMessages.size(); i++) {
+            MessagingMessage existing = mHistoricMessages.get(i);
+            if (existing.sameAs(m)) {
+                mHistoricMessages.remove(i);
+                return existing;
+            }
+        }
+        return null;
+    }
+
+    public void showHistoricMessages(boolean show) {
+        mShowHistoricMessages = show;
+        updateHistoricMessageVisibility();
+    }
+
+    private void updateHistoricMessageVisibility() {
+        int numHistoric = mHistoricMessages.size();
+        for (int i = 0; i < numHistoric; i++) {
+            MessagingMessage existing = mHistoricMessages.get(i);
+            existing.setVisibility(mShowHistoricMessages ? VISIBLE : GONE);
+        }
+        int numGroups = mGroups.size();
+        for (int i = 0; i < numGroups; i++) {
+            MessagingGroup group = mGroups.get(i);
+            int visibleChildren = 0;
+            List<MessagingMessage> messages = group.getMessages();
+            int numGroupMessages = messages.size();
+            for (int j = 0; j < numGroupMessages; j++) {
+                MessagingMessage message = messages.get(j);
+                if (message.getVisibility() != GONE) {
+                    visibleChildren++;
+                }
+            }
+            if (visibleChildren > 0 && group.getVisibility() == GONE) {
+                group.setVisibility(VISIBLE);
+            } else if (visibleChildren == 0 && group.getVisibility() != GONE)   {
+                group.setVisibility(GONE);
+            }
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        if (!mAddedGroups.isEmpty()) {
+            getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+                @Override
+                public boolean onPreDraw() {
+                    for (MessagingGroup group : mAddedGroups) {
+                        if (!group.isShown()) {
+                            continue;
+                        }
+                        MessagingPropertyAnimator.fadeIn(group.getAvatar());
+                        MessagingPropertyAnimator.fadeIn(group.getSenderView());
+                        MessagingPropertyAnimator.startLocalTranslationFrom(group,
+                                group.getHeight(), LINEAR_OUT_SLOW_IN);
+                    }
+                    mAddedGroups.clear();
+                    getViewTreeObserver().removeOnPreDrawListener(this);
+                    return true;
+                }
+            });
+        }
+    }
+
+    public MessagingLinearLayout getMessagingLinearLayout() {
+        return mMessagingLinearLayout;
+    }
+
+    public ArrayList<MessagingGroup> getMessagingGroups() {
+        return mGroups;
+    }
+
+    private void updateExpandButton() {
+        int drawableId;
+        int contentDescriptionId;
+        int gravity;
+        int topMargin = 0;
+        ViewGroup newContainer;
+        int newContainerHeight;
+        if (mIsCollapsed) {
+            drawableId = R.drawable.ic_expand_notification;
+            contentDescriptionId = R.string.expand_button_content_description_collapsed;
+            gravity = Gravity.CENTER;
+            newContainer = mExpandButtonAndContentContainer;
+            newContainerHeight = LayoutParams.MATCH_PARENT;
+        } else {
+            drawableId = R.drawable.ic_collapse_notification;
+            contentDescriptionId = R.string.expand_button_content_description_expanded;
+            gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
+            topMargin = mExpandButtonExpandedTopMargin;
+            newContainer = this;
+            newContainerHeight = mExpandButtonExpandedSize;
+        }
+        mExpandButton.setImageDrawable(getContext().getDrawable(drawableId));
+        mExpandButton.setColorFilter(mExpandButton.getOriginalNotificationColor());
+
+        // We need to make sure that the expand button is in the linearlayout pushing over the
+        // content when collapsed, but allows the content to flow under it when expanded.
+        if (newContainer != mExpandButtonContainer.getParent()) {
+            ((ViewGroup) mExpandButtonContainer.getParent()).removeView(mExpandButtonContainer);
+            newContainer.addView(mExpandButtonContainer);
+            MarginLayoutParams layoutParams =
+                    (MarginLayoutParams) mExpandButtonContainer.getLayoutParams();
+            layoutParams.height = newContainerHeight;
+            mExpandButtonContainer.setLayoutParams(layoutParams);
+        }
+
+        // update if the expand button is centered
+        FrameLayout.LayoutParams layoutParams = (LayoutParams) mExpandButton.getLayoutParams();
+        layoutParams.gravity = gravity;
+        layoutParams.topMargin = topMargin;
+        mExpandButton.setLayoutParams(layoutParams);
+
+        mExpandButtonContainer.setContentDescription(mContext.getText(contentDescriptionId));
+
+    }
+
+    public void updateExpandability(boolean expandable, @Nullable OnClickListener onClickListener) {
+        if (expandable) {
+            mExpandButtonContainer.setVisibility(VISIBLE);
+            mExpandButtonContainer.setOnClickListener(onClickListener);
+        } else {
+            // TODO: handle content paddings to end of layout
+            mExpandButtonContainer.setVisibility(GONE);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/widget/IMessagingLayout.java b/core/java/com/android/internal/widget/IMessagingLayout.java
new file mode 100644
index 0000000..149d056
--- /dev/null
+++ b/core/java/com/android/internal/widget/IMessagingLayout.java
@@ -0,0 +1,42 @@
+/*
+ * 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.internal.widget;
+
+import android.content.Context;
+
+import java.util.ArrayList;
+
+/**
+ * An interface for a MessagingLayout
+ */
+public interface IMessagingLayout {
+
+    /**
+     * @return the layout containing the messages
+     */
+    MessagingLinearLayout getMessagingLinearLayout();
+
+    /**
+     * @return the context of this view
+     */
+    Context getContext();
+
+    /**
+     * @return the list of messaging groups
+     */
+    ArrayList<MessagingGroup> getMessagingGroups();
+}
diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java
index c9a9161..9977903 100644
--- a/core/java/com/android/internal/widget/MessagingGroup.java
+++ b/core/java/com/android/internal/widget/MessagingGroup.java
@@ -55,8 +55,9 @@
     private static Pools.SimplePool<MessagingGroup> sInstancePool
             = new Pools.SynchronizedPool<>(10);
     private MessagingLinearLayout mMessageContainer;
-    private ImageFloatingTextView mSenderName;
+    ImageFloatingTextView mSenderView;
     private ImageView mAvatarView;
+    private View mAvatarContainer;
     private String mAvatarSymbol = "";
     private int mLayoutColor;
     private CharSequence mAvatarName = "";
@@ -72,10 +73,18 @@
     private boolean mImagesAtEnd;
     private ViewGroup mImageContainer;
     private MessagingImageMessage mIsolatedMessage;
-    private boolean mTransformingImages;
+    private boolean mClippingDisabled;
     private Point mDisplaySize = new Point();
     private ProgressBar mSendingSpinner;
     private View mSendingSpinnerContainer;
+    private boolean mShowingAvatar = true;
+    private CharSequence mSenderName;
+    private boolean mSingleLine = false;
+    private LinearLayout mContentContainer;
+    private int mRequestedMaxDisplayedLines = Integer.MAX_VALUE;
+    private int mSenderTextPaddingSingleLine;
+    private boolean mIsFirstGroupInLayout = true;
+    private boolean mCanHideSenderIfFirst;
 
     public MessagingGroup(@NonNull Context context) {
         super(context);
@@ -99,26 +108,34 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         mMessageContainer = findViewById(R.id.group_message_container);
-        mSenderName = findViewById(R.id.message_name);
+        mSenderView = findViewById(R.id.message_name);
         mAvatarView = findViewById(R.id.message_icon);
         mImageContainer = findViewById(R.id.messaging_group_icon_container);
         mSendingSpinner = findViewById(R.id.messaging_group_sending_progress);
+        mContentContainer = findViewById(R.id.messaging_group_content_container);
         mSendingSpinnerContainer = findViewById(R.id.messaging_group_sending_progress_container);
         DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
         mDisplaySize.x = displayMetrics.widthPixels;
         mDisplaySize.y = displayMetrics.heightPixels;
+        mSenderTextPaddingSingleLine = getResources().getDimensionPixelSize(
+                R.dimen.messaging_group_singleline_sender_padding_end);
     }
 
     public void updateClipRect() {
         // We want to clip to the senderName if it's available, otherwise our images will come
         // from a weird position
         Rect clipRect;
-        if (mSenderName.getVisibility() != View.GONE && !mTransformingImages) {
-            ViewGroup parent = (ViewGroup) mSenderName.getParent();
-            int top = getDistanceFromParent(mSenderName, parent) - getDistanceFromParent(
-                    mMessageContainer, parent) + mSenderName.getHeight();
+        if (mSenderView.getVisibility() != View.GONE && !mClippingDisabled) {
+            int top;
+            if (mSingleLine) {
+                top = 0;
+            } else {
+                top = getDistanceFromParent(mSenderView, mContentContainer)
+                        - getDistanceFromParent(mMessageContainer, mContentContainer)
+                        + mSenderView.getHeight();
+            }
             int size = Math.max(mDisplaySize.x, mDisplaySize.y);
-            clipRect = new Rect(0, top, size, size);
+            clipRect = new Rect(-size, top, size, size);
         } else {
             clipRect = null;
         }
@@ -140,17 +157,31 @@
         if (nameOverride == null) {
             nameOverride = sender.getName();
         }
-        mSenderName.setText(nameOverride);
+        mSenderName = nameOverride;
+        if (mSingleLine && !TextUtils.isEmpty(nameOverride)) {
+            nameOverride = mContext.getResources().getString(
+                    R.string.conversation_single_line_name_display, nameOverride);
+        }
+        mSenderView.setText(nameOverride);
         mNeedsGeneratedAvatar = sender.getIcon() == null;
         if (!mNeedsGeneratedAvatar) {
             setAvatar(sender.getIcon());
         }
-        mAvatarView.setVisibility(VISIBLE);
-        mSenderName.setVisibility(TextUtils.isEmpty(nameOverride) ? GONE : VISIBLE);
+        updateSenderVisibility();
+    }
+
+    /**
+     * Should the avatar be shown for this view.
+     *
+     * @param showingAvatar should it be shown
+     */
+    public void setShowingAvatar(boolean showingAvatar) {
+        mAvatarView.setVisibility(showingAvatar ? VISIBLE : GONE);
+        mShowingAvatar = showingAvatar;
     }
 
     public void setSending(boolean sending) {
-        int visibility = sending ? View.VISIBLE : View.GONE;
+        int visibility = sending ? VISIBLE : GONE;
         if (mSendingSpinnerContainer.getVisibility() != visibility) {
             mSendingSpinnerContainer.setVisibility(visibility);
             updateMessageColor();
@@ -171,7 +202,9 @@
 
     public void setAvatar(Icon icon) {
         mAvatarIcon = icon;
-        mAvatarView.setImageIcon(icon);
+        if (mShowingAvatar || icon == null) {
+            mAvatarView.setImageIcon(icon);
+        }
         mAvatarSymbol = "";
         mAvatarName = "";
     }
@@ -220,13 +253,20 @@
         setAvatar(null);
         mAvatarView.setAlpha(1.0f);
         mAvatarView.setTranslationY(0.0f);
-        mSenderName.setAlpha(1.0f);
-        mSenderName.setTranslationY(0.0f);
+        mSenderView.setAlpha(1.0f);
+        mSenderView.setTranslationY(0.0f);
         setAlpha(1.0f);
         mIsolatedMessage = null;
         mMessages = null;
+        mSenderName = null;
         mAddedMessages.clear();
         mFirstLayout = true;
+        setCanHideSenderIfFirst(false);
+        setIsFirstInLayout(true);
+
+        setMaxDisplayedLines(Integer.MAX_VALUE);
+        setSingleLine(false);
+        setShowingAvatar(true);
         MessagingPropertyAnimator.recycle(this);
         sInstancePool.release(MessagingGroup.this);
     }
@@ -252,7 +292,7 @@
     }
 
     public CharSequence getSenderName() {
-        return mSenderName.getText();
+        return mSenderName;
     }
 
     public static void dropCache() {
@@ -310,7 +350,12 @@
 
     @Override
     public void setMaxDisplayedLines(int lines) {
-        mMessageContainer.setMaxDisplayedLines(lines);
+        mRequestedMaxDisplayedLines = lines;
+        updateMaxDisplayedLines();
+    }
+
+    private void updateMaxDisplayedLines() {
+        mMessageContainer.setMaxDisplayedLines(mSingleLine ? 1 : mRequestedMaxDisplayedLines);
     }
 
     @Override
@@ -324,6 +369,35 @@
         return mIsHidingAnimated;
     }
 
+    @Override
+    public void setIsFirstInLayout(boolean first) {
+        if (first != mIsFirstGroupInLayout) {
+            mIsFirstGroupInLayout = first;
+            updateSenderVisibility();
+        }
+    }
+
+    /**
+     * @param canHide true if the sender can be hidden if it is first
+     */
+    public void setCanHideSenderIfFirst(boolean canHide) {
+        if (mCanHideSenderIfFirst != canHide) {
+            mCanHideSenderIfFirst = canHide;
+            updateSenderVisibility();
+        }
+    }
+
+    private void updateSenderVisibility() {
+        boolean hidden = (mIsFirstGroupInLayout || mSingleLine) && mCanHideSenderIfFirst
+                || TextUtils.isEmpty(mSenderName);
+        mSenderView.setVisibility(hidden ? GONE : VISIBLE);
+    }
+
+    @Override
+    public boolean hasDifferentHeightWhenFirst() {
+        return mCanHideSenderIfFirst && !mSingleLine && !TextUtils.isEmpty(mSenderName);
+    }
+
     private void setIsHidingAnimated(boolean isHiding) {
         ViewParent parent = getParent();
         mIsHidingAnimated = isHiding;
@@ -362,7 +436,7 @@
         mTextColor = messageTextColor;
         mSendingTextColor = calculateSendingTextColor();
         updateMessageColor();
-        mSenderName.setTextColor(senderTextColor);
+        mSenderView.setTextColor(senderTextColor);
     }
 
     public void setLayoutColor(int layoutColor) {
@@ -506,13 +580,17 @@
     }
 
     public View getSenderView() {
-        return mSenderName;
+        return mSenderView;
     }
 
     public View getAvatar() {
         return mAvatarView;
     }
 
+    public Icon getAvatarIcon() {
+        return mAvatarIcon;
+    }
+
     public MessagingLinearLayout getMessageContainer() {
         return mMessageContainer;
     }
@@ -529,8 +607,8 @@
         return mSender;
     }
 
-    public void setTransformingImages(boolean transformingImages) {
-        mTransformingImages = transformingImages;
+    public void setClippingDisabled(boolean disabled) {
+        mClippingDisabled = disabled;
     }
 
     public void setDisplayImagesAtEnd(boolean atEnd) {
@@ -543,4 +621,27 @@
     public List<MessagingMessage> getMessages() {
         return mMessages;
     }
+
+    /**
+     * Set this layout to be single line and therefore displaying both the sender and the text on
+     * the same line.
+     *
+     * @param singleLine should be layout be single line
+     */
+    public void setSingleLine(boolean singleLine) {
+        if (singleLine != mSingleLine) {
+            mSingleLine = singleLine;
+            mContentContainer.setOrientation(
+                    singleLine ? LinearLayout.HORIZONTAL : LinearLayout.VERTICAL);
+            MarginLayoutParams layoutParams = (MarginLayoutParams) mSenderView.getLayoutParams();
+            layoutParams.setMarginEnd(singleLine ? mSenderTextPaddingSingleLine : 0);
+            updateMaxDisplayedLines();
+            updateClipRect();
+            updateSenderVisibility();
+        }
+    }
+
+    public boolean isSingleLine() {
+        return mSingleLine;
+    }
 }
diff --git a/core/java/com/android/internal/widget/MessagingImageMessage.java b/core/java/com/android/internal/widget/MessagingImageMessage.java
index 64650a7..c243f3b 100644
--- a/core/java/com/android/internal/widget/MessagingImageMessage.java
+++ b/core/java/com/android/internal/widget/MessagingImageMessage.java
@@ -120,7 +120,7 @@
         return true;
     }
 
-    static MessagingMessage createMessage(MessagingLayout layout,
+    static MessagingMessage createMessage(IMessagingLayout layout,
             Notification.MessagingStyle.Message m, ImageResolver resolver) {
         MessagingLinearLayout messagingLinearLayout = layout.getMessagingLinearLayout();
         MessagingImageMessage createdMessage = sInstancePool.acquire();
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index f608958..503f3f1 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -58,7 +58,8 @@
  * messages and adapts the layout accordingly.
  */
 @RemoteViews.RemoteView
-public class MessagingLayout extends FrameLayout implements ImageMessageConsumer {
+public class MessagingLayout extends FrameLayout
+        implements ImageMessageConsumer, IMessagingLayout {
 
     private static final float COLOR_SHIFT_AMOUNT = 60;
     /**
@@ -143,9 +144,29 @@
         mNameReplacement = nameReplacement;
     }
 
+    /**
+     * Set this layout to show the collapsed representation.
+     *
+     * @param isCollapsed is it collapsed
+     */
     @RemotableViewMethod
-    public void setDisplayImagesAtEnd(boolean atEnd) {
-        mDisplayImagesAtEnd = atEnd;
+    public void setIsCollapsed(boolean isCollapsed) {
+        mDisplayImagesAtEnd = isCollapsed;
+    }
+
+    @RemotableViewMethod
+    public void setLargeIcon(Icon largeIcon) {
+        // Unused
+    }
+
+    /**
+     * Sets the conversation title of this conversation.
+     *
+     * @param conversationTitle the conversation title
+     */
+    @RemotableViewMethod
+    public void setConversationTitle(CharSequence conversationTitle) {
+        // Unused
     }
 
     @RemotableViewMethod
@@ -371,6 +392,15 @@
         mSenderTextColor = color;
     }
 
+
+    /**
+     * @param color the color of the notification background
+     */
+    @RemotableViewMethod
+    public void setNotificationBackgroundColor(int color) {
+        // Nothing to do with this
+    }
+
     @RemotableViewMethod
     public void setMessageTextColor(int color) {
         mMessageTextColor = color;
diff --git a/core/java/com/android/internal/widget/MessagingLinearLayout.java b/core/java/com/android/internal/widget/MessagingLinearLayout.java
index 0c8613b..ac04862 100644
--- a/core/java/com/android/internal/widget/MessagingLinearLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLinearLayout.java
@@ -43,7 +43,7 @@
 
     private int mMaxDisplayedLines = Integer.MAX_VALUE;
 
-    private MessagingLayout mMessagingLayout;
+    private IMessagingLayout mMessagingLayout;
 
     public MessagingLinearLayout(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
@@ -84,6 +84,11 @@
             final View child = getChildAt(i);
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
             lp.hide = true;
+            if (child instanceof MessagingChild) {
+                MessagingChild messagingChild = (MessagingChild) child;
+                // Whenever we encounter the message first, it's always first in the layout
+                messagingChild.setIsFirstInLayout(true);
+            }
         }
 
         totalHeight = mPaddingTop + mPaddingBottom;
@@ -91,6 +96,11 @@
         int linesRemaining = mMaxDisplayedLines;
         // Starting from the bottom: we measure every view as if it were the only one. If it still
         // fits, we take it, otherwise we stop there.
+        MessagingChild previousChild = null;
+        View previousView = null;
+        int previousChildHeight = 0;
+        int previousTotalHeight = 0;
+        int previousLinesConsumed = 0;
         for (int i = count - 1; i >= 0 && totalHeight < targetHeight; i--) {
             if (getChildAt(i).getVisibility() == GONE) {
                 continue;
@@ -99,7 +109,16 @@
             LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
             MessagingChild messagingChild = null;
             int spacing = mSpacing;
+            int previousChildIncrease = 0;
             if (child instanceof MessagingChild) {
+                // We need to remeasure the previous child again if it's not the first anymore
+                if (previousChild != null && previousChild.hasDifferentHeightWhenFirst()) {
+                    previousChild.setIsFirstInLayout(false);
+                    measureChildWithMargins(previousView, widthMeasureSpec, 0, heightMeasureSpec,
+                            previousTotalHeight - previousChildHeight);
+                    previousChildIncrease = previousView.getMeasuredHeight() - previousChildHeight;
+                    linesRemaining -= previousChild.getConsumedLines() - previousLinesConsumed;
+                }
                 messagingChild = (MessagingChild) child;
                 messagingChild.setMaxDisplayedLines(linesRemaining);
                 spacing += messagingChild.getExtraSpacing();
@@ -110,18 +129,26 @@
 
             final int childHeight = child.getMeasuredHeight();
             int newHeight = Math.max(totalHeight, totalHeight + childHeight + lp.topMargin +
-                    lp.bottomMargin + spacing);
+                    lp.bottomMargin + spacing + previousChildIncrease);
             int measureType = MessagingChild.MEASURED_NORMAL;
             if (messagingChild != null) {
                 measureType = messagingChild.getMeasuredType();
-                linesRemaining -= messagingChild.getConsumedLines();
             }
 
             // We never measure the first item as too small, we want to at least show something.
             boolean isTooSmall = measureType == MessagingChild.MEASURED_TOO_SMALL && !first;
             boolean isShortened = measureType == MessagingChild.MEASURED_SHORTENED
                     || measureType == MessagingChild.MEASURED_TOO_SMALL && first;
-            if (newHeight <= targetHeight && !isTooSmall) {
+            boolean showView = newHeight <= targetHeight && !isTooSmall;
+            if (showView) {
+                if (messagingChild != null) {
+                    previousLinesConsumed = messagingChild.getConsumedLines();
+                    linesRemaining -= previousLinesConsumed;
+                    previousChild = messagingChild;
+                    previousView = child;
+                    previousChildHeight = childHeight;
+                    previousTotalHeight = totalHeight;
+                }
                 totalHeight = newHeight;
                 measuredWidth = Math.max(measuredWidth,
                         child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin
@@ -131,6 +158,16 @@
                     break;
                 }
             } else {
+                // We now became too short, let's make sure to reset any previous views to be first
+                // and remeasure it.
+                if (previousChild != null && previousChild.hasDifferentHeightWhenFirst()) {
+                    previousChild.setIsFirstInLayout(true);
+                    // We need to remeasure the previous child again since it became first
+                    measureChildWithMargins(previousView, widthMeasureSpec, 0, heightMeasureSpec,
+                            previousTotalHeight - previousChildHeight);
+                    // The totalHeight is already correct here since we only set it during the
+                    // first pass
+                }
                 break;
             }
             first = false;
@@ -255,11 +292,11 @@
         mMaxDisplayedLines = numberLines;
     }
 
-    public void setMessagingLayout(MessagingLayout layout) {
+    public void setMessagingLayout(IMessagingLayout layout) {
         mMessagingLayout = layout;
     }
 
-    public MessagingLayout getMessagingLayout() {
+    public IMessagingLayout getMessagingLayout() {
         return mMessagingLayout;
     }
 
@@ -273,6 +310,20 @@
         void setMaxDisplayedLines(int lines);
         void hideAnimated();
         boolean isHidingAnimated();
+
+        /**
+         * Set that this view is first in layout. Relevant and only set if
+         * {@link #hasDifferentHeightWhenFirst()}.
+         * @param first is this first?
+         */
+        default void setIsFirstInLayout(boolean first) {}
+
+        /**
+         * @return if this layout has different height it is first in the layout
+         */
+        default boolean hasDifferentHeightWhenFirst() {
+            return false;
+        }
         default int getExtraSpacing() {
             return 0;
         }
diff --git a/core/java/com/android/internal/widget/MessagingMessage.java b/core/java/com/android/internal/widget/MessagingMessage.java
index c32d370..8c84379 100644
--- a/core/java/com/android/internal/widget/MessagingMessage.java
+++ b/core/java/com/android/internal/widget/MessagingMessage.java
@@ -32,7 +32,7 @@
      **/
     String IMAGE_MIME_TYPE_PREFIX = "image/";
 
-    static MessagingMessage createMessage(MessagingLayout layout,
+    static MessagingMessage createMessage(IMessagingLayout layout,
             Notification.MessagingStyle.Message m, ImageResolver resolver) {
         if (hasImage(m) && !ActivityManager.isLowRamDeviceStatic()) {
             return MessagingImageMessage.createMessage(layout, m, resolver);
diff --git a/core/java/com/android/internal/widget/MessagingTextMessage.java b/core/java/com/android/internal/widget/MessagingTextMessage.java
index 4081a86..d778c59 100644
--- a/core/java/com/android/internal/widget/MessagingTextMessage.java
+++ b/core/java/com/android/internal/widget/MessagingTextMessage.java
@@ -26,14 +26,10 @@
 import android.util.AttributeSet;
 import android.util.Pools;
 import android.view.LayoutInflater;
-import android.view.ViewGroup;
-import android.view.ViewParent;
 import android.widget.RemoteViews;
 
 import com.android.internal.R;
 
-import java.util.Objects;
-
 /**
  * A message of a {@link MessagingLayout}.
  */
@@ -74,7 +70,7 @@
         return true;
     }
 
-    static MessagingMessage createMessage(MessagingLayout layout,
+    static MessagingMessage createMessage(IMessagingLayout layout,
             Notification.MessagingStyle.Message m) {
         MessagingLinearLayout messagingLinearLayout = layout.getMessagingLinearLayout();
         MessagingTextMessage createdMessage = sInstancePool.acquire();
diff --git a/core/java/com/android/internal/widget/NotificationExpandButton.java b/core/java/com/android/internal/widget/NotificationExpandButton.java
index 39f82a5..a499806 100644
--- a/core/java/com/android/internal/widget/NotificationExpandButton.java
+++ b/core/java/com/android/internal/widget/NotificationExpandButton.java
@@ -20,7 +20,7 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
-import android.view.View;
+import android.view.RemotableViewMethod;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.Button;
 import android.widget.ImageView;
@@ -32,6 +32,8 @@
 @RemoteViews.RemoteView
 public class NotificationExpandButton extends ImageView {
 
+    private int mOriginalNotificationColor;
+
     public NotificationExpandButton(Context context) {
         super(context);
     }
@@ -56,6 +58,15 @@
         extendRectToMinTouchSize(outRect);
     }
 
+    @RemotableViewMethod
+    public void setOriginalNotificationColor(int color) {
+        mOriginalNotificationColor = color;
+    }
+
+    public int getOriginalNotificationColor() {
+        return mOriginalNotificationColor;
+    }
+
     private void extendRectToMinTouchSize(Rect rect) {
         int touchTargetSize = (int) (getResources().getDisplayMetrics().density * 48);
         rect.left = rect.centerX() - touchTargetSize / 2;
diff --git a/core/java/com/android/internal/widget/RemeasuringLinearLayout.java b/core/java/com/android/internal/widget/RemeasuringLinearLayout.java
index e352b45..7b154a5 100644
--- a/core/java/com/android/internal/widget/RemeasuringLinearLayout.java
+++ b/core/java/com/android/internal/widget/RemeasuringLinearLayout.java
@@ -23,6 +23,8 @@
 import android.widget.LinearLayout;
 import android.widget.RemoteViews;
 
+import java.util.ArrayList;
+
 /**
  * A LinearLayout that sets it's height again after the last measure pass. This is needed for
  * MessagingLayouts where groups need to be able to snap it's height to.
@@ -30,6 +32,8 @@
 @RemoteViews.RemoteView
 public class RemeasuringLinearLayout extends LinearLayout {
 
+    private ArrayList<View> mMatchParentViews = new ArrayList<>();
+
     public RemeasuringLinearLayout(Context context) {
         super(context);
     }
@@ -53,6 +57,8 @@
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         int count = getChildCount();
         int height = 0;
+        boolean isVertical = getOrientation() == LinearLayout.VERTICAL;
+        boolean isWrapContent = getLayoutParams().height == LayoutParams.WRAP_CONTENT;
         for (int i = 0; i < count; ++i) {
             final View child = getChildAt(i);
             if (child == null || child.getVisibility() == View.GONE) {
@@ -60,9 +66,25 @@
             }
 
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            height = Math.max(height, height + child.getMeasuredHeight() + lp.topMargin +
-                    lp.bottomMargin);
+            if (!isWrapContent || lp.height != LayoutParams.MATCH_PARENT || isVertical) {
+                int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
+                height = Math.max(height, isVertical ? height + childHeight : childHeight);
+            } else {
+                // We have match parent children in a wrap content view, let's measure the
+                // view properly
+                mMatchParentViews.add(child);
+            }
         }
+        if (mMatchParentViews.size() > 0) {
+            int exactHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
+            for (View child : mMatchParentViews) {
+                child.measure(getChildMeasureSpec(
+                        widthMeasureSpec, getPaddingStart() + getPaddingEnd(),
+                        child.getLayoutParams().width),
+                        exactHeightSpec);
+            }
+        }
+        mMatchParentViews.clear();
         setMeasuredDimension(getMeasuredWidth(), height);
     }
 }
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 0d0dc3e..f7c6dbd 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -853,8 +853,11 @@
     } cfg_state = CONFIG_UNKNOWN;
 
     if (cfg_state == CONFIG_UNKNOWN) {
-        const std::map<std::string, std::string> configs =
-            vintf::VintfObject::GetInstance()->getRuntimeInfo()->kernelConfigs();
+        auto runtime_info = vintf::VintfObject::GetInstance()
+                                    ->getRuntimeInfo(false /* skip cache */,
+                                                     vintf::RuntimeInfo::FetchFlag::CONFIG_GZ);
+        CHECK(runtime_info != nullptr) << "Kernel configs cannot be fetched. b/151092221";
+        const std::map<std::string, std::string>& configs = runtime_info->kernelConfigs();
         std::map<std::string, std::string>::const_iterator it = configs.find("CONFIG_VMAP_STACK");
         cfg_state = (it != configs.end() && it->second == "y") ? CONFIG_SET : CONFIG_UNSET;
     }
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 2128f99..dafb377 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -121,16 +121,11 @@
 static pid_t gSystemServerPid = 0;
 
 static constexpr const char* kPropFuse = "persist.sys.fuse";
-static constexpr const char* kZygoteClassName = "com/android/internal/os/Zygote";
-
+static const char kZygoteClassName[] = "com/android/internal/os/Zygote";
 static jclass gZygoteClass;
 static jmethodID gCallPostForkSystemServerHooks;
 static jmethodID gCallPostForkChildHooks;
 
-static constexpr const char* kZygoteInitClassName = "com/android/internal/os/ZygoteInit";
-static jclass gZygoteInitClass;
-static jmethodID gCreateSystemServerClassLoader;
-
 static bool gIsSecurityEnforced = true;
 
 /**
@@ -356,10 +351,14 @@
 
 // Must match values in com.android.internal.os.Zygote.
 enum RuntimeFlags : uint32_t {
-  DEBUG_ENABLE_JDWP = 1,
-  PROFILE_FROM_SHELL = 1 << 15,
-  MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20),
-  MEMORY_TAG_LEVEL_TBI = 1 << 19,
+    DEBUG_ENABLE_JDWP = 1,
+    PROFILE_FROM_SHELL = 1 << 15,
+    MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20),
+    MEMORY_TAG_LEVEL_TBI = 1 << 19,
+    GWP_ASAN_LEVEL_MASK = (1 << 21) | (1 << 22),
+    GWP_ASAN_LEVEL_NEVER = 0 << 21,
+    GWP_ASAN_LEVEL_LOTTERY = 1 << 21,
+    GWP_ASAN_LEVEL_ALWAYS = 2 << 21,
 };
 
 enum UnsolicitedZygoteMessageTypes : uint32_t {
@@ -1741,6 +1740,18 @@
   }
   android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &heap_tagging_level, sizeof(heap_tagging_level));
 
+  bool forceEnableGwpAsan = false;
+  switch (runtime_flags & RuntimeFlags::GWP_ASAN_LEVEL_MASK) {
+      default:
+      case RuntimeFlags::GWP_ASAN_LEVEL_NEVER:
+          break;
+      case RuntimeFlags::GWP_ASAN_LEVEL_ALWAYS:
+          forceEnableGwpAsan = true;
+          [[fallthrough]];
+      case RuntimeFlags::GWP_ASAN_LEVEL_LOTTERY:
+          android_mallopt(M_INITIALIZE_GWP_ASAN, &forceEnableGwpAsan, sizeof(forceEnableGwpAsan));
+  }
+
   if (NeedsNoRandomizeWorkaround()) {
     // Work around ARM kernel ASLR lossage (http://b/5817320).
     int old_personality = personality(0xffffffff);
@@ -1780,15 +1791,6 @@
       fail_fn("Error calling post fork system server hooks.");
     }
 
-    // Prefetch the classloader for the system server. This is done early to
-    // allow a tie-down of the proper system server selinux domain.
-    env->CallStaticVoidMethod(gZygoteInitClass, gCreateSystemServerClassLoader);
-    if (env->ExceptionCheck()) {
-      // Be robust here. The Java code will attempt to create the classloader
-      // at a later point (but may not have rights to use AoT artifacts).
-      env->ExceptionClear();
-    }
-
     // TODO(oth): Remove hardcoded label here (b/117874058).
     static const char* kSystemServerLabel = "u:r:system_server:s0";
     if (selinux_android_setcon(kSystemServerLabel) != 0) {
@@ -2466,13 +2468,6 @@
   gCallPostForkChildHooks = GetStaticMethodIDOrDie(env, gZygoteClass, "callPostForkChildHooks",
                                                    "(IZZLjava/lang/String;)V");
 
-  gZygoteInitClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, kZygoteInitClassName));
-  gCreateSystemServerClassLoader = GetStaticMethodIDOrDie(env, gZygoteInitClass,
-                                                          "createSystemServerClassLoader",
-                                                          "()V");
-
-  RegisterMethodsOrDie(env, "com/android/internal/os/Zygote", gMethods, NELEM(gMethods));
-
-  return JNI_OK;
+  return RegisterMethodsOrDie(env, "com/android/internal/os/Zygote", gMethods, NELEM(gMethods));
 }
 }  // namespace android
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 030483b..ef6eb38 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -724,6 +724,16 @@
     // CATEGORY: SETTINGS
     // OS: R
     ACTION_ADB_WIRELESS_OFF = 1735;
+
+    // ACTION: Change Wi-Fi hotspot name
+    // CATEGORY: SETTINGS
+    // OS: R
+    ACTION_SETTINGS_CHANGE_WIFI_HOTSPOT_NAME = 1736;
+
+    // ACTION: Change Wi-Fi hotspot password
+    // CATEGORY: SETTINGS
+    // OS: R
+    ACTION_SETTINGS_CHANGE_WIFI_HOTSPOT_PASSWORD = 1737;
 }
 
 /**
diff --git a/core/proto/android/content/package_item_info.proto b/core/proto/android/content/package_item_info.proto
index 4a7d043..e8ae1b3 100644
--- a/core/proto/android/content/package_item_info.proto
+++ b/core/proto/android/content/package_item_info.proto
@@ -109,6 +109,7 @@
         }
         optional int32 network_security_config_res = 17;
         optional int32 category = 18;
+        optional bool enable_gwp_asan = 19;
     }
     optional Detail detail = 17;
 }
diff --git a/core/proto/android/telephony/enums.proto b/core/proto/android/telephony/enums.proto
index 4777169..f14e3ed 100644
--- a/core/proto/android/telephony/enums.proto
+++ b/core/proto/android/telephony/enums.proto
@@ -20,6 +20,43 @@
 option java_outer_classname = "TelephonyProtoEnums";
 option java_multiple_files = true;
 
+enum CallBearerEnum {
+    /** Call bearer is unknown or invalid */
+    CALL_BEARER_UNKNOWN = 0;
+
+    /** Call bearer is legacy CS */
+    CALL_BEARER_CS = 1;
+
+    /** Call bearer is IMS */
+    CALL_BEARER_IMS = 2;
+}
+
+enum CallDirectionEnum {
+    /** Call direction: unknown or invalid */
+    CALL_DIRECTION_UNKNOWN = 0;
+
+    /** Call direction: mobile originated (outgoing for this device) */
+    CALL_DIRECTION_MO = 1;
+
+    /** Call direction: mobile terminated (incoming for this device) */
+    CALL_DIRECTION_MT = 2;
+}
+
+// Call setup duration buckets.
+// See com.android.internal.telephony.metrics.VoiceCallSessionStats for definition.
+enum CallSetupDurationEnum {
+    CALL_SETUP_DURATION_UNKNOWN = 0;
+    CALL_SETUP_DURATION_EXTREMELY_FAST = 1;
+    CALL_SETUP_DURATION_ULTRA_FAST = 2;
+    CALL_SETUP_DURATION_VERY_FAST = 3;
+    CALL_SETUP_DURATION_FAST = 4;
+    CALL_SETUP_DURATION_NORMAL = 5;
+    CALL_SETUP_DURATION_SLOW = 6;
+    CALL_SETUP_DURATION_VERY_SLOW = 7;
+    CALL_SETUP_DURATION_ULTRA_SLOW = 8;
+    CALL_SETUP_DURATION_EXTREMELY_SLOW = 9;
+}
+
 // Data conn. power states, primarily used by android/telephony/DataConnectionRealTimeInfo.java.
 enum DataConnectionPowerStateEnum {
     DATA_CONNECTION_POWER_STATE_LOW = 1;
@@ -63,7 +100,6 @@
     SIGNAL_STRENGTH_GREAT = 4;
 }
 
-
 enum ServiceStateEnum {
     /**
      * Normal operation condition, the phone is registered
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5086c7e..4001def 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1187,6 +1187,16 @@
                 android:description="@string/permdesc_callCompanionApp"
                 android:protectionLevel="normal" />
 
+    <!-- Exempt this uid from restrictions to background audio recoding
+     <p>Protection level: signature|privileged
+     @hide
+     @SystemApi
+    -->
+    <permission android:name="android.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS"
+                android:label="@string/permlab_exemptFromAudioRecordRestrictions"
+                android:description="@string/permdesc_exemptFromAudioRecordRestrictions"
+                android:protectionLevel="signature|privileged" />
+
     <!-- Allows a calling app to continue a call which was started in another app.  An example is a
          video calling app that wants to continue a voice call on the user's mobile network.<p>
          When the handover of a call from one app to another takes place, there are two devices
@@ -3735,7 +3745,8 @@
     <permission android:name="android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY"
                 android:protectionLevel="signature|installer" />
 
-    <!-- @hide Allows an application to upgrade runtime permissions. -->
+    <!-- @SystemApi @TestApi Allows an application to upgrade runtime permissions.
+    @hide -->
     <permission android:name="android.permission.UPGRADE_RUNTIME_PERMISSIONS"
                 android:protectionLevel="signature" />
 
@@ -4992,15 +5003,15 @@
     <permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"
                 android:protectionLevel="signature|appPredictor" />
 
-    <!-- Feature Id for Country Detector. -->
-    <feature android:featureId="CountryDetector" android:label="@string/country_detector"/>
-    <!-- Feature Id for Location service. -->
-    <feature android:featureId="LocationService" android:label="@string/location_service"/>
-    <!-- Feature Id for Sensor Notification service. -->
-    <feature android:featureId="SensorNotificationService"
+    <!-- Attribution for Country Detector. -->
+    <attribution android:tag="CountryDetector" android:label="@string/country_detector"/>
+    <!-- Attribution for Location service. -->
+    <attribution android:tag="LocationService" android:label="@string/location_service"/>
+    <!-- Attribution for Sensor Notification service. -->
+    <attribution android:tag="SensorNotificationService"
              android:label="@string/sensor_notification_service"/>
     <!-- Feature Id for Twilight service. -->
-    <feature android:featureId="TwilightService" android:label="@string/twilight_service"/>
+    <attribution android:tag="TwilightService" android:label="@string/twilight_service"/>
 
     <application android:process="system"
                  android:persistent="true"
@@ -5189,6 +5200,7 @@
         <activity android:name="com.android.internal.app.BlockedAppActivity"
                 android:theme="@style/Theme.Dialog.Confirmation"
                 android:excludeFromRecents="true"
+                android:lockTaskMode="always"
                 android:process=":ui">
         </activity>
 
diff --git a/core/res/res/drawable/conversation_badge_background.xml b/core/res/res/drawable/conversation_badge_background.xml
new file mode 100644
index 0000000..0dd0dcd
--- /dev/null
+++ b/core/res/res/drawable/conversation_badge_background.xml
@@ -0,0 +1,28 @@
+<?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
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+
+    <solid
+        android:color="#ffffff"/>
+
+    <size
+        android:width="26dp"
+        android:height="26dp"/>
+</shape>
+
diff --git a/core/res/res/drawable/ic_collapse_notification.xml b/core/res/res/drawable/ic_collapse_notification.xml
index 124e99e..ca4f0ed 100644
--- a/core/res/res/drawable/ic_collapse_notification.xml
+++ b/core/res/res/drawable/ic_collapse_notification.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-Copyright (C) 2015 The Android Open Source Project
+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.
@@ -15,11 +15,14 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="14.0dp"
-        android:height="14.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+    android:width="22.0dp"
+    android:height="22.0dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
     <path
         android:fillColor="#FF000000"
-        android:pathData="M12.0,8.0l-6.0,6.0l1.4,1.4l4.6,-4.6l4.6,4.6L18.0,14.0L12.0,8.0z"/>
-</vector>
+        android:pathData="M18.59,16.41L20.0,15.0l-8.0,-8.0 -8.0,8.0 1.41,1.41L12.0,9.83"/>
+    <path
+        android:pathData="M0 0h24v24H0V0z"
+        android:fillColor="#00000000"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_expand_notification.xml b/core/res/res/drawable/ic_expand_notification.xml
index 847e326..a080ce4 100644
--- a/core/res/res/drawable/ic_expand_notification.xml
+++ b/core/res/res/drawable/ic_expand_notification.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-Copyright (C) 2015 The Android Open Source Project
+Copyright (C) 2014 The Android Open Source Project
 
    Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
@@ -15,11 +15,14 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="14.0dp"
-        android:height="14.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+    android:width="22.0dp"
+    android:height="22.0dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
     <path
         android:fillColor="#FF000000"
-        android:pathData="M16.6,8.6L12.0,13.2L7.4,8.6L6.0,10.0l6.0,6.0l6.0,-6.0L16.6,8.6z"/>
-</vector>
+        android:pathData="M5.41,7.59L4.0,9.0l8.0,8.0 8.0,-8.0 -1.41,-1.41L12.0,14.17"/>
+    <path
+        android:pathData="M24 24H0V0h24v24z"
+        android:fillColor="#00000000"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/layout-car/car_resolver_different_item_header.xml b/core/res/res/layout-car/car_resolver_different_item_header.xml
deleted file mode 100644
index 222ecc6..0000000
--- a/core/res/res/layout-car/car_resolver_different_item_header.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright 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.
- */
--->
-<TextView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_alwaysShow="true"
-    android:text="@*android:string/use_a_different_app"
-    android:minHeight="56dp"
-    android:textAppearance="?android:attr/textAppearanceLarge"
-    android:gravity="start|center_vertical"
-    android:paddingStart="16dp"
-    android:paddingEnd="16dp"
-    android:paddingTop="8dp"
-    android:paddingBottom="8dp"
-    android:elevation="8dp"
-/>
\ No newline at end of file
diff --git a/core/res/res/layout-car/car_resolver_list.xml b/core/res/res/layout-car/car_resolver_list.xml
index 15a8645..755cbfe 100644
--- a/core/res/res/layout-car/car_resolver_list.xml
+++ b/core/res/res/layout-car/car_resolver_list.xml
@@ -23,90 +23,142 @@
     android:id="@id/contentPanel">
 
     <LinearLayout
-        android:id="@+id/button_bar"
-        android:visibility="gone"
-        style="?attr/buttonBarStyle"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_ignoreOffset="true"
-        android:layout_alwaysShow="true"
-        android:layout_hasNestedScrollIndicator="true"
-        android:background="?attr/colorBackgroundFloating"
-        android:orientation="horizontal"
-        android:paddingTop="8dp"
-        android:paddingBottom="8dp"
-        android:paddingStart="12dp"
+        android:layout_height="match_parent"
         android:weightSum="5"
-        android:paddingEnd="12dp"
+        android:layout_alwaysShow="true"
+        android:orientation="vertical"
+        android:background="?attr/colorBackgroundFloating"
         android:elevation="8dp">
 
-        <TextView
-            android:id="@+id/profile_button"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginEnd="8dp"
-            android:paddingStart="8dp"
-            android:paddingEnd="8dp"
-            android:textSize="40sp"
-            android:layout_weight="5"
-            android:layout_gravity = "left"
+        <LinearLayout
+            android:id="@+id/button_bar"
             android:visibility="gone"
-            android:textColor="?attr/colorAccent"
-            android:singleLine="true"/>
-
-        <TextView
-            android:id="@+id/title"
-            android:layout_width="wrap_content"
+            style="?attr/buttonBarStyle"
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:minHeight="56dp"
-            android:layout_gravity = "left"
-            android:layout_weight="3"
+            android:layout_ignoreOffset="true"
+            android:layout_alwaysShow="true"
+            android:layout_hasNestedScrollIndicator="true"
+            android:background="?attr/colorBackgroundFloating"
+            android:orientation="horizontal"
             android:paddingTop="8dp"
-            android:layout_below="@id/profile_button"
-            android:paddingBottom="8dp"/>
+            android:paddingStart="12dp"
+            android:weightSum="4"
+            android:paddingEnd="12dp"
+            android:elevation="8dp">
 
-        <Button
-            android:id="@+id/button_once"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:enabled="false"
-            android:layout_gravity = "right"
-            android:text="@string/activity_resolver_use_once"
-            android:layout_weight="1"
-            android:onClick="onButtonClick"/>
+            <TextView
+                android:id="@+id/profile_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginEnd="8dp"
+                android:paddingStart="8dp"
+                android:paddingEnd="8dp"
+                android:textSize="40sp"
+                android:layout_weight="4"
+                android:layout_gravity="left"
+                android:visibility="gone"
+                android:textColor="?attr/colorAccent"
+                android:singleLine="true"/>
 
-        <Button
-            android:id="@+id/button_always"
-            android:layout_marginLeft="10dp"
-            android:layout_width="wrap_content"
+            <TextView
+                android:id="@+id/title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="left"
+                android:layout_weight="3"
+                android:paddingTop="8dp"
+                android:layout_below="@id/profile_button"
+                android:textAppearance="?android:attr/textAppearanceLarge"
+                android:paddingBottom="8dp"/>
+
+            <Button
+                android:id="@+id/button_once"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:enabled="false"
+                android:layout_gravity="right"
+                style="?attr/buttonBarButtonStyle"
+                android:text="@string/activity_resolver_use_once"
+                android:layout_weight="0.5"
+                android:onClick="onButtonClick"/>
+
+            <Button
+                android:id="@+id/button_always"
+                android:layout_marginLeft="2dp"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:enabled="false"
+                android:layout_gravity="right"
+                style="?attr/buttonBarButtonStyle"
+                android:text="@string/activity_resolver_use_always"
+                android:layout_weight="0.5"
+                android:onClick="onButtonClick"/>
+        </LinearLayout>
+
+        <FrameLayout
+            android:id="@+id/stub"
+            android:visibility="gone"
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:enabled="false"
-            android:layout_gravity = "right"
-            android:text="@string/activity_resolver_use_always"
-            android:layout_weight="1"
-            android:onClick="onButtonClick"/>
+            android:background="?attr/colorBackgroundFloating"/>
+
+        <TabHost
+            android:id="@+id/profile_tabhost"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentTop="true"
+            android:layout_centerHorizontal="true"
+            android:background="?attr/colorBackgroundFloating">
+            <LinearLayout
+                android:orientation="vertical"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content">
+                <TabWidget
+                    android:id="@android:id/tabs"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:visibility="gone">
+                </TabWidget>
+                <View
+                    android:id="@+id/resolver_tab_divider"
+                    android:visibility="gone"
+                    android:layout_width="match_parent"
+                    android:layout_height="1dp"
+                    android:background="?attr/colorBackgroundFloating"
+                    android:foreground="?attr/dividerVertical"
+                    android:layout_marginBottom="8dp"/>
+                <FrameLayout
+                    android:id="@android:id/tabcontent"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content">
+                    <com.android.internal.app.ResolverViewPager
+                        android:id="@+id/profile_pager"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"/>
+                </FrameLayout>
+            </LinearLayout>
+        </TabHost>
+
+        <View
+            android:layout_alwaysShow="true"
+            android:layout_width="match_parent"
+            android:layout_height="1dp"
+            android:background="?attr/colorBackgroundFloating"
+            android:foreground="?attr/dividerVertical"/>
+
+        <TextView android:id="@+id/empty"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:background="?attr/colorBackgroundFloating"
+                  android:elevation="8dp"
+                  android:layout_alwaysShow="true"
+                  android:text="@string/noApplications"
+                  android:padding="32dp"
+                  android:gravity="center"
+                  android:visibility="gone"/>
+
     </LinearLayout>
 
-    <ListView
-        android:layout_width="match_parent"
-        android:layout_height="500dp"
-        android:id="@+id/resolver_list"
-        android:clipToPadding="false"
-        android:scrollbarStyle="outsideOverlay"
-        android:background="?attr/colorBackgroundFloating"
-        android:elevation="8dp"
-        android:nestedScrollingEnabled="true"
-        android:scrollIndicators="top|bottom"/>
-
-    <TextView android:id="@+id/empty"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:background="?attr/colorBackgroundFloating"
-              android:elevation="8dp"
-              android:layout_alwaysShow="true"
-              android:text="@string/noApplications"
-              android:padding="32dp"
-              android:gravity="center"
-              android:visibility="gone"/>
-
 </com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/layout-car/car_resolver_list_with_default.xml b/core/res/res/layout-car/car_resolver_list_with_default.xml
index 2aed00b..5e450b2 100644
--- a/core/res/res/layout-car/car_resolver_list_with_default.xml
+++ b/core/res/res/layout-car/car_resolver_list_with_default.xml
@@ -40,12 +40,12 @@
 
             <ImageView
                 android:id="@+id/icon"
-                android:layout_width="24dp"
-                android:layout_height="24dp"
+                android:layout_width="60dp"
+                android:layout_height="60dp"
                 android:layout_gravity="start|top"
-                android:layout_marginStart="16dp"
-                android:layout_marginEnd="16dp"
-                android:layout_marginTop="20dp"
+                android:layout_marginStart="10dp"
+                android:layout_marginEnd="5dp"
+                android:layout_marginTop="10dp"
                 android:src="@drawable/resolver_icon_placeholder"
                 android:scaleType="fitCenter"/>
 
@@ -55,7 +55,7 @@
                 android:layout_weight="1"
                 android:layout_height="?attr/listPreferredItemHeight"
                 android:layout_marginStart="16dp"
-                android:textAppearance="?attr/textAppearanceMedium"
+                android:textAppearance="?android:attr/textAppearanceLarge"
                 android:gravity="start|center_vertical"
                 android:paddingEnd="16dp"/>
 
@@ -120,7 +120,7 @@
                 android:layout_width="wrap_content"
                 android:layout_gravity="start"
                 android:maxLines="2"
-                style="?attr/buttonBarNegativeButtonStyle"
+                style="?attr/buttonBarButtonStyle"
                 android:minHeight="@dimen/alert_dialog_button_bar_height"
                 android:layout_height="wrap_content"
                 android:enabled="false"
@@ -133,29 +133,64 @@
                 android:layout_gravity="end"
                 android:maxLines="2"
                 android:minHeight="@dimen/alert_dialog_button_bar_height"
-                style="?attr/buttonBarPositiveButtonStyle"
+                style="?attr/buttonBarButtonStyle"
                 android:layout_height="wrap_content"
                 android:enabled="false"
                 android:text="@string/activity_resolver_use_always"
                 android:onClick="onButtonClick"/>
         </LinearLayout>
 
+        <FrameLayout
+            android:id="@+id/stub"
+            android:layout_alwaysShow="true"
+            android:visibility="gone"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:background="?attr/colorBackgroundFloating"/>
+
+        <TabHost
+            android:layout_alwaysShow="true"
+            android:id="@+id/profile_tabhost"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentTop="true"
+            android:layout_centerHorizontal="true"
+            android:background="?attr/colorBackgroundFloating">
+            <LinearLayout
+                android:orientation="vertical"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content">
+                <TabWidget
+                    android:id="@android:id/tabs"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:visibility="gone">
+                </TabWidget>
+                <View
+                    android:id="@+id/resolver_tab_divider"
+                    android:visibility="gone"
+                    android:layout_width="match_parent"
+                    android:layout_height="1dp"
+                    android:background="?attr/colorBackgroundFloating"
+                    android:foreground="?attr/dividerVertical"
+                    android:layout_marginBottom="8dp"/>
+                <FrameLayout
+                    android:id="@android:id/tabcontent"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content">
+                    <com.android.internal.app.ResolverViewPager
+                        android:id="@+id/profile_pager"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content">
+                    </com.android.internal.app.ResolverViewPager>
+                </FrameLayout>
+            </LinearLayout>
+        </TabHost>
+
         <View
             android:layout_width="match_parent"
             android:layout_height="1dp"
             android:background="?attr/dividerVertical"/>
-
-        <ListView
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:id="@+id/resolver_list"
-            android:layout_weight="4"
-            android:clipToPadding="false"
-            android:scrollbarStyle="outsideOverlay"
-            android:background="?attr/colorBackgroundFloating"
-            android:elevation="8dp"
-            android:nestedScrollingEnabled="true"
-            android:divider="@null"/>
     </LinearLayout>
 
 </com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/layout/conversation_face_pile_layout.xml b/core/res/res/layout/conversation_face_pile_layout.xml
new file mode 100644
index 0000000..1db3870
--- /dev/null
+++ b/core/res/res/layout/conversation_face_pile_layout.xml
@@ -0,0 +1,45 @@
+<?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
+  -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/conversation_face_pile"
+    android:layout_width="@dimen/conversation_avatar_size"
+    android:layout_height="@dimen/conversation_avatar_size"
+    android:forceHasOverlappingRendering="false"
+    >
+    <ImageView
+        android:id="@+id/conversation_face_pile_top"
+        android:layout_width="36dp"
+        android:layout_height="36dp"
+        android:scaleType="centerCrop"
+        android:layout_gravity="end|top"
+        />
+    <FrameLayout
+        android:id="@+id/conversation_face_pile_bottom_background"
+        android:layout_width="40dp"
+        android:layout_height="40dp"
+        android:layout_gravity="start|bottom"
+        android:background="@drawable/conversation_badge_background">
+        <ImageView
+            android:id="@+id/conversation_face_pile_bottom"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:scaleType="centerCrop"
+            android:layout_gravity="center"
+            />
+    </FrameLayout>
+</FrameLayout>
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index f5fa1b6a..6f36aae 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -91,7 +91,6 @@
         android:textAppearance="@style/TextAppearance.Material.Notification.Time"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_gravity="center"
         android:layout_marginStart="@dimen/notification_header_separating_margin"
         android:layout_marginEnd="@dimen/notification_header_separating_margin"
         android:showRelative="true"
diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml
new file mode 100644
index 0000000..dc52e97
--- /dev/null
+++ b/core/res/res/layout/notification_template_material_conversation.xml
@@ -0,0 +1,205 @@
+<?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
+  -->
+<com.android.internal.widget.ConversationLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/status_bar_latest_event_content"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:clipChildren="false"
+    android:tag="conversation"
+    android:theme="@style/Theme.DeviceDefault.Notification"
+    >
+
+    <FrameLayout
+        android:layout_width="@dimen/conversation_content_start"
+        android:layout_height="wrap_content"
+        android:gravity="start|top"
+        android:clipChildren="false"
+        android:clipToPadding="false"
+        android:paddingTop="12dp"
+    >
+
+        <FrameLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="top|center_horizontal"
+        >
+
+            <!-- Big icon: 52x52, 12dp padding left + top, 16dp padding right -->
+            <ImageView
+                android:id="@+id/conversation_icon"
+                android:layout_width="@dimen/conversation_avatar_size"
+                android:layout_height="@dimen/conversation_avatar_size"
+                android:scaleType="centerCrop"
+                android:importantForAccessibility="no"
+            />
+
+            <ViewStub
+                android:layout="@layout/conversation_face_pile_layout"
+                android:layout_width="@dimen/conversation_avatar_size"
+                android:layout_height="@dimen/conversation_avatar_size"
+                android:id="@+id/conversation_face_pile"
+                />
+
+            <FrameLayout
+                android:id="@+id/conversation_icon_badge"
+                android:layout_width="20dp"
+                android:layout_height="20dp"
+                android:layout_marginLeft="@dimen/conversation_badge_side_margin"
+                android:layout_marginTop="@dimen/conversation_badge_side_margin"
+                android:background="@drawable/conversation_badge_background" >
+                <!-- Badge: 20x20, 48dp padding left + top -->
+                <com.android.internal.widget.CachingIconView
+                    android:id="@+id/icon"
+                    android:layout_width="@dimen/conversation_icon_size_badged"
+                    android:layout_height="@dimen/conversation_icon_size_badged"
+                    android:layout_gravity="center"
+                />
+            </FrameLayout>
+        </FrameLayout>
+    </FrameLayout>
+
+    <!-- Wraps entire "expandable" notification -->
+    <com.android.internal.widget.RemeasuringLinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="top"
+        android:clipToPadding="false"
+        android:clipChildren="false"
+        android:orientation="vertical"
+        >
+        <!-- LinearLayout for Expand Button-->
+        <com.android.internal.widget.RemeasuringLinearLayout
+            android:id="@+id/expand_button_and_content_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:gravity="start|top"
+            android:orientation="horizontal"
+            android:clipChildren="false"
+            android:clipToPadding="false">
+            <!--TODO: move this into a separate layout and share logic with the header to bring back app opps etc-->
+            <FrameLayout
+                android:id="@+id/notification_action_list_margin_target"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1">
+
+                <!-- Header -->
+                <LinearLayout
+                    android:id="@+id/conversation_header"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal"
+                    android:paddingTop="16dp"
+                    android:paddingStart="@dimen/conversation_content_start"
+                >
+                    <TextView
+                        android:id="@+id/conversation_text"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginEnd="@dimen/notification_header_separating_margin"
+                        android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+                        android:textSize="16sp"
+                        android:singleLine="true"
+                        />
+
+                    <TextView
+                        android:id="@+id/time_divider"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:textAppearance="?attr/notificationHeaderTextAppearance"
+                        android:layout_marginStart="@dimen/notification_header_separating_margin"
+                        android:layout_marginEnd="@dimen/notification_header_separating_margin"
+                        android:text="@string/notification_header_divider_symbol"
+                        android:layout_gravity="center"
+                        android:paddingTop="1sp"
+                        android:singleLine="true"
+                        android:visibility="gone"
+                    />
+
+                    <DateTimeView
+                        android:id="@+id/time"
+                        android:textAppearance="@style/TextAppearance.Material.Notification.Time"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_gravity="center"
+                        android:layout_marginStart="@dimen/notification_header_separating_margin"
+                        android:paddingTop="1sp"
+                        android:showRelative="true"
+                        android:singleLine="true"
+                        android:visibility="gone"
+                    />
+
+                    <ImageView
+                        android:id="@+id/profile_badge"
+                        android:layout_width="@dimen/notification_badge_size"
+                        android:layout_height="@dimen/notification_badge_size"
+                        android:layout_gravity="center"
+                        android:layout_marginStart="4dp"
+                        android:paddingTop="2dp"
+                        android:scaleType="fitCenter"
+                        android:visibility="gone"
+                        android:contentDescription="@string/notification_work_profile_content_description"
+                    />
+                </LinearLayout>
+
+                <!-- Messages -->
+                <com.android.internal.widget.MessagingLinearLayout
+                    android:id="@+id/notification_messaging"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="40dp"
+                    android:spacing="@dimen/notification_messaging_spacing"
+                    android:clipToPadding="false"
+                    android:clipChildren="false"
+                    />
+            </FrameLayout>
+            <!-- Unread Count -->
+            <!-- <TextView /> -->
+
+            <!-- This is where the expand button will be placed when collapsed-->
+        </com.android.internal.widget.RemeasuringLinearLayout>
+
+        <include layout="@layout/notification_template_smart_reply_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/notification_content_margin"
+            android:layout_marginStart="@dimen/conversation_content_start"
+            android:layout_marginEnd="@dimen/notification_content_margin_end" />
+        <include layout="@layout/notification_material_action_list" />
+    </com.android.internal.widget.RemeasuringLinearLayout>
+
+    <!--This is dynamically placed between here and at the end of the layout-->
+    <FrameLayout
+        android:id="@+id/expand_button_container"
+        android:layout_width="wrap_content"
+        android:layout_height="@dimen/conversation_expand_button_expanded_size"
+        android:layout_gravity="end|top"
+        android:paddingStart="16dp"
+        android:paddingEnd="@dimen/notification_content_margin_end">
+        <com.android.internal.widget.NotificationExpandButton
+            android:id="@+id/expand_button"
+            android:layout_width="@dimen/notification_header_expand_icon_size"
+            android:layout_height="@dimen/notification_header_expand_icon_size"
+            android:layout_gravity="center"
+            android:drawable="@drawable/ic_expand_notification"
+            android:clickable="false"
+            android:importantForAccessibility="no"
+            />
+    </FrameLayout>
+</com.android.internal.widget.ConversationLayout>
diff --git a/core/res/res/layout/notification_template_messaging_group.xml b/core/res/res/layout/notification_template_messaging_group.xml
index 483b479..15146c0 100644
--- a/core/res/res/layout/notification_template_messaging_group.xml
+++ b/core/res/res/layout/notification_template_messaging_group.xml
@@ -20,14 +20,19 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="horizontal" >
-    <ImageView
-        android:id="@+id/message_icon"
-        android:layout_width="@dimen/messaging_avatar_size"
-        android:layout_height="@dimen/messaging_avatar_size"
-        android:layout_marginEnd="12dp"
-        android:scaleType="centerCrop"
-        android:importantForAccessibility="no" />
+    <FrameLayout
+        android:layout_width="@dimen/conversation_content_start"
+        android:layout_height="wrap_content">         <!--TODO: make sure to make this padding dynamic-->
+        <ImageView
+            android:layout_gravity="top|center_horizontal"
+            android:id="@+id/message_icon"
+            android:layout_width="@dimen/messaging_avatar_size"
+            android:layout_height="@dimen/messaging_avatar_size"
+            android:scaleType="centerCrop"
+            android:importantForAccessibility="no" />
+    </FrameLayout>
     <com.android.internal.widget.RemeasuringLinearLayout
+        android:id="@+id/messaging_group_content_container"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_weight="1"
@@ -43,7 +48,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginTop="@dimen/notification_text_margin_top"
-            android:spacing="2dp"/>
+            android:spacing="2dp" />
     </com.android.internal.widget.RemeasuringLinearLayout>
     <FrameLayout
         android:id="@+id/messaging_group_icon_container"
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index e17fc8a..b754e0c 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -100,8 +100,7 @@
                 android:layout_width="match_parent"
                 android:layout_height="1dp"
                 android:background="?attr/colorBackgroundFloating"
-                android:foreground="?attr/dividerVertical"
-                android:layout_marginBottom="@dimen/resolver_tab_divider_bottom_padding"/>
+                android:foreground="?attr/dividerVertical"/>
             <FrameLayout
                 android:id="@android:id/tabcontent"
                 android:layout_width="match_parent"
diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml
index 0d4523a..06a7633 100644
--- a/core/res/res/layout/resolver_list_with_default.xml
+++ b/core/res/res/layout/resolver_list_with_default.xml
@@ -183,8 +183,7 @@
                 android:layout_width="match_parent"
                 android:layout_height="1dp"
                 android:background="?attr/colorBackgroundFloating"
-                android:foreground="?attr/dividerVertical"
-                android:layout_marginBottom="@dimen/resolver_tab_divider_bottom_padding"/>
+                android:foreground="?attr/dividerVertical"/>
             <FrameLayout
                 android:id="@android:id/tabcontent"
                 android:layout_width="match_parent"
diff --git a/core/res/res/values-mcc219/config.xml b/core/res/res/values-mcc219/config.xml
deleted file mode 100644
index 7ae82fa..0000000
--- a/core/res/res/values-mcc219/config.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2009, 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources>
-    <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD -->
-    <string-array name="config_twoDigitNumberPattern">
-        <item>"92"</item>
-        <item>"93"</item>
-        <item>"94"</item>
-        <item>"95"</item>
-        <item>"96"</item>
-    </string-array>
-
-</resources>
diff --git a/core/res/res/values-mcc220/config.xml b/core/res/res/values-mcc220/config.xml
deleted file mode 100644
index 7ae82fa..0000000
--- a/core/res/res/values-mcc220/config.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2009, 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources>
-    <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD -->
-    <string-array name="config_twoDigitNumberPattern">
-        <item>"92"</item>
-        <item>"93"</item>
-        <item>"94"</item>
-        <item>"95"</item>
-        <item>"96"</item>
-    </string-array>
-
-</resources>
diff --git a/core/res/res/values-mcc310-mnc150/config.xml b/core/res/res/values-mcc310-mnc150/config.xml
deleted file mode 100644
index e7d1325..0000000
--- a/core/res/res/values-mcc310-mnc150/config.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string-array translatable="false" name="config_twoDigitNumberPattern">
-        <item>"0"</item>
-        <item>"00"</item>
-        <item>"*0"</item>
-        <item>"*1"</item>
-        <item>"*2"</item>
-        <item>"*3"</item>
-        <item>"*4"</item>
-        <item>"*5"</item>
-        <item>"*6"</item>
-        <item>"*7"</item>
-        <item>"*8"</item>
-        <item>"*9"</item>
-        <item>"#0"</item>
-        <item>"#1"</item>
-        <item>"#2"</item>
-        <item>"#3"</item>
-        <item>"#4"</item>
-        <item>"#5"</item>
-        <item>"#6"</item>
-        <item>"#7"</item>
-        <item>"#8"</item>
-        <item>"#9"</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc310-mnc410/config.xml b/core/res/res/values-mcc310-mnc410/config.xml
index 3fb3f0f..53e4193 100644
--- a/core/res/res/values-mcc310-mnc410/config.xml
+++ b/core/res/res/values-mcc310-mnc410/config.xml
@@ -23,31 +23,6 @@
     <!-- Configure mobile network MTU. Carrier specific value is set here.
     -->
     <integer name="config_mobile_mtu">1410</integer>
-    <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD -->
-    <string-array name="config_twoDigitNumberPattern">
-        <item>"0"</item>
-        <item>"00"</item>
-        <item>"*0"</item>
-        <item>"*1"</item>
-        <item>"*2"</item>
-        <item>"*3"</item>
-        <item>"*4"</item>
-        <item>"*5"</item>
-        <item>"*6"</item>
-        <item>"*7"</item>
-        <item>"*8"</item>
-        <item>"*9"</item>
-        <item>"#0"</item>
-        <item>"#1"</item>
-        <item>"#2"</item>
-        <item>"#3"</item>
-        <item>"#4"</item>
-        <item>"#5"</item>
-        <item>"#6"</item>
-        <item>"#7"</item>
-        <item>"#8"</item>
-        <item>"#9"</item>
-    </string-array>
 
     <!-- Enable 5 bar signal strength icon -->
     <bool name="config_inflateSignalStrength">true</bool>
diff --git a/core/res/res/values-mcc313-mnc100/config.xml b/core/res/res/values-mcc313-mnc100/config.xml
deleted file mode 100644
index ccd03f1..0000000
--- a/core/res/res/values-mcc313-mnc100/config.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string-array translatable="false" name="config_twoDigitNumberPattern">
-        <item>"0"</item>
-        <item>"00"</item>
-        <item>"*0"</item>
-        <item>"*1"</item>
-        <item>"*2"</item>
-        <item>"*3"</item>
-        <item>"*4"</item>
-        <item>"*5"</item>
-        <item>"*6"</item>
-        <item>"*7"</item>
-        <item>"*8"</item>
-        <item>"*9"</item>
-        <item>"#0"</item>
-        <item>"#1"</item>
-        <item>"#2"</item>
-        <item>"#3"</item>
-        <item>"#4"</item>
-        <item>"#5"</item>
-        <item>"#6"</item>
-        <item>"#7"</item>
-        <item>"#8"</item>
-        <item>"#9"</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc334-mnc050/config.xml b/core/res/res/values-mcc334-mnc050/config.xml
index 23678f1..2e8c504 100644
--- a/core/res/res/values-mcc334-mnc050/config.xml
+++ b/core/res/res/values-mcc334-mnc050/config.xml
@@ -31,8 +31,4 @@
       <item>9</item>
     </integer-array>
 
-    <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD -->
-    <string-array translatable="false" name="config_twoDigitNumberPattern">
-        <item>"#9"</item>
-    </string-array>
 </resources>
diff --git a/core/res/res/values-mcc334-mnc090/config.xml b/core/res/res/values-mcc334-mnc090/config.xml
deleted file mode 100644
index 1632a42..0000000
--- a/core/res/res/values-mcc334-mnc090/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2017, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD -->
-
-    <string-array translatable="false" name="config_twoDigitNumberPattern">
-        <item>"#9"</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc704-mnc01/config.xml b/core/res/res/values-mcc704-mnc01/config.xml
deleted file mode 100644
index 10b6470..0000000
--- a/core/res/res/values-mcc704-mnc01/config.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2016, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You my obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<resources>
-    <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD -->
-    <string-array name="config_twoDigitNumberPattern">
-        <item>"*1"</item>
-        <item>"*5"</item>
-        <item>"*9"</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc708-mnc001/config.xml b/core/res/res/values-mcc708-mnc001/config.xml
deleted file mode 100755
index 7b7c48d..0000000
--- a/core/res/res/values-mcc708-mnc001/config.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2016, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources>
-    <string-array translatable="false" name="config_twoDigitNumberPattern">
-        <item>"*1"</item>
-        <item>"*5"</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index c245131..cb88aee 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1541,6 +1541,28 @@
         <flag name="microphone" value="0x80" />
     </attr>
 
+    <!-- Enable sampled memory bug detection in this process.
+         When enabled, a very small, random subset of native
+         memory allocations are protected with guard pages, providing an
+         ASan-like error report in case of a memory corruption bug.
+
+         GWP-ASan is a recursive acronym. It stands for “GWP-ASan Will Provide Allocation SANity”.
+         See the <a href="http://llvm.org/docs/GwpAsan.html">LLVM documentation</a>
+         for more information about this feature.
+
+         <p>This tag can be applied to any tag that allows
+         {@link android.R.styleable#AndroidManifestProcess process} tag:
+         {@link android.R.styleable#AndroidManifestApplication application} tag (to supply
+         a default setting for all application components), or with the
+         {@link android.R.styleable#AndroidManifestProvider provider},
+         {@link android.R.styleable#AndroidManifestService service},
+         {@link android.R.styleable#AndroidManifestReceiver receiver},
+         {@link android.R.styleable#AndroidManifestActivity activity} tag.
+         When multiple components run in the same process,
+         the first component to start determines the behavior of the entire process.
+
+         The default value is {@code false}. -->
+    <attr name="enableGwpAsan" format="boolean" />
 
     <!-- The <code>manifest</code> tag is the root of an
          <code>AndroidManifest.xml</code> file,
@@ -1552,7 +1574,7 @@
          <code>com.google.app.<em>appname</em></code>
 
          <p>Inside of the manifest tag, may appear the following tags
-         in any order: {@link #AndroidManifestFeature feature},
+         in any order: {@link #AndroidManifestAttribution attribution},
          {@link #AndroidManifestPermission permission},
          {@link #AndroidManifestPermissionGroup permission-group},
          {@link #AndroidManifestPermissionTree permission-tree},
@@ -1806,31 +1828,38 @@
 
              The default value is {@code true}. -->
         <attr name="allowNativeHeapPointerTagging" format="boolean" />
+
+        <attr name="enableGwpAsan" />
+
     </declare-styleable>
 
-    <!-- The <code>feature</code> tag declares a feature. A feature is a logical part of an app.
-    E.g. photo sharing app might include a direct messaging component. To tag certain code as
-    belonging to a feature, use a context created via
-    {@link android.content.Context#createFeatureContext(String)} for any interaction with the
+    <!-- An attribution is a logical part of an app and is identified by a tag.
+    E.g. a photo sharing app might include a direct messaging component. To tag certain code as
+    belonging to an attribution, use a context created via
+    {@link android.content.Context#createAttributionContext(String)} for any interaction with the
     system.
 
     <p>This appears as a child tag of the root {@link #AndroidManifest manifest} tag.
 
-    <p>In case this feature inherits from another feature, this tag can contain one or multiple
-    {@link #AndroidManifestFeatureInheritFrom inherit-from} tags. -->
-    <declare-styleable name="AndroidManifestFeature" parent="AndroidManifest">
-        <!-- Required identifier for a feature. Can be passed to
-        {@link android.content.Context#createFeatureContext} to create a context for this feature
-        -->
+    <p>In case this attribution inherits from another attribution, this tag can contain one or
+    multiple {@link #AndroidManifestAttributionInheritFrom inherit-from} tags. -->
+    <declare-styleable name="AndroidManifestAttribution" parent="AndroidManifest">
+        <!-- TODO moltmann: Remove -->
         <attr name="featureId" format="string" />
-        <!-- Required user visible label for a feature. -->
+        <!-- Required identifier for a attribution. Can be passed to
+        {@link android.content.Context#createAttributionContext} to create a context tagged with
+        this attribution
+        -->
+        <attr name="tag" format="string" />
+        <!-- Required user visible label for a attribution. -->
         <attr name="label" format="string" />
     </declare-styleable>
 
     <!-- Declares previously declared features this feature inherits from. -->
-    <declare-styleable name="AndroidManifestFeatureInheritFrom" parent="AndroidManifestFeature">
-        <!-- Identifier of the feature this feature inherits from -->
-        <attr name="featureId" format="string" />
+    <declare-styleable name="AndroidManifestAttributionInheritFrom"
+                       parent="AndroidManifestAttribution">
+        <!-- Identifier of the attribution this attribution inherits from -->
+        <attr name="tag" format="string" />
     </declare-styleable>
 
     <!-- The <code>permission</code> tag declares a security permission that can be
@@ -2312,6 +2341,7 @@
     <declare-styleable name="AndroidManifestProcess" parent="AndroidManifestProcesses">
         <!-- Required name of the process that is allowed -->
         <attr name="process" />
+        <attr name="enableGwpAsan" />
     </declare-styleable>
 
     <!-- The <code>deny-permission</code> tag specifies that a permission is to be denied
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0b6e65fe..6f468e0 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2024,7 +2024,10 @@
          a keyboard is present. -->
     <bool name="config_showMenuShortcutsWhenKeyboardPresent">false</bool>
 
-    <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD -->
+    <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD.
+
+         Note: This config is deprecated, please use carrier config which is
+               CarrierConfigManager.KEY_MMI_TWO_DIGIT_NUMBER_PATTERN_STRING_ARRAY instead. -->
     <string-array name="config_twoDigitNumberPattern" translatable="false">
     </string-array>
 
@@ -4415,4 +4418,7 @@
 
     <!-- Package name of custom session policy provider class used by MediaSessionService. -->
     <string name="config_customSessionPolicyProvider"></string>
+
+    <!-- The max scale for the wallpaper when it's zoomed in -->
+    <item name="config_wallpaperMaxScale" format="float" type="dimen">1</item>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 9118617..e3fe982 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -682,7 +682,25 @@
     <!-- The size of the right icon image when on low ram -->
     <dimen name="notification_right_icon_size_low_ram">@dimen/notification_right_icon_size</dimen>
 
-    <dimen name="messaging_avatar_size">52dp</dimen>
+    <dimen name="messaging_avatar_size">@dimen/notification_right_icon_size</dimen>
+    <dimen name="conversation_avatar_size">52dp</dimen>
+    <!-- Start of the content in the conversation template -->
+    <dimen name="conversation_content_start">80dp</dimen>
+    <!-- Size of the expand button when expanded -->
+    <dimen name="conversation_expand_button_expanded_size">80dp</dimen>
+    <!-- Top margin of the expand button for conversations when expanded -->
+    <dimen name="conversation_expand_button_top_margin_expanded">18dp</dimen>
+    <!-- Side margins of the conversation badge in relation to the conversation icon -->
+    <dimen name="conversation_badge_side_margin">36dp</dimen>
+    <!-- size of the notification icon when badged in a conversation -->
+    <dimen name="conversation_icon_size_badged">15dp</dimen>
+    <!-- size of the notification icon when centered in a conversation -->
+    <dimen name="conversation_icon_size_centered">20dp</dimen>
+    <!-- margin on the top when the icon is centered for group conversations -->
+    <dimen name="conversation_icon_margin_top_centered">5dp</dimen>
+
+    <!-- Padding between text and sender when singleline -->
+    <dimen name="messaging_group_singleline_sender_padding_end">4dp</dimen>
 
     <dimen name="messaging_group_sending_progress_size">24dp</dimen>
 
@@ -776,7 +794,6 @@
     <dimen name="resolver_empty_state_height_with_tabs">268dp</dimen>
     <dimen name="resolver_max_collapsed_height">192dp</dimen>
     <dimen name="resolver_max_collapsed_height_with_tabs">248dp</dimen>
-    <dimen name="resolver_tab_divider_bottom_padding">8dp</dimen>
 
     <dimen name="chooser_action_button_icon_size">18dp</dimen>
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 39e8197..96807cc 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3004,6 +3004,7 @@
       <public name="animatedImageDrawable"/>
       <public name="htmlDescription"/>
       <public name="preferMinimalPostProcessing"/>
+      <!-- @removed -->
       <public name="featureId" />
       <public name="supportsInlineSuggestions" />
       <public name="crossProfile" />
@@ -3015,6 +3016,7 @@
       <public name="allowNativeHeapPointerTagging" />
       <public name="preserveLegacyExternalStorage" />
       <public name="mimeGroup" />
+      <public name="enableGwpAsan" />
     </public-group>
 
     <public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4b49dd3..f101f59 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1217,6 +1217,14 @@
         device. This includes information such as call numbers for calls and the state of the
         calls.</string>
 
+    <!-- Title of an application permission. When granted the app is exempt from audio record
+    restrictions.
+    [CHAR LIMIT=NONE]-->
+    <string name="permlab_exemptFromAudioRecordRestrictions">exempt from audio record restrictions</string>
+    <!-- Description of an application permission. When granted the app is exempt from audio record
+    restrictions. [CHAR LIMIT=NONE]-->
+    <string name="permdesc_exemptFromAudioRecordRestrictions">Exempt the app from restrictions to record audio.</string>
+
     <!-- Title of an application permission.  When granted the user is giving access to a third
          party app to continue a call which originated in another app.  For example, the user
          could be in a voice call over their carrier's mobile network, and a third party video
@@ -5432,6 +5440,15 @@
     <string name="as_app_forced_to_restricted_bucket">
         <xliff:g id="package_name" example="com.android.example">%1$s</xliff:g> has been put into the RESTRICTED bucket</string>
 
+    <!-- The way a conversation name is displayed when single line. The text will be displayed to the end of this text with some spacing -->
+    <string name="conversation_single_line_name_display"><xliff:g id="sender_name" example="Sara">%1$s</xliff:g>:</string>
+
+    <!-- Conversation Title fallback if the there is no name provided in a 1:1 conversation [CHAR LIMIT=40]-->
+    <string name="conversation_title_fallback_one_to_one">Conversation</string>
+
+    <!-- Conversation Title fallback if the there is no name provided in a group chat conversation [CHAR LIMIT=40]-->
+    <string name="conversation_title_fallback_group_chat">Group Conversation</string>
+
     <!-- ResolverActivity - profile tabs -->
     <!-- Label of a tab on a screen. A user can tap this tap to switch to the 'Personal' view (that shows their personal content) if they have a work profile on their device. [CHAR LIMIT=NONE] -->
     <string name="resolver_personal_tab">Personal</string>
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 966f495..64768cf 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -146,7 +146,7 @@
         <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification</item>
     </style>
     <style name="Widget.DeviceDefault.Notification.MessagingName" parent="Widget.Material.Notification.MessagingName">
-        <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification.MessagingName</item>
+        <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification.Title</item>
     </style>
     <style name="Widget.DeviceDefault.PreferenceFrameLayout" parent="Widget.Material.PreferenceFrameLayout"/>
     <style name="Widget.DeviceDefault.ProgressBar.Inverse" parent="Widget.Material.ProgressBar.Inverse"/>
@@ -290,9 +290,6 @@
     <style name="TextAppearance.DeviceDefault.Notification.Title" parent="TextAppearance.Material.Notification.Title">
         <item name="fontFamily">@string/config_headlineFontFamilyMedium</item>
     </style>
-    <style name="TextAppearance.DeviceDefault.Notification.MessagingName" parent="TextAppearance.DeviceDefault.Notification.Title">
-        <item name="textSize">16sp</item>
-    </style>
     <style name="TextAppearance.DeviceDefault.Notification.Reply" parent="TextAppearance.Material.Notification.Reply">
         <item name="fontFamily">@string/config_bodyFontFamily</item>
     </style>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 63ac0e6..2415837 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -504,7 +504,7 @@
     <style name="Widget.Material.Notification.MessagingText" parent="Widget.Material.Notification.Text">
         <item name="layout_width">wrap_content</item>
         <item name="layout_height">wrap_content</item>
-        <item name="ellipsize">end</item>z
+        <item name="ellipsize">end</item>
     </style>
 
     <style name="Widget.Material.Notification.MessagingName" parent="Widget.Material.Light.TextView">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 826379d..49a0f17 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3876,6 +3876,30 @@
   <java-symbol type="array" name="config_defaultImperceptibleKillingExemptionPkgs" />
   <java-symbol type="array" name="config_defaultImperceptibleKillingExemptionProcStates" />
 
+  <java-symbol type="string" name="conversation_single_line_name_display" />
+  <java-symbol type="string" name="conversation_title_fallback_one_to_one" />
+  <java-symbol type="string" name="conversation_title_fallback_group_chat" />
+  <java-symbol type="id" name="conversation_icon" />
+  <java-symbol type="id" name="conversation_icon_badge" />
+  <java-symbol type="id" name="expand_button_container" />
+  <java-symbol type="id" name="messaging_group_content_container" />
+  <java-symbol type="id" name="expand_button_and_content_container" />
+  <java-symbol type="id" name="conversation_header" />
+  <java-symbol type="id" name="conversation_face_pile_bottom_background" />
+  <java-symbol type="id" name="conversation_face_pile_bottom" />
+  <java-symbol type="id" name="conversation_face_pile_top" />
+  <java-symbol type="id" name="conversation_face_pile" />
+  <java-symbol type="id" name="conversation_text" />
+  <java-symbol type="dimen" name="conversation_expand_button_top_margin_expanded" />
+  <java-symbol type="dimen" name="conversation_expand_button_expanded_size" />
+  <java-symbol type="dimen" name="messaging_group_singleline_sender_padding_end" />
+  <java-symbol type="dimen" name="conversation_badge_side_margin" />
+  <java-symbol type="dimen" name="conversation_icon_size_badged" />
+  <java-symbol type="dimen" name="conversation_icon_size_centered" />
+  <java-symbol type="dimen" name="conversation_icon_margin_top_centered" />
+  <java-symbol type="layout" name="notification_template_material_conversation" />
+  <java-symbol type="layout" name="conversation_face_pile_layout" />
+
   <!-- Intent resolver and share sheet -->
   <java-symbol type="color" name="resolver_tabs_active_color" />
   <java-symbol type="color" name="resolver_tabs_inactive_color" />
@@ -3909,7 +3933,6 @@
   <java-symbol type="dimen" name="resolver_empty_state_height_with_tabs" />
   <java-symbol type="dimen" name="resolver_max_collapsed_height_with_tabs" />
   <java-symbol type="bool" name="sharesheet_show_content_preview" />
-  <java-symbol type="dimen" name="resolver_tab_divider_bottom_padding" />
 
   <!-- Toast message for background started foreground service while-in-use permission restriction feature -->
   <java-symbol type="string" name="allow_while_in_use_permission_in_fgs" />
@@ -3927,4 +3950,6 @@
 
   <java-symbol type="string" name="config_customMediaKeyDispatcher" />
   <java-symbol type="string" name="config_customSessionPolicyProvider" />
+  <!-- The max scale for the wallpaper when it's zoomed in -->
+  <java-symbol type="dimen" name="config_wallpaperMaxScale"/>
 </resources>
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index 50cd5a3..fe25e79 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -123,7 +123,7 @@
         mController = new InsetsAnimationControlImpl(controls,
                 new Rect(0, 0, 500, 500), mInsetsState, mMockListener, systemBars(),
                 mMockController, 10 /* durationMs */, new LinearInterpolator(),
-                false /* fade */, LAYOUT_INSETS_DURING_ANIMATION_SHOWN, 0 /* animationType */);
+                false /* fade */, 0 /* animationType */);
     }
 
     @Test
@@ -182,7 +182,7 @@
 
     @Test
     public void testCancelled() {
-        mController.onCancelled();
+        mController.cancel();
         try {
             mController.setInsetsAndAlpha(Insets.NONE, 1f /*alpha */, 0f /* fraction */);
             fail("Expected exception to be thrown");
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 7737b1a..f1ab8dd 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.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -47,8 +48,10 @@
 import android.graphics.Rect;
 import android.os.CancellationSignal;
 import android.platform.test.annotations.Presubmit;
+import android.view.InsetsState.InternalInsetsType;
 import android.view.SurfaceControl.Transaction;
 import android.view.WindowInsets.Type;
+import android.view.WindowInsetsController.OnControllableInsetsChangedListener;
 import android.view.WindowManager.BadTokenException;
 import android.view.WindowManager.LayoutParams;
 import android.view.animation.LinearInterpolator;
@@ -67,6 +70,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mockito;
 
 import java.util.concurrent.CountDownLatch;
 import java.util.function.Supplier;
@@ -166,28 +171,30 @@
 
     @Test
     public void testControlsChanged() {
-        InsetsSourceControl control =
-                new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
-        mController.onControlsChanged(new InsetsSourceControl[] { control });
-        assertEquals(mLeash,
-                mController.getSourceConsumer(ITYPE_STATUS_BAR).getControl().getLeash());
+        mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
+        assertNotNull(mController.getSourceConsumer(ITYPE_STATUS_BAR).getControl().getLeash());
+        mController.addOnControllableInsetsChangedListener(
+                ((controller, typeMask) -> assertEquals(statusBars(), typeMask)));
     }
 
     @Test
     public void testControlsRevoked() {
-        InsetsSourceControl control =
-                new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
-        mController.onControlsChanged(new InsetsSourceControl[] { control });
+        OnControllableInsetsChangedListener listener
+                = mock(OnControllableInsetsChangedListener.class);
+        mController.addOnControllableInsetsChangedListener(listener);
+        mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
         mController.onControlsChanged(new InsetsSourceControl[0]);
         assertNull(mController.getSourceConsumer(ITYPE_STATUS_BAR).getControl());
+        InOrder inOrder = Mockito.inOrder(listener);
+        inOrder.verify(listener).onControllableInsetsChanged(eq(mController), eq(0));
+        inOrder.verify(listener).onControllableInsetsChanged(eq(mController), eq(statusBars()));
+        inOrder.verify(listener).onControllableInsetsChanged(eq(mController), eq(0));
     }
 
     @Test
     public void testControlsRevoked_duringAnim() {
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
-            InsetsSourceControl control =
-                    new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
-            mController.onControlsChanged(new InsetsSourceControl[] { control });
+            mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
 
             WindowInsetsAnimationControlListener mockListener =
                     mock(WindowInsetsAnimationControlListener.class);
@@ -206,10 +213,15 @@
     public void testFrameDoesntMatchDisplay() {
         mController.onFrameChanged(new Rect(0, 0, 100, 100));
         mController.getState().setDisplayFrame(new Rect(0, 0, 200, 200));
+        InsetsSourceControl control =
+                new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
+        mController.onControlsChanged(new InsetsSourceControl[] { control });
         WindowInsetsAnimationControlListener controlListener =
                 mock(WindowInsetsAnimationControlListener.class);
         mController.controlWindowInsetsAnimation(0, 0 /* durationMs */, new LinearInterpolator(),
                 controlListener);
+        mController.addOnControllableInsetsChangedListener(
+                (controller, typeMask) -> assertEquals(0, typeMask));
         verify(controlListener).onCancelled();
         verify(controlListener, never()).onReady(any(), anyInt());
     }
@@ -245,11 +257,8 @@
 
     @Test
     public void testApplyImeVisibility() {
-        final InsetsSourceControl ime = new InsetsSourceControl(ITYPE_IME, mLeash, new Point());
-
-        InsetsSourceControl[] controls = new InsetsSourceControl[3];
-        controls[0] = ime;
-        mController.onControlsChanged(controls);
+        InsetsSourceControl ime = createControl(ITYPE_IME);
+        mController.onControlsChanged(new InsetsSourceControl[] { ime });
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained();
             mController.applyImeVisibility(true);
@@ -412,9 +421,7 @@
 
     @Test
     public void testRestoreStartsAnimation() {
-        InsetsSourceControl control =
-                new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
-        mController.onControlsChanged(new InsetsSourceControl[]{control});
+        mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             mController.hide(Type.statusBars());
@@ -431,7 +438,7 @@
             assertTrue(mController.getState().getSource(ITYPE_STATUS_BAR).isVisible());
 
             // Gaining control
-            mController.onControlsChanged(new InsetsSourceControl[]{control});
+            mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
             assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_STATUS_BAR));
             mController.cancelExistingAnimation();
             assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible());
@@ -442,8 +449,6 @@
 
     @Test
     public void testStartImeAnimationAfterGettingControl() {
-        InsetsSourceControl control =
-                new InsetsSourceControl(ITYPE_IME, mLeash, new Point());
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
 
@@ -454,7 +459,7 @@
             mController.show(ime(), true /* fromIme */);
 
             // Gaining control shortly after
-            mController.onControlsChanged(new InsetsSourceControl[]{control});
+            mController.onControlsChanged(createSingletonControl(ITYPE_IME));
 
             assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_IME));
             mController.cancelExistingAnimation();
@@ -466,16 +471,13 @@
 
     @Test
     public void testStartImeAnimationAfterGettingControl_imeLater() {
-        InsetsSourceControl control =
-                new InsetsSourceControl(ITYPE_IME, mLeash, new Point());
-
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
 
             mController.show(ime());
             assertFalse(mController.getState().getSource(ITYPE_IME).isVisible());
 
             // Gaining control shortly after
-            mController.onControlsChanged(new InsetsSourceControl[]{control});
+            mController.onControlsChanged(createSingletonControl(ITYPE_IME));
 
             // Pretend IME is calling
             mController.show(ime(), true /* fromIme */);
@@ -490,9 +492,7 @@
 
     @Test
     public void testAnimationEndState_controller() throws Exception {
-        InsetsSourceControl control =
-                new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
-        mController.onControlsChanged(new InsetsSourceControl[] { control });
+        mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             WindowInsetsAnimationControlListener mockListener =
@@ -518,9 +518,7 @@
 
     @Test
     public void testCancellation_afterGainingControl() throws Exception {
-        InsetsSourceControl control =
-                new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
-        mController.onControlsChanged(new InsetsSourceControl[] { control });
+        mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             WindowInsetsAnimationControlListener mockListener =
@@ -638,12 +636,23 @@
         latch.await();
     }
 
+    private InsetsSourceControl createControl(@InternalInsetsType int type) {
+
+        // Simulate binder behavior by copying SurfaceControl. Otherwise, InsetsController will
+        // attempt to release mLeash directly.
+        SurfaceControl copy = new SurfaceControl();
+        copy.copyFrom(mLeash);
+        return new InsetsSourceControl(type, copy, new Point());
+    }
+
+    private InsetsSourceControl[] createSingletonControl(@InternalInsetsType int type) {
+        return new InsetsSourceControl[] { createControl(type) };
+    }
+
     private InsetsSourceControl[] prepareControls() {
-        final InsetsSourceControl navBar = new InsetsSourceControl(ITYPE_NAVIGATION_BAR, mLeash,
-                new Point());
-        final InsetsSourceControl statusBar = new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash,
-                new Point());
-        final InsetsSourceControl ime = new InsetsSourceControl(ITYPE_IME, mLeash, new Point());
+        final InsetsSourceControl navBar = createControl(ITYPE_NAVIGATION_BAR);
+        final InsetsSourceControl statusBar = createControl(ITYPE_STATUS_BAR);
+        final InsetsSourceControl ime = createControl(ITYPE_IME);
 
         InsetsSourceControl[] controls = new InsetsSourceControl[3];
         controls[0] = navBar;
diff --git a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
index 9787b77..9797178 100644
--- a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
@@ -25,12 +25,14 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.os.CancellationSignal;
 import android.platform.test.annotations.Presubmit;
+import android.view.WindowInsetsController.OnControllableInsetsChangedListener;
 import android.view.animation.LinearInterpolator;
 
 import org.junit.Before;
@@ -163,11 +165,43 @@
     }
 
     @Test
+    public void testAddOnControllableInsetsChangedListener() {
+        OnControllableInsetsChangedListener listener =
+                mock(OnControllableInsetsChangedListener.class);
+        mPendingInsetsController.addOnControllableInsetsChangedListener(listener);
+        mPendingInsetsController.replayAndAttach(mReplayedController);
+        verify(mReplayedController).addOnControllableInsetsChangedListener(eq(listener));
+        verify(listener).onControllableInsetsChanged(eq(mPendingInsetsController), eq(0));
+    }
+
+    @Test
+    public void testAddRemoveControllableInsetsChangedListener() {
+        OnControllableInsetsChangedListener listener =
+                mock(OnControllableInsetsChangedListener.class);
+        mPendingInsetsController.addOnControllableInsetsChangedListener(listener);
+        mPendingInsetsController.removeOnControllableInsetsChangedListener(listener);
+        mPendingInsetsController.replayAndAttach(mReplayedController);
+        verify(mReplayedController, never()).addOnControllableInsetsChangedListener(any());
+        verify(listener).onControllableInsetsChanged(eq(mPendingInsetsController), eq(0));
+    }
+
+    @Test
+    public void testAddOnControllableInsetsChangedListener_direct() {
+        mPendingInsetsController.replayAndAttach(mReplayedController);
+        OnControllableInsetsChangedListener listener =
+                mock(OnControllableInsetsChangedListener.class);
+        mPendingInsetsController.addOnControllableInsetsChangedListener(listener);
+        verify(mReplayedController).addOnControllableInsetsChangedListener(eq(listener));
+    }
+
+    @Test
     public void testReplayTwice() {
         mPendingInsetsController.show(systemBars());
         mPendingInsetsController.setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
         mPendingInsetsController.setSystemBarsAppearance(APPEARANCE_LIGHT_STATUS_BARS,
                 APPEARANCE_LIGHT_STATUS_BARS);
+        mPendingInsetsController.addOnControllableInsetsChangedListener(
+                (controller, typeMask) -> {});
         mPendingInsetsController.replayAndAttach(mReplayedController);
         InsetsController secondController = mock(InsetsController.class);
         mPendingInsetsController.replayAndAttach(secondController);
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
index 02a88fc..5ea0718 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -20,6 +20,7 @@
 
 import static org.testng.Assert.assertThrows;
 
+import android.graphics.Insets;
 import android.view.View;
 import android.view.ViewStructure;
 import android.view.autofill.AutofillId;
@@ -172,6 +173,11 @@
         }
 
         @Override
+        void internalNotifyViewInsetsChanged(Insets viewInsets) {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
         public void updateContentCaptureContext(ContentCaptureContext context) {
             throw new UnsupportedOperationException("should not have been called");
         }
diff --git a/core/tests/coretests/src/android/view/textclassifier/SystemTextClassifierMetadataTest.java b/core/tests/coretests/src/android/view/textclassifier/SystemTextClassifierMetadataTest.java
new file mode 100644
index 0000000..e4cfc53
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/SystemTextClassifierMetadataTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SystemTextClassifierMetadataTest {
+
+    @Test
+    public void testInvalidPackageNameThrowsException() {
+        assertThrows(NullPointerException.class,
+                () -> new SystemTextClassifierMetadata(/* packageName= */ null, /* userId= */
+                        1, /* useDefaultTextClassifier= */ false));
+    }
+
+    @Test
+    public void testParcel() {
+        SystemTextClassifierMetadata sysTcMetadata = new SystemTextClassifierMetadata(
+                "package", /* userId= */ 1, /* useDefaultTextClassifier= */ false);
+
+        Parcel p = Parcel.obtain();
+        sysTcMetadata.writeToParcel(p, 0);
+        p.setDataPosition(0);
+
+        SystemTextClassifierMetadata targetSysTcMetadata =
+                SystemTextClassifierMetadata.CREATOR.createFromParcel(p);
+
+        assertThat(targetSysTcMetadata.getUserId()).isEqualTo(sysTcMetadata.getUserId());
+        assertThat(targetSysTcMetadata.getCallingPackageName()).isEqualTo(
+                sysTcMetadata.getCallingPackageName());
+        assertThat(targetSysTcMetadata.useDefaultTextClassifier()).isEqualTo(
+                sysTcMetadata.useDefaultTextClassifier());
+    }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
index d544029..39ededa 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
@@ -17,6 +17,7 @@
 package android.view.textclassifier;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 
@@ -199,7 +200,9 @@
                         .setReferenceTime(referenceTime)
                         .setExtras(BUNDLE)
                         .build();
-        reference.setCallingPackageName(packageName);
+        final SystemTextClassifierMetadata systemTcMetadata =
+                new SystemTextClassifierMetadata(packageName, 1, false);
+        reference.setSystemTextClassifierMetadata(systemTcMetadata);
 
         // Parcel and unparcel.
         final Parcel parcel = Parcel.obtain();
@@ -216,5 +219,11 @@
         assertEquals(referenceTime, result.getReferenceTime());
         assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
         assertEquals(packageName, result.getCallingPackageName());
+        final SystemTextClassifierMetadata resultSystemTcMetadata =
+                result.getSystemTextClassifierMetadata();
+        assertNotNull(resultSystemTcMetadata);
+        assertEquals(packageName, resultSystemTcMetadata.getCallingPackageName());
+        assertEquals(1, resultSystemTcMetadata.getUserId());
+        assertFalse(resultSystemTcMetadata.useDefaultTextClassifier());
     }
 }
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
index d0d32e3..31f8029 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
@@ -17,6 +17,8 @@
 package android.view.textclassifier;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 
 import android.icu.util.ULocale;
 import android.os.Bundle;
@@ -75,7 +77,9 @@
         final TextLanguage.Request reference = new TextLanguage.Request.Builder(text)
                 .setExtras(bundle)
                 .build();
-        reference.setCallingPackageName(packageName);
+        final SystemTextClassifierMetadata systemTcMetadata =
+                new SystemTextClassifierMetadata(packageName, 1, false);
+        reference.setSystemTextClassifierMetadata(systemTcMetadata);
 
         final Parcel parcel = Parcel.obtain();
         reference.writeToParcel(parcel, 0);
@@ -85,5 +89,11 @@
         assertEquals(text, result.getText());
         assertEquals("bundle", result.getExtras().getString(bundleKey));
         assertEquals(packageName, result.getCallingPackageName());
+        final SystemTextClassifierMetadata resultSystemTcMetadata =
+                result.getSystemTextClassifierMetadata();
+        assertNotNull(resultSystemTcMetadata);
+        assertEquals(packageName, resultSystemTcMetadata.getCallingPackageName());
+        assertEquals(1, resultSystemTcMetadata.getUserId());
+        assertFalse(resultSystemTcMetadata.useDefaultTextClassifier());
     }
 }
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
index ec5813f..4f0b44b 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
@@ -17,6 +17,8 @@
 package android.view.textclassifier;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 
 import android.os.Bundle;
 import android.os.LocaleList;
@@ -115,7 +117,9 @@
                 .setExtras(BUNDLE)
                 .setReferenceTime(referenceTime)
                 .build();
-        reference.setCallingPackageName(packageName);
+        final SystemTextClassifierMetadata systemTcMetadata =
+                new SystemTextClassifierMetadata(packageName, 1, false);
+        reference.setSystemTextClassifierMetadata(systemTcMetadata);
 
         // Parcel and unparcel.
         final Parcel parcel = Parcel.obtain();
@@ -132,5 +136,11 @@
         assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
         assertEquals(packageName, result.getCallingPackageName());
         assertEquals(referenceTime, result.getReferenceTime());
+        final SystemTextClassifierMetadata resultSystemTcMetadata =
+                result.getSystemTextClassifierMetadata();
+        assertNotNull(resultSystemTcMetadata);
+        assertEquals(packageName, resultSystemTcMetadata.getCallingPackageName());
+        assertEquals(1, resultSystemTcMetadata.getUserId());
+        assertFalse(resultSystemTcMetadata.useDefaultTextClassifier());
     }
 }
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
index 30cc4e8..82788c8 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
@@ -17,6 +17,8 @@
 package android.view.textclassifier;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 
 import android.os.Bundle;
 import android.os.LocaleList;
@@ -82,7 +84,9 @@
                         .setDefaultLocales(new LocaleList(Locale.US, Locale.GERMANY))
                         .setExtras(BUNDLE)
                         .build();
-        reference.setCallingPackageName(packageName);
+        final SystemTextClassifierMetadata systemTcMetadata =
+                new SystemTextClassifierMetadata(packageName, 1, false);
+        reference.setSystemTextClassifierMetadata(systemTcMetadata);
 
         // Parcel and unparcel.
         final Parcel parcel = Parcel.obtain();
@@ -96,5 +100,11 @@
         assertEquals("en-US,de-DE", result.getDefaultLocales().toLanguageTags());
         assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
         assertEquals(packageName, result.getCallingPackageName());
+        final SystemTextClassifierMetadata resultSystemTcMetadata =
+                result.getSystemTextClassifierMetadata();
+        assertNotNull(resultSystemTcMetadata);
+        assertEquals(packageName, resultSystemTcMetadata.getCallingPackageName());
+        assertEquals(1, resultSystemTcMetadata.getUserId());
+        assertFalse(resultSystemTcMetadata.useDefaultTextClassifier());
     }
 }
diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml
index 61281ee..efe658a 100644
--- a/data/etc/car/com.google.android.car.kitchensink.xml
+++ b/data/etc/car/com.google.android.car.kitchensink.xml
@@ -33,6 +33,7 @@
         <permission name="android.permission.MODIFY_AUDIO_ROUTING"/>
         <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
         <permission name="android.permission.MODIFY_PHONE_STATE"/>
+        <permission name="android.permission.MONITOR_INPUT"/>
         <permission name="android.permission.PROVIDE_TRUST_AGENT"/>
         <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
         <permission name="android.permission.REAL_GET_TASKS"/>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 49edcf7..487a6e9 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -164,6 +164,7 @@
         <permission name="android.permission.REBOOT"/>
         <permission name="android.permission.REGISTER_CALL_PROVIDER"/>
         <permission name="android.permission.REGISTER_SIM_SUBSCRIPTION"/>
+        <permission name="android.permission.REGISTER_STATS_PULL_ATOM"/>
         <permission name="android.permission.SEND_RESPOND_VIA_MESSAGE"/>
         <permission name="android.permission.SET_TIME_ZONE"/>
         <permission name="android.permission.SHUTDOWN"/>
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index d9d2eea..a7d0cb8 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -764,8 +764,9 @@
         private @KeyProperties.BlockModeEnum String[] mBlockModes;
         private boolean mRandomizedEncryptionRequired = true;
         private boolean mUserAuthenticationRequired;
-        private int mUserAuthenticationValidityDurationSeconds = -1;
-        private @KeyProperties.AuthEnum int mUserAuthenticationType;
+        private int mUserAuthenticationValidityDurationSeconds = 0;
+        private @KeyProperties.AuthEnum int mUserAuthenticationType =
+                KeyProperties.AUTH_BIOMETRIC_STRONG;
         private boolean mUserPresenceRequired = false;
         private byte[] mAttestationChallenge = null;
         private boolean mUniqueIdIncluded = false;
@@ -1240,7 +1241,8 @@
             if (seconds == -1) {
                 return setUserAuthenticationParameters(0, KeyProperties.AUTH_BIOMETRIC_STRONG);
             }
-            return setUserAuthenticationParameters(seconds, KeyProperties.AUTH_BIOMETRIC_STRONG);
+            return setUserAuthenticationParameters(seconds, KeyProperties.AUTH_DEVICE_CREDENTIAL
+                                                            | KeyProperties.AUTH_BIOMETRIC_STRONG);
         }
 
         /**
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index 8120a93..2e793de 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -562,8 +562,9 @@
         private @KeyProperties.BlockModeEnum String[] mBlockModes;
         private boolean mRandomizedEncryptionRequired = true;
         private boolean mUserAuthenticationRequired;
-        private @KeyProperties.AuthEnum int mUserAuthenticationType;
-        private int mUserAuthenticationValidityDurationSeconds = -1;
+        private int mUserAuthenticationValidityDurationSeconds = 0;
+        private @KeyProperties.AuthEnum int mUserAuthenticationType =
+                KeyProperties.AUTH_BIOMETRIC_STRONG;
         private boolean mUserPresenceRequired = false;
         private boolean mUserAuthenticationValidWhileOnBody;
         private boolean mInvalidatedByBiometricEnrollment = true;
@@ -870,7 +871,8 @@
             if (seconds == -1) {
                 return setUserAuthenticationParameters(0, KeyProperties.AUTH_BIOMETRIC_STRONG);
             }
-            return setUserAuthenticationParameters(seconds, KeyProperties.AUTH_BIOMETRIC_STRONG);
+            return setUserAuthenticationParameters(seconds, KeyProperties.AUTH_DEVICE_CREDENTIAL
+                                                            | KeyProperties.AUTH_BIOMETRIC_STRONG);
         }
 
         /**
diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java
index 4ead253..bc933ff 100644
--- a/keystore/java/android/security/keystore/KeymasterUtils.java
+++ b/keystore/java/android/security/keystore/KeymasterUtils.java
@@ -165,8 +165,7 @@
             }
             args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
                     KeymasterArguments.toUint64(sid));
-            args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
-                    KeymasterDefs.HW_AUTH_PASSWORD | KeymasterDefs.HW_AUTH_BIOMETRIC);
+            args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, spec.getUserAuthenticationType());
             args.addUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
                     spec.getUserAuthenticationValidityDurationSeconds());
             if (spec.isUserAuthenticationValidWhileOnBody()) {
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 550e41f..ff7049e 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -612,7 +612,7 @@
     public Location getLastLocation() {
         try {
             return mService.getLastLocation(null, mContext.getPackageName(),
-                    mContext.getFeatureId());
+                    mContext.getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -645,7 +645,7 @@
 
         try {
             return mService.getLastLocation(request, mContext.getPackageName(),
-                    mContext.getFeatureId());
+                    mContext.getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -742,7 +742,7 @@
 
         try {
             if (mService.getCurrentLocation(currentLocationRequest, remoteCancellationSignal,
-                    listenerTransport, mContext.getPackageName(), mContext.getFeatureId(),
+                    listenerTransport, mContext.getPackageName(), mContext.getAttributionTag(),
                     getListenerIdentifier(consumer))) {
                 listenerTransport.register(mContext.getSystemService(AlarmManager.class),
                         remoteCancellationSignal);
@@ -1189,7 +1189,7 @@
             boolean registered = false;
             try {
                 mService.requestLocationUpdates(locationRequest, transport, null,
-                        mContext.getPackageName(), mContext.getFeatureId(),
+                        mContext.getPackageName(), mContext.getAttributionTag(),
                         getListenerIdentifier(listener));
                 registered = true;
             } catch (RemoteException e) {
@@ -1235,7 +1235,7 @@
 
         try {
             mService.requestLocationUpdates(locationRequest, null, pendingIntent,
-                    mContext.getPackageName(), mContext.getFeatureId(),
+                    mContext.getPackageName(), mContext.getAttributionTag(),
                     getListenerIdentifier(pendingIntent));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1711,7 +1711,7 @@
         LocationRequest request = new LocationRequest().setExpireIn(expiration);
         try {
             mService.requestGeofence(request, fence, intent, mContext.getPackageName(),
-                    mContext.getFeatureId(), getListenerIdentifier(intent));
+                    mContext.getAttributionTag(), getListenerIdentifier(intent));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1798,7 +1798,7 @@
 
         try {
             mService.requestGeofence(request, fence, intent, mContext.getPackageName(),
-                    mContext.getFeatureId(), getListenerIdentifier(intent));
+                    mContext.getAttributionTag(), getListenerIdentifier(intent));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2946,7 +2946,7 @@
 
             GnssStatusListener transport = new GnssStatusListener();
             if (mService.registerGnssStatusCallback(transport, mContext.getPackageName(),
-                    mContext.getFeatureId())) {
+                    mContext.getAttributionTag())) {
                 mListenerTransport = transport;
                 return true;
             } else {
@@ -3012,7 +3012,7 @@
 
             GnssMeasurementsListener transport = new GnssMeasurementsListener();
             if (mService.addGnssMeasurementsListener(request, transport, mContext.getPackageName(),
-                    mContext.getFeatureId(), "gnss measurement callback")) {
+                    mContext.getAttributionTag(), "gnss measurement callback")) {
                 mListenerTransport = transport;
                 return true;
             } else {
@@ -3065,7 +3065,7 @@
 
             GnssNavigationMessageListener transport = new GnssNavigationMessageListener();
             if (mService.addGnssNavigationMessageListener(transport, mContext.getPackageName(),
-                    mContext.getFeatureId(), "gnss navigation callback")) {
+                    mContext.getAttributionTag(), "gnss navigation callback")) {
                 mListenerTransport = transport;
                 return true;
             } else {
@@ -3106,7 +3106,7 @@
 
             GnssAntennaInfoListener transport = new GnssAntennaInfoListener();
             if (mService.addGnssAntennaInfoListener(transport, mContext.getPackageName(),
-                    mContext.getFeatureId(), "gnss antenna info callback")) {
+                    mContext.getAttributionTag(), "gnss antenna info callback")) {
                 mListenerTransport = transport;
                 return true;
             } else {
@@ -3143,7 +3143,7 @@
 
             BatchedLocationCallback transport = new BatchedLocationCallback();
             if (mService.addGnssBatchingCallback(transport, mContext.getPackageName(),
-                    mContext.getFeatureId(), "batched location callback")) {
+                    mContext.getAttributionTag(), "batched location callback")) {
                 mListenerTransport = transport;
                 return true;
             } else {
diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java
index 7932dcb..ea06632 100644
--- a/media/java/android/media/tv/tuner/Lnb.java
+++ b/media/java/android/media/tv/tuner/Lnb.java
@@ -156,7 +156,7 @@
 
     private long mNativeContext;
 
-    Lnb(int id) {
+    private Lnb(int id) {
         mId = id;
     }
 
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index bcbc12b..08a33f1 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -43,7 +43,11 @@
 import android.media.tv.tuner.frontend.FrontendStatus.FrontendStatusType;
 import android.media.tv.tuner.frontend.OnTuneEventListener;
 import android.media.tv.tuner.frontend.ScanCallback;
+import android.media.tv.tunerresourcemanager.ResourceClientProfile;
+import android.media.tv.tunerresourcemanager.TunerLnbRequest;
+import android.media.tv.tunerresourcemanager.TunerResourceManager;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.Looper;
 import android.os.Message;
 
@@ -67,6 +71,7 @@
     private static final String TAG = "MediaTvTuner";
     private static final boolean DEBUG = false;
 
+    private static final int MSG_RESOURCE_LOST = 1;
     private static final int MSG_ON_FILTER_EVENT = 2;
     private static final int MSG_ON_FILTER_STATUS = 3;
     private static final int MSG_ON_LNB_EVENT = 4;
@@ -93,6 +98,8 @@
     }
 
     private final Context mContext;
+    private final TunerResourceManager mTunerResourceManager;
+    private final int mClientId;
 
     private List<Integer> mFrontendIds;
     private Frontend mFrontend;
@@ -102,6 +109,7 @@
 
     private List<Integer> mLnbIds;
     private Lnb mLnb;
+    private Integer mLnbId;
     @Nullable
     private OnTuneEventListener mOnTuneEventListener;
     @Nullable
@@ -115,6 +123,15 @@
     @Nullable
     private Executor mOnResourceLostListenerExecutor;
 
+
+    private final TunerResourceManager.ResourcesReclaimListener mResourceListener =
+            new TunerResourceManager.ResourcesReclaimListener() {
+                @Override
+                public void onReclaimResources() {
+                    mHandler.sendMessage(mHandler.obtainMessage(MSG_RESOURCE_LOST));
+                }
+            };
+
     /**
      * Constructs a Tuner instance.
      *
@@ -127,6 +144,14 @@
             @TvInputService.PriorityHintUseCaseType int useCase) {
         nativeSetup();
         mContext = context;
+        mTunerResourceManager = (TunerResourceManager)
+                context.getSystemService(Context.TV_TUNER_RESOURCE_MGR_SERVICE);
+
+        int[] clientId = new int[1];
+        ResourceClientProfile profile = new ResourceClientProfile(tvInputSessionId, useCase);
+        mTunerResourceManager.registerClientProfile(
+                profile, new HandlerExecutor(mHandler), mResourceListener, clientId);
+        mClientId = clientId[0];
     }
 
     /**
@@ -226,6 +251,7 @@
 
     private native List<Integer> nativeGetLnbIds();
     private native Lnb nativeOpenLnbById(int id);
+    private native Lnb nativeOpenLnbByName(String name);
 
     private native Descrambler nativeOpenDescrambler();
 
@@ -275,6 +301,14 @@
                     }
                     break;
                 }
+                case MSG_RESOURCE_LOST: {
+                    if (mOnResourceLostListener != null
+                                && mOnResourceLostListenerExecutor != null) {
+                        mOnResourceLostListenerExecutor.execute(
+                                () -> mOnResourceLostListener.onResourceLost(Tuner.this));
+                    }
+                    break;
+                }
                 default:
                     // fall through
             }
@@ -698,7 +732,10 @@
         Objects.requireNonNull(executor, "executor must not be null");
         Objects.requireNonNull(cb, "LnbCallback must not be null");
         TunerUtils.checkTunerPermission(mContext);
-        return openLnbByName(null, executor, cb);
+        if (mLnbId == null && !requestLnb()) {
+            return null;
+        }
+        return nativeOpenLnbById(mLnbId);
     }
 
     /**
@@ -714,11 +751,21 @@
     @Nullable
     public Lnb openLnbByName(@NonNull String name, @CallbackExecutor @NonNull Executor executor,
             @NonNull LnbCallback cb) {
+        Objects.requireNonNull(name, "LNB name must not be null");
         Objects.requireNonNull(executor, "executor must not be null");
         Objects.requireNonNull(cb, "LnbCallback must not be null");
         TunerUtils.checkTunerPermission(mContext);
-        // TODO: use resource manager to get LNB ID.
-        return new Lnb(0);
+        return nativeOpenLnbByName(name);
+    }
+
+    private boolean requestLnb() {
+        int[] lnbId = new int[1];
+        TunerLnbRequest request = new TunerLnbRequest(mClientId);
+        boolean granted = mTunerResourceManager.requestLnb(request, lnbId);
+        if (granted) {
+            mLnbId = lnbId[0];
+        }
+        return granted;
     }
 
     /**
diff --git a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
index dbd9db4..37a016e 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
@@ -69,7 +69,7 @@
      */
     public static final int PLAYBACK_STATUS_FULL = Constants.PlaybackStatus.SPACE_FULL;
 
-    long mNativeContext;
+    private long mNativeContext;
 
     private native int nativeAttachFilter(Filter filter);
     private native int nativeDetachFilter(Filter filter);
diff --git a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
index c1c6c62..d06356c 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
@@ -30,7 +30,7 @@
  */
 @SystemApi
 public class DvrRecorder implements AutoCloseable {
-    long mNativeContext;
+    private long mNativeContext;
 
     private native int nativeAttachFilter(Filter filter);
     private native int nativeDetachFilter(Filter filter);
diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
index e99fd36f..1510b2d 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
@@ -125,9 +125,9 @@
     /** @hide */
     @IntDef(flag = true,
             prefix = "CONSTELLATION_",
-            value = {CONSTELLATION_UNDEFINED, CONSTELLATION_AUTO, CONSTELLATION_CONSTELLATION_QPSK,
-                    CONSTELLATION_CONSTELLATION_16QAM, CONSTELLATION_CONSTELLATION_64QAM,
-                    CONSTELLATION_CONSTELLATION_256QAM})
+            value = {CONSTELLATION_UNDEFINED, CONSTELLATION_AUTO, CONSTELLATION_QPSK,
+                    CONSTELLATION_16QAM, CONSTELLATION_64QAM,
+                    CONSTELLATION_256QAM})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Constellation {}
 
@@ -142,22 +142,22 @@
     /**
      * QPSK Constellation.
      */
-    public static final int CONSTELLATION_CONSTELLATION_QPSK =
+    public static final int CONSTELLATION_QPSK =
             Constants.FrontendDvbtConstellation.CONSTELLATION_QPSK;
     /**
      * 16QAM Constellation.
      */
-    public static final int CONSTELLATION_CONSTELLATION_16QAM =
+    public static final int CONSTELLATION_16QAM =
             Constants.FrontendDvbtConstellation.CONSTELLATION_16QAM;
     /**
      * 64QAM Constellation.
      */
-    public static final int CONSTELLATION_CONSTELLATION_64QAM =
+    public static final int CONSTELLATION_64QAM =
             Constants.FrontendDvbtConstellation.CONSTELLATION_64QAM;
     /**
      * 256QAM Constellation.
      */
-    public static final int CONSTELLATION_CONSTELLATION_256QAM =
+    public static final int CONSTELLATION_256QAM =
             Constants.FrontendDvbtConstellation.CONSTELLATION_256QAM;
 
 
@@ -275,11 +275,11 @@
     @IntDef(flag = true,
             prefix = "GUARD_INTERVAL_",
             value = {GUARD_INTERVAL_UNDEFINED, GUARD_INTERVAL_AUTO,
-            GUARD_INTERVAL_INTERVAL_1_32, GUARD_INTERVAL_INTERVAL_1_16,
-            GUARD_INTERVAL_INTERVAL_1_8, GUARD_INTERVAL_INTERVAL_1_4,
-            GUARD_INTERVAL_INTERVAL_1_128,
-            GUARD_INTERVAL_INTERVAL_19_128,
-            GUARD_INTERVAL_INTERVAL_19_256})
+            GUARD_INTERVAL_1_32, GUARD_INTERVAL_1_16,
+            GUARD_INTERVAL_1_8, GUARD_INTERVAL_1_4,
+            GUARD_INTERVAL_1_128,
+            GUARD_INTERVAL_19_128,
+            GUARD_INTERVAL_19_256})
     @Retention(RetentionPolicy.SOURCE)
     public @interface GuardInterval {}
 
@@ -295,37 +295,37 @@
     /**
      * 1/32 Guard Interval.
      */
-    public static final int GUARD_INTERVAL_INTERVAL_1_32 =
+    public static final int GUARD_INTERVAL_1_32 =
             Constants.FrontendDvbtGuardInterval.INTERVAL_1_32;
     /**
      * 1/16 Guard Interval.
      */
-    public static final int GUARD_INTERVAL_INTERVAL_1_16 =
+    public static final int GUARD_INTERVAL_1_16 =
             Constants.FrontendDvbtGuardInterval.INTERVAL_1_16;
     /**
      * 1/8 Guard Interval.
      */
-    public static final int GUARD_INTERVAL_INTERVAL_1_8 =
+    public static final int GUARD_INTERVAL_1_8 =
             Constants.FrontendDvbtGuardInterval.INTERVAL_1_8;
     /**
      * 1/4 Guard Interval.
      */
-    public static final int GUARD_INTERVAL_INTERVAL_1_4 =
+    public static final int GUARD_INTERVAL_1_4 =
             Constants.FrontendDvbtGuardInterval.INTERVAL_1_4;
     /**
      * 1/128 Guard Interval.
      */
-    public static final int GUARD_INTERVAL_INTERVAL_1_128 =
+    public static final int GUARD_INTERVAL_1_128 =
             Constants.FrontendDvbtGuardInterval.INTERVAL_1_128;
     /**
      * 19/128 Guard Interval.
      */
-    public static final int GUARD_INTERVAL_INTERVAL_19_128 =
+    public static final int GUARD_INTERVAL_19_128 =
             Constants.FrontendDvbtGuardInterval.INTERVAL_19_128;
     /**
      * 19/256 Guard Interval.
      */
-    public static final int GUARD_INTERVAL_INTERVAL_19_256 =
+    public static final int GUARD_INTERVAL_19_256 =
             Constants.FrontendDvbtGuardInterval.INTERVAL_19_256;
 
     /** @hide */
@@ -435,14 +435,14 @@
      * Gets Code Rate for High Priority level.
      */
     @CodeRate
-    public int getHpCodeRate() {
+    public int getHighPriorityCodeRate() {
         return mHpCodeRate;
     }
     /**
      * Gets Code Rate for Low Priority level.
      */
     @CodeRate
-    public int getLpCodeRate() {
+    public int getLowPriorityCodeRate() {
         return mLpCodeRate;
     }
     /**
@@ -560,7 +560,7 @@
          * Sets Code Rate for High Priority level.
          */
         @NonNull
-        public Builder setHpCodeRate(@CodeRate int hpCodeRate) {
+        public Builder setHighPriorityCodeRate(@CodeRate int hpCodeRate) {
             mHpCodeRate = hpCodeRate;
             return this;
         }
@@ -568,7 +568,7 @@
          * Sets Code Rate for Low Priority level.
          */
         @NonNull
-        public Builder setLpCodeRate(@CodeRate int lpCodeRate) {
+        public Builder setLowPriorityCodeRate(@CodeRate int lpCodeRate) {
             mLpCodeRate = lpCodeRate;
             return this;
         }
diff --git a/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl b/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
index 20efaa1..77cac6e 100644
--- a/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
@@ -19,6 +19,8 @@
 import android.media.tv.tunerresourcemanager.CasSessionRequest;
 import android.media.tv.tunerresourcemanager.IResourcesReclaimListener;
 import android.media.tv.tunerresourcemanager.ResourceClientProfile;
+import android.media.tv.tunerresourcemanager.TunerDemuxRequest;
+import android.media.tv.tunerresourcemanager.TunerDescramblerRequest;
 import android.media.tv.tunerresourcemanager.TunerFrontendInfo;
 import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
 import android.media.tv.tunerresourcemanager.TunerLnbRequest;
@@ -148,6 +150,53 @@
     void shareFrontend(in int selfClientId, in int targetClientId);
 
     /*
+     * This API is used by the Tuner framework to request an available demux from the TunerHAL.
+     *
+     * <p>There are three possible scenarios:
+     * <ul>
+     * <li>If there is demux available, the API would send the handle back.
+     *
+     * <li>If no Demux is available but the current request info can show higher priority than
+     * other uses of demuxes, the API will send
+     * {@link IResourcesReclaimListener#onReclaimResources()} to the {@link Tuner}. Tuner would
+     * handle the resource reclaim on the holder of lower priority and notify the holder of its
+     * resource loss.
+     *
+     * <li>If no demux can be granted, the API would return false.
+     * <ul>
+     *
+     * @param request {@link TunerDemuxRequest} information of the current request.
+     * @param demuxHandle a one-element array to return the granted demux handle.
+     *
+     * @return true if there is demux granted.
+     */
+    boolean requestDemux(in TunerDemuxRequest request, out int[] demuxHandle);
+
+    /*
+     * This API is used by the Tuner framework to request an available descrambler from the
+     * TunerHAL.
+     *
+     * <p>There are three possible scenarios:
+     * <ul>
+     * <li>If there is descrambler available, the API would send the handle back.
+     *
+     * <li>If no Descrambler is available but the current request info can show higher priority than
+     * other uses of Descrambler, the API will send
+     * {@link IResourcesReclaimListener#onReclaimResources()} to the {@link Tuner}. Tuner would
+     * handle the resource reclaim on the holder of lower priority and notify the holder of its
+     * resource loss.
+     *
+     * <li>If no Descrambler can be granted, the API would return false.
+     * <ul>
+     *
+     * @param request {@link TunerDescramblerRequest} information of the current request.
+     * @param descramblerHandle a one-element array to return the granted descrambler handle.
+     *
+     * @return true if there is Descrambler granted.
+     */
+    boolean requestDescrambler(in TunerDescramblerRequest request, out int[] descramblerHandle);
+
+    /*
      * This API is used by the Tuner framework to request an available Cas session. This session
      * needs to be under the CAS system with the id indicated in the {@code request}.
      *
@@ -210,6 +259,24 @@
     void releaseFrontend(in int frontendId);
 
     /*
+     * Notifies the TRM that the Demux with the given handle was released.
+     *
+     * <p>Client must call this whenever it releases a demux.
+     *
+     * @param demuxHandle the handle of the released Tuner Demux.
+     */
+    void releaseDemux(in int demuxHandle);
+
+    /*
+     * Notifies the TRM that the Descrambler with the given handle was released.
+     *
+     * <p>Client must call this whenever it releases a descrambler.
+     *
+     * @param demuxHandle the handle of the released Tuner Descrambler.
+     */
+    void releaseDescrambler(in int descramblerHandle);
+
+    /*
      * Notifies the TRM that the given Cas session has been released.
      *
      * <p>Client must call this whenever it releases a Cas session.
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl b/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl
new file mode 100644
index 0000000..919a215
--- /dev/null
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tunerresourcemanager;
+
+/**
+ * Information required to request a Tuner Demux.
+ *
+ * @hide
+ */
+parcelable TunerDemuxRequest;
\ No newline at end of file
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.java b/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.java
new file mode 100644
index 0000000..34a7761
--- /dev/null
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tunerresourcemanager;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * Information required to request a Tuner Demux.
+ *
+ * @hide
+ */
+public final class TunerDemuxRequest implements Parcelable {
+    static final String TAG = "TunerDemuxRequest";
+
+    public static final
+                @NonNull
+                Parcelable.Creator<TunerDemuxRequest> CREATOR =
+                new Parcelable.Creator<TunerDemuxRequest>() {
+                @Override
+                public TunerDemuxRequest createFromParcel(Parcel source) {
+                    try {
+                        return new TunerDemuxRequest(source);
+                    } catch (Exception e) {
+                        Log.e(TAG, "Exception creating TunerDemuxRequest from parcel", e);
+                        return null;
+                    }
+                }
+
+                @Override
+                public TunerDemuxRequest[] newArray(int size) {
+                    return new TunerDemuxRequest[size];
+                }
+            };
+
+    /**
+     * Client id of the client that sends the request.
+     */
+    private final int mClientId;
+
+    private TunerDemuxRequest(@NonNull Parcel source) {
+        mClientId = source.readInt();
+    }
+
+    /**
+     * Constructs a new {@link TunerDemuxRequest} with the given parameters.
+     *
+     * @param clientId id of the client.
+     */
+    public TunerDemuxRequest(int clientId) {
+        mClientId = clientId;
+    }
+
+    /**
+     * Returns the id of the client.
+     */
+    public int getClientId() {
+        return mClientId;
+    }
+
+    // Parcelable
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        StringBuilder b = new StringBuilder(128);
+        b.append("TunerDemuxRequest {clientId=").append(mClientId);
+        b.append("}");
+        return b.toString();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mClientId);
+    }
+}
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.aidl b/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.aidl
new file mode 100644
index 0000000..fbafb3b
--- /dev/null
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tunerresourcemanager;
+
+/**
+ * Information required to request a Tuner Descrambler.
+ *
+ * @hide
+ */
+parcelable TunerDescramblerRequest;
\ No newline at end of file
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.java b/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.java
new file mode 100644
index 0000000..5816287
--- /dev/null
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tunerresourcemanager;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * Information required to request a Tuner Descrambler.
+ *
+ * @hide
+ */
+public final class TunerDescramblerRequest implements Parcelable {
+    static final String TAG = "TunerDescramblerRequest";
+
+    public static final
+                @NonNull
+                Parcelable.Creator<TunerDescramblerRequest> CREATOR =
+                new Parcelable.Creator<TunerDescramblerRequest>() {
+                @Override
+                public TunerDescramblerRequest createFromParcel(Parcel source) {
+                    try {
+                        return new TunerDescramblerRequest(source);
+                    } catch (Exception e) {
+                        Log.e(TAG, "Exception creating TunerDescramblerRequest from parcel", e);
+                        return null;
+                    }
+                }
+
+                @Override
+                public TunerDescramblerRequest[] newArray(int size) {
+                    return new TunerDescramblerRequest[size];
+                }
+            };
+
+    /**
+     * Client id of the client that sends the request.
+     */
+    private final int mClientId;
+
+    private TunerDescramblerRequest(@NonNull Parcel source) {
+        mClientId = source.readInt();
+    }
+
+    /**
+     * Constructs a new {@link TunerDescramblerRequest} with the given parameters.
+     *
+     * @param clientId id of the client.
+     */
+    public TunerDescramblerRequest(int clientId) {
+        mClientId = clientId;
+    }
+
+    /**
+     * Returns the id of the client.
+     */
+    public int getClientId() {
+        return mClientId;
+    }
+
+    // Parcelable
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        StringBuilder b = new StringBuilder(128);
+        b.append("TunerDescramblerRequest {clientId=").append(mClientId);
+        b.append("}");
+        return b.toString();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mClientId);
+    }
+}
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
index 7c11ed4..524b6c2 100644
--- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
@@ -65,6 +65,7 @@
     public static final int INVALID_LNB_ID = -1;
     public static final int INVALID_TV_INPUT_DEVICE_ID = -1;
     public static final int INVALID_TV_INPUT_PORT_ID = -1;
+    public static final int INVALID_RESOURCE_HANDLE = -1;
 
     private final ITunerResourceManager mService;
     private final int mUserId;
@@ -260,6 +261,71 @@
     }
 
     /**
+     * Requests a Tuner Demux resource.
+     *
+     * <p>There are three possible scenarios:
+     * <ul>
+     * <li>If there is Demux available, the API would send the handle back.
+     *
+     * <li>If no Demux is available but the current request has a higher priority than other uses of
+     * demuxes, the API will send {@link IResourcesReclaimListener#onReclaimResources()} to the
+     * {@link Tuner}. Tuner would handle the resource reclaim on the holder of lower priority and
+     * notify the holder of its resource loss.
+     *
+     * <li>If no Demux system can be granted, the API would return false.
+     * <ul>
+     *
+     * @param request {@link TunerDemuxRequest} information of the current request.
+     * @param demuxHandle a one-element array to return the granted Demux handle.
+     *                    If no Demux granted, this will return {@link #INVALID_RESOURCE_HANDLE}.
+     *
+     * @return true if there is Demux granted.
+     */
+    public boolean requestDemux(@NonNull TunerDemuxRequest request, @NonNull int[] demuxHandle) {
+        boolean result = false;
+        try {
+            result = mService.requestDemux(request, demuxHandle);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        return result;
+    }
+
+    /**
+     * Requests a Tuner Descrambler resource.
+     *
+     * <p>There are three possible scenarios:
+     * <ul>
+     * <li>If there is Descrambler available, the API would send the handle back.
+     *
+     * <li>If no Descrambler is available but the current request has a higher priority than other
+     * uses of descramblers, the API will send
+     * {@link IResourcesReclaimListener#onReclaimResources()} to the {@link Tuner}. Tuner would
+     * handle the resource reclaim on the holder of lower priority and notify the holder of its
+     * resource loss.
+     *
+     * <li>If no Descrambler system can be granted, the API would return false.
+     * <ul>
+     *
+     * @param request {@link TunerDescramblerRequest} information of the current request.
+     * @param descramblerHandle a one-element array to return the granted Descrambler handle.
+     *                          If no Descrambler granted, this will return
+     *                          {@link #INVALID_RESOURCE_HANDLE}.
+     *
+     * @return true if there is Descrambler granted.
+     */
+    public boolean requestDescrambler(@NonNull TunerDescramblerRequest request,
+                @NonNull int[] descramblerHandle) {
+        boolean result = false;
+        try {
+            result = mService.requestDescrambler(request, descramblerHandle);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        return result;
+    }
+
+    /**
      * Requests a CAS session resource.
      *
      * <p>There are three possible scenarios:
@@ -345,6 +411,36 @@
     }
 
     /**
+     * Notifies the TRM that the Demux with the given handle has been released.
+     *
+     * <p>Client must call this whenever it releases an Demux.
+     *
+     * @param demuxHandle the handle of the released Tuner Demux.
+     */
+    public void releaseDemux(int demuxHandle) {
+        try {
+            mService.releaseDemux(demuxHandle);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notifies the TRM that the Descrambler with the given handle has been released.
+     *
+     * <p>Client must call this whenever it releases an Descrambler.
+     *
+     * @param descramblerHandle the handle of the released Tuner Descrambler.
+     */
+    public void releaseDescrambler(int descramblerHandle) {
+        try {
+            mService.releaseDescrambler(descramblerHandle);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Notifies the TRM that the given Cas session has been released.
      *
      * <p>Client must call this whenever it releases a Cas session.
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 01f068a..a37c9e5 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -138,11 +138,13 @@
     jfieldID filterContext;
     jfieldID timeFilterContext;
     jfieldID descramblerContext;
-    jfieldID dvrContext;
+    jfieldID dvrRecorderContext;
+    jfieldID dvrPlaybackContext;
     jmethodID frontendInitID;
     jmethodID filterInitID;
     jmethodID timeFilterInitID;
-    jmethodID dvrInitID;
+    jmethodID dvrRecorderInitID;
+    jmethodID dvrPlaybackInitID;
     jmethodID onFrontendEventID;
     jmethodID onFilterStatusID;
     jmethodID onFilterEventID;
@@ -1072,6 +1074,37 @@
     return lnbObj;
 }
 
+jobject JTuner::openLnbByName(jstring name) {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    std::string lnbName(env->GetStringUTFChars(name, nullptr));
+    sp<ILnb> iLnbSp;
+    Result res;
+    LnbId id;
+    mTuner->openLnbByName(lnbName, [&](Result r, LnbId lnbId, const sp<ILnb>& lnb) {
+        res = r;
+        iLnbSp = lnb;
+        id = lnbId;
+    });
+    if (res != Result::SUCCESS || iLnbSp == nullptr) {
+        ALOGE("Failed to open lnb");
+        return NULL;
+    }
+    mLnb = iLnbSp;
+    sp<LnbCallback> lnbCb = new LnbCallback(mObject, id);
+    mLnb->setCallback(lnbCb);
+
+    jobject lnbObj = env->NewObject(
+            env->FindClass("android/media/tv/tuner/Lnb"),
+            gFields.lnbInitID,
+            id);
+
+    sp<Lnb> lnbSp = new Lnb(iLnbSp, lnbObj);
+    lnbSp->incStrong(lnbObj);
+    env->SetLongField(lnbObj, gFields.lnbContext, (jlong) lnbSp.get());
+
+    return lnbObj;
+}
+
 int JTuner::tune(const FrontendSettings& settings) {
     if (mFe == NULL) {
         ALOGE("frontend is not initialized");
@@ -1302,7 +1335,7 @@
     return timeFilterObj;
 }
 
-jobject JTuner::openDvr(DvrType type, int bufferSize) {
+jobject JTuner::openDvr(DvrType type, jlong bufferSize) {
     ALOGD("JTuner::openDvr");
     if (mDemux == NULL) {
         if (openDemux() != Result::SUCCESS) {
@@ -1311,24 +1344,38 @@
     }
     sp<IDvr> iDvrSp;
     sp<DvrCallback> callback = new DvrCallback();
-    mDemux->openDvr(type, bufferSize, callback,
-            [&](Result, const sp<IDvr>& dvr) {
+    Result res;
+    mDemux->openDvr(type, (uint32_t) bufferSize, callback,
+            [&](Result r, const sp<IDvr>& dvr) {
+                res = r;
                 iDvrSp = dvr;
             });
 
-    if (iDvrSp == NULL) {
+    if (res != Result::SUCCESS || iDvrSp == NULL) {
         return NULL;
     }
 
     JNIEnv *env = AndroidRuntime::getJNIEnv();
-    jobject dvrObj =
-            env->NewObject(
-                    env->FindClass("android/media/tv/tuner/dvr/Dvr"),
-                    gFields.dvrInitID,
-                    mObject);
-    sp<Dvr> dvrSp = new Dvr(iDvrSp, dvrObj);
-    dvrSp->incStrong(dvrObj);
-    env->SetLongField(dvrObj, gFields.dvrContext, (jlong)dvrSp.get());
+    jobject dvrObj;
+    if (type == DvrType::RECORD) {
+        dvrObj =
+                env->NewObject(
+                        env->FindClass("android/media/tv/tuner/dvr/DvrRecorder"),
+                        gFields.dvrRecorderInitID,
+                        mObject);
+        sp<Dvr> dvrSp = new Dvr(iDvrSp, dvrObj);
+        dvrSp->incStrong(dvrObj);
+        env->SetLongField(dvrObj, gFields.dvrRecorderContext, (jlong)dvrSp.get());
+    } else {
+        dvrObj =
+                env->NewObject(
+                        env->FindClass("android/media/tv/tuner/dvr/DvrPlayback"),
+                        gFields.dvrPlaybackInitID,
+                        mObject);
+        sp<Dvr> dvrSp = new Dvr(iDvrSp, dvrObj);
+        dvrSp->incStrong(dvrObj);
+        env->SetLongField(dvrObj, gFields.dvrPlaybackContext, (jlong)dvrSp.get());
+    }
 
     callback->setDvr(dvrObj);
 
@@ -1852,7 +1899,11 @@
 }
 
 static sp<Dvr> getDvr(JNIEnv *env, jobject dvr) {
-    return (Dvr *)env->GetLongField(dvr, gFields.dvrContext);
+    bool isRecorder =
+            env->IsInstanceOf(dvr, env->FindClass("android/media/tv/tuner/dvr/DvrRecorder"));
+    jfieldID fieldId =
+            isRecorder ? gFields.dvrRecorderContext : gFields.dvrPlaybackContext;
+    return (Dvr *)env->GetLongField(dvr, fieldId);
 }
 
 static void android_media_tv_Tuner_native_init(JNIEnv *env) {
@@ -1872,8 +1923,7 @@
 
     jclass lnbClazz = env->FindClass("android/media/tv/tuner/Lnb");
     gFields.lnbContext = env->GetFieldID(lnbClazz, "mNativeContext", "J");
-    gFields.lnbInitID =
-            env->GetMethodID(lnbClazz, "<init>", "(I)V");
+    gFields.lnbInitID = env->GetMethodID(lnbClazz, "<init>", "(I)V");
 
     jclass filterClazz = env->FindClass("android/media/tv/tuner/filter/Filter");
     gFields.filterContext = env->GetFieldID(filterClazz, "mNativeContext", "J");
@@ -1894,9 +1944,13 @@
     gFields.descramblerInitID =
             env->GetMethodID(descramblerClazz, "<init>", "()V");
 
-    jclass dvrClazz = env->FindClass("android/media/tv/tuner/dvr/Dvr");
-    gFields.dvrContext = env->GetFieldID(dvrClazz, "mNativeContext", "J");
-    gFields.dvrInitID = env->GetMethodID(dvrClazz, "<init>", "()V");
+    jclass dvrRecorderClazz = env->FindClass("android/media/tv/tuner/dvr/DvrRecorder");
+    gFields.dvrRecorderContext = env->GetFieldID(dvrRecorderClazz, "mNativeContext", "J");
+    gFields.dvrRecorderInitID = env->GetMethodID(dvrRecorderClazz, "<init>", "()V");
+
+    jclass dvrPlaybackClazz = env->FindClass("android/media/tv/tuner/dvr/DvrPlayback");
+    gFields.dvrPlaybackContext = env->GetFieldID(dvrPlaybackClazz, "mNativeContext", "J");
+    gFields.dvrPlaybackInitID = env->GetMethodID(dvrPlaybackClazz, "<init>", "()V");
 
     jclass linearBlockClazz = env->FindClass("android/media/MediaCodec$LinearBlock");
     gFields.linearBlockInitID = env->GetMethodID(linearBlockClazz, "<init>", "()V");
@@ -1996,6 +2050,12 @@
     return tuner->openLnbById(id);
 }
 
+static jobject android_media_tv_Tuner_open_lnb_by_name(JNIEnv *env, jobject thiz, jstring name) {
+    sp<JTuner> tuner = getTuner(env, thiz);
+    return tuner->openLnbByName(name);
+}
+
+
 static jobject android_media_tv_Tuner_open_filter(
         JNIEnv *env, jobject thiz, jint type, jint subType, jlong bufferSize) {
     sp<JTuner> tuner = getTuner(env, thiz);
@@ -2618,13 +2678,15 @@
 }
 
 static jobject android_media_tv_Tuner_open_dvr_recorder(
-        JNIEnv* /* env */, jobject /* thiz */, jlong /* bufferSize */) {
-    return NULL;
+        JNIEnv* env, jobject thiz, jlong bufferSize) {
+    sp<JTuner> tuner = getTuner(env, thiz);
+    return tuner->openDvr(DvrType::RECORD, bufferSize);
 }
 
 static jobject android_media_tv_Tuner_open_dvr_playback(
-        JNIEnv* /* env */, jobject /* thiz */, jlong /* bufferSize */) {
-    return NULL;
+        JNIEnv* env, jobject thiz, jlong bufferSize) {
+    sp<JTuner> tuner = getTuner(env, thiz);
+    return tuner->openDvr(DvrType::PLAYBACK, bufferSize);
 }
 
 static jobject android_media_tv_Tuner_get_demux_caps(JNIEnv*, jobject) {
@@ -2678,11 +2740,13 @@
 }
 
 static int android_media_tv_Tuner_start_dvr(JNIEnv *env, jobject dvr) {
+
     sp<IDvr> dvrSp = getDvr(env, dvr)->getIDvr();
     if (dvrSp == NULL) {
         ALOGD("Failed to start dvr: dvr not found");
         return false;
     }
+
     Result result = dvrSp->start();
     return (int) result;
 }
@@ -2888,6 +2952,8 @@
             (void *)android_media_tv_Tuner_get_lnb_ids },
     { "nativeOpenLnbById", "(I)Landroid/media/tv/tuner/Lnb;",
             (void *)android_media_tv_Tuner_open_lnb_by_id },
+    { "nativeOpenLnbByName", "(Ljava/lang/String;)Landroid/media/tv/tuner/Lnb;",
+            (void *)android_media_tv_Tuner_open_lnb_by_name },
     { "nativeOpenDescrambler", "()Landroid/media/tv/tuner/Descrambler;",
             (void *)android_media_tv_Tuner_open_descrambler },
     { "nativeOpenDvrRecorder", "(J)Landroid/media/tv/tuner/dvr/DvrRecorder;",
@@ -2930,7 +2996,7 @@
     { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_descrambler },
 };
 
-static const JNINativeMethod gDvrMethods[] = {
+static const JNINativeMethod gDvrRecorderMethods[] = {
     { "nativeAttachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I",
             (void *)android_media_tv_Tuner_attach_filter },
     { "nativeDetachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I",
@@ -2942,14 +3008,22 @@
     { "nativeFlushDvr", "()I", (void *)android_media_tv_Tuner_flush_dvr },
     { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_dvr },
     { "nativeSetFileDescriptor", "(I)V", (void *)android_media_tv_Tuner_dvr_set_fd },
-};
-
-static const JNINativeMethod gDvrRecorderMethods[] = {
     { "nativeWrite", "(J)J", (void *)android_media_tv_Tuner_write_dvr },
     { "nativeWrite", "([BJJ)J", (void *)android_media_tv_Tuner_write_dvr_to_array },
 };
 
 static const JNINativeMethod gDvrPlaybackMethods[] = {
+    { "nativeAttachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I",
+            (void *)android_media_tv_Tuner_attach_filter },
+    { "nativeDetachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I",
+            (void *)android_media_tv_Tuner_detach_filter },
+    { "nativeConfigureDvr", "(Landroid/media/tv/tuner/dvr/DvrSettings;)I",
+            (void *)android_media_tv_Tuner_configure_dvr },
+    { "nativeStartDvr", "()I", (void *)android_media_tv_Tuner_start_dvr },
+    { "nativeStopDvr", "()I", (void *)android_media_tv_Tuner_stop_dvr },
+    { "nativeFlushDvr", "()I", (void *)android_media_tv_Tuner_flush_dvr },
+    { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_dvr },
+    { "nativeSetFileDescriptor", "(I)V", (void *)android_media_tv_Tuner_dvr_set_fd },
     { "nativeRead", "(J)J", (void *)android_media_tv_Tuner_read_dvr },
     { "nativeRead", "([BJJ)J", (void *)android_media_tv_Tuner_read_dvr_from_array },
 };
@@ -2990,13 +3064,6 @@
         return false;
     }
     if (AndroidRuntime::registerNativeMethods(
-            env, "android/media/tv/tuner/dvr/Dvr",
-            gDvrMethods,
-            NELEM(gDvrMethods)) != JNI_OK) {
-        ALOGE("Failed to register dvr native methods");
-        return false;
-    }
-    if (AndroidRuntime::registerNativeMethods(
             env, "android/media/tv/tuner/dvr/DvrRecorder",
             gDvrRecorderMethods,
             NELEM(gDvrRecorderMethods)) != JNI_OK) {
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index d3298a8..5d2bba6 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -180,10 +180,11 @@
     int setLna(bool enable);
     jobject getLnbIds();
     jobject openLnbById(int id);
+    jobject openLnbByName(jstring name);
     jobject openFilter(DemuxFilterType type, int bufferSize);
     jobject openTimeFilter();
     jobject openDescrambler();
-    jobject openDvr(DvrType type, int bufferSize);
+    jobject openDvr(DvrType type, jlong bufferSize);
 
 protected:
     Result openDemux();
diff --git a/media/jni/soundpool/StreamManager.h b/media/jni/soundpool/StreamManager.h
index 15b39f2..30ad220 100644
--- a/media/jni/soundpool/StreamManager.h
+++ b/media/jni/soundpool/StreamManager.h
@@ -70,9 +70,10 @@
     static int staticFunction(void *data) {
         JavaThread *jt = static_cast<JavaThread *>(data);
         jt->mF();
+        jt->mIsClosed = true;  // set the flag that we are closed
+                               // now before we allow the destructor to execute;
+                               // otherwise there may be a use after free.
         jt->mPromise.set_value();
-        jt->mIsClosed = true;  // publicly inform that we are closed
-                               // after we have accessed all variables.
         return 0;
     }
 
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index 6354ccd..ebd7658 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -284,12 +284,12 @@
             ICameraDeviceCallbacks dummyCallbacks = new DummyCameraDeviceCallbacks();
 
             String clientPackageName = getContext().getPackageName();
-            String clientFeatureId = getContext().getFeatureId();
+            String clientAttributionTag = getContext().getAttributionTag();
 
             ICameraDeviceUser cameraUser =
                     mUtils.getCameraService().connectDevice(
                         dummyCallbacks, String.valueOf(cameraId),
-                        clientPackageName, clientFeatureId,
+                        clientPackageName, clientAttributionTag,
                         ICameraService.USE_CALLING_UID);
             assertNotNull(String.format("Camera %s was null", cameraId), cameraUser);
 
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index 466c5f4..bf3e746 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -238,12 +238,12 @@
         ICameraDeviceCallbacks.Stub dummyCallbacks = new DummyCameraDeviceCallbacks();
 
         String clientPackageName = getContext().getPackageName();
-        String clientFeatureId = getContext().getFeatureId();
+        String clientAttributionTag = getContext().getAttributionTag();
 
         mMockCb = spy(dummyCallbacks);
 
         mCameraUser = mUtils.getCameraService().connectDevice(mMockCb, mCameraId,
-                clientPackageName, clientFeatureId, ICameraService.USE_CALLING_UID);
+                clientPackageName, clientAttributionTag, ICameraService.USE_CALLING_UID);
         assertNotNull(String.format("Camera %s was null", mCameraId), mCameraUser);
         mHandlerThread = new HandlerThread(TAG);
         mHandlerThread.start();
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 140c075..8292d30 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -24,7 +24,6 @@
 import com.android.keyguard.KeyguardViewController;
 import com.android.systemui.car.CarDeviceProvisionedController;
 import com.android.systemui.car.CarDeviceProvisionedControllerImpl;
-import com.android.systemui.car.CarNotificationInterruptionStateProvider;
 import com.android.systemui.dagger.SystemUIRootComponent;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dock.DockManagerImpl;
@@ -41,7 +40,6 @@
 import com.android.systemui.statusbar.car.CarStatusBar;
 import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
@@ -67,10 +65,6 @@
 @Module(includes = {DividerModule.class})
 abstract class CarSystemUIModule {
 
-    @Binds
-    abstract NotificationInterruptionStateProvider bindNotificationInterruptionStateProvider(
-            CarNotificationInterruptionStateProvider notificationInterruptionStateProvider);
-
     @Singleton
     @Provides
     @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME)
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
deleted file mode 100644
index 447e579..0000000
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2018 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.car;
-
-import android.content.Context;
-
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.policy.BatteryController;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-/** Auto-specific implementation of {@link NotificationInterruptionStateProvider}. */
-@Singleton
-public class CarNotificationInterruptionStateProvider extends
-        NotificationInterruptionStateProvider {
-
-    @Inject
-    public CarNotificationInterruptionStateProvider(Context context,
-            NotificationFilter filter,
-            StatusBarStateController stateController,
-            BatteryController batteryController) {
-        super(context, filter, stateController, batteryController);
-    }
-
-    @Override
-    public boolean shouldHeadsUp(NotificationEntry entry) {
-        // Because space is usually constrained in the auto use-case, there should not be a
-        // pinned notification when the shade has been expanded. Ensure this by not pinning any
-        // notification if the shade is already opened.
-        if (!getPresenter().isPresenterFullyCollapsed()) {
-            return false;
-        }
-
-        return super.shouldHeadsUp(entry);
-    }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index b2e2104..411f14d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -95,13 +95,15 @@
 import com.android.systemui.statusbar.SuperStatusBarViewFactory;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.init.NotificationsController;
+import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
+import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.phone.AutoHideController;
@@ -249,7 +251,7 @@
             RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
             NotificationGutsManager notificationGutsManager,
             NotificationLogger notificationLogger,
-            NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+            NotificationInterruptStateProvider notificationInterruptStateProvider,
             NotificationViewHierarchyManager notificationViewHierarchyManager,
             KeyguardViewMediator keyguardViewMediator,
             NotificationAlertingManager notificationAlertingManager,
@@ -335,7 +337,7 @@
                 remoteInputQuickSettingsDisabler,
                 notificationGutsManager,
                 notificationLogger,
-                notificationInterruptionStateProvider,
+                notificationInterruptStateProvider,
                 notificationViewHierarchyManager,
                 keyguardViewMediator,
                 notificationAlertingManager,
@@ -488,6 +490,22 @@
                                 .isCurrentUserSetupInProgress();
                     }
                 });
+
+        mNotificationInterruptStateProvider.addSuppressor(new NotificationInterruptSuppressor() {
+            @Override
+            public String getName() {
+                return TAG;
+            }
+
+            @Override
+            public boolean suppressInterruptions(NotificationEntry entry) {
+                // Because space is usually constrained in the auto use-case, there should not be a
+                // pinned notification when the shade has been expanded.
+                // Ensure this by not allowing any interruptions (ie: pinning any notifications) if
+                // the shade is already opened.
+                return !getPresenter().isPresenterFullyCollapsed();
+            }
+        });
     }
 
     @Override
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
index 4754118..160268b 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
@@ -62,13 +62,13 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.dagger.StatusBarDependenciesModule;
-import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.init.NotificationsController;
+import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
+import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.row.NotificationRowModule;
@@ -146,7 +146,7 @@
             RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
             NotificationGutsManager notificationGutsManager,
             NotificationLogger notificationLogger,
-            NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+            NotificationInterruptStateProvider notificationInterruptionStateProvider,
             NotificationViewHierarchyManager notificationViewHierarchyManager,
             KeyguardViewMediator keyguardViewMediator,
             NotificationAlertingManager notificationAlertingManager,
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
index f93faeb..ace50f3 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
@@ -545,7 +545,7 @@
             bundle.putString(META_DATA_PREFERENCE_KEYHINT, key);
         }
         try {
-            return provider.call(context.getPackageName(), context.getFeatureId(),
+            return provider.call(context.getPackageName(), context.getAttributionTag(),
                     uri.getAuthority(), method, uri.toString(), bundle);
         } catch (RemoteException e) {
             return null;
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index e8e1d0b..00f6bcb 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1039,7 +1039,7 @@
     <!-- Title for the accessibility preference to configure display color space correction. [CHAR LIMIT=NONE] -->
     <string name="accessibility_display_daltonizer_preference_title">Color correction</string>
     <!-- Subtitle for the accessibility preference to configure display color space correction. [CHAR LIMIT=NONE] -->
-    <string name="accessibility_display_daltonizer_preference_subtitle">Color correction helps people with colorblindness see more accurate colors</string>
+    <string name="accessibility_display_daltonizer_preference_subtitle">Color correction helps the device display more accurate colors. Color correction may be helpful for people with colorblindness.</string>
     <!-- Summary shown for color space correction preference when its value is overridden by another preference [CHAR LIMIT=35] -->
     <string name="daltonizer_type_overridden">Overridden by <xliff:g id="title" example="Simulate color space">%1$s</xliff:g></string>
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java
index 68a3729..245b7843 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java
@@ -8,7 +8,6 @@
 
 import android.app.AppOpsManager;
 import android.app.AppOpsManager.OpEntry;
-import android.app.AppOpsManager.OpFeatureEntry;
 import android.app.AppOpsManager.PackageOps;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -163,6 +162,6 @@
             AppOpsManager.OP_FLAG_SELF), new AppOpsManager.NoteOpEvent(time, -1, null));
 
         return new OpEntry(op, AppOpsManager.MODE_ALLOWED, Collections.singletonMap(null,
-                new OpFeatureEntry(op, false, accessEvents, null)));
+                new AppOpsManager.AttributedOpEntry(op, false, accessEvents, null)));
     }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
index 3f8d758..cc9b931 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
@@ -7,7 +7,7 @@
 
 import android.app.AppOpsManager;
 import android.app.AppOpsManager.OpEntry;
-import android.app.AppOpsManager.OpFeatureEntry;
+import android.app.AppOpsManager.AttributedOpEntry;
 import android.app.AppOpsManager.PackageOps;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -18,8 +18,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.LongSparseArray;
-import android.util.LongSparseLongArray;
-import android.util.Pair;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -164,6 +162,6 @@
                 AppOpsManager.OP_FLAG_SELF), new AppOpsManager.NoteOpEvent(time, duration, null));
 
         return new OpEntry(op, AppOpsManager.MODE_ALLOWED, Collections.singletonMap(null,
-                new OpFeatureEntry(op, false, accessEvents, null)));
+                new AttributedOpEntry(op, false, accessEvents, null)));
     }
 }
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 1e888da..18d8f14 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
@@ -19,7 +19,6 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -121,8 +120,6 @@
 
         verify(currentDevice).disconnect();
         verify(device).connect();
-        verify(mCallback).onSelectedDeviceStateChanged(any(),
-                eq(LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED));
     }
 
     @Test
@@ -368,7 +365,8 @@
         mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_2);
 
         assertThat(mLocalMediaManager.getCurrentConnectedDevice()).isEqualTo(device2);
-        verify(mCallback).onDeviceAttributesChanged();
+        verify(mCallback).onSelectedDeviceStateChanged(device2,
+                LocalMediaManager.MediaDeviceState.STATE_CONNECTED);
     }
 
     @Test
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 8fa98c8..d350d9d 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -141,6 +141,8 @@
         Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL,
         Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK,
         Settings.Secure.UI_NIGHT_MODE,
+        Settings.Secure.DARK_THEME_CUSTOM_START_TIME,
+        Settings.Secure.DARK_THEME_CUSTOM_END_TIME,
         Settings.Secure.LOCK_SCREEN_WHEN_TRUST_LOST,
         Settings.Secure.SKIP_DIRECTION,
         Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 75c5f95..4d33b62 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -28,6 +28,7 @@
 import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.NULLABLE_COMPONENT_NAME_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.PACKAGE_NAME_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.TILE_LIST_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.TTS_LIST_VALIDATOR;
 
@@ -235,7 +236,9 @@
         VALIDATORS.put(Secure.AWARE_TAP_PAUSE_TOUCH_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.ODI_CAPTIONS_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.DARK_MODE_DIALOG_SEEN, BOOLEAN_VALIDATOR);
-        VALIDATORS.put(Secure.UI_NIGHT_MODE, new InclusiveIntegerRangeValidator(0, 2));
+        VALIDATORS.put(Secure.UI_NIGHT_MODE, NON_NEGATIVE_INTEGER_VALIDATOR);
+        VALIDATORS.put(Secure.DARK_THEME_CUSTOM_START_TIME, NONE_NEGATIVE_LONG_VALIDATOR);
+        VALIDATORS.put(Secure.DARK_THEME_CUSTOM_END_TIME, NONE_NEGATIVE_LONG_VALIDATOR);
         VALIDATORS.put(Secure.GLOBAL_ACTIONS_PANEL_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.AWARE_LOCK_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.DISPLAY_DENSITY_FORCED, NON_NEGATIVE_INTEGER_VALIDATOR);
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
index 71c7544..8d5c6e6 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
@@ -207,4 +207,15 @@
 
     static final Validator ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR =
             new AccessibilityShortcutTargetListValidator();
+
+    static final Validator NONE_NEGATIVE_LONG_VALIDATOR = new Validator() {
+        @Override
+        public boolean validate(String value) {
+            try {
+                return Long.parseLong(value) >= 0;
+            } catch (NumberFormatException e) {
+                return false;
+            }
+        }
+    };
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 36bb8ef..b6e31d2 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -75,6 +75,9 @@
         sBroadcastOnRestore.add(Settings.Secure.ENABLED_VR_LISTENERS);
         sBroadcastOnRestore.add(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
         sBroadcastOnRestore.add(Settings.Global.BLUETOOTH_ON);
+        sBroadcastOnRestore.add(Settings.Secure.UI_NIGHT_MODE);
+        sBroadcastOnRestore.add(Settings.Secure.DARK_THEME_CUSTOM_START_TIME);
+        sBroadcastOnRestore.add(Settings.Secure.DARK_THEME_CUSTOM_END_TIME);
     }
 
     private interface SettingsLookup {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 2dc6f39..5a9d749 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2778,6 +2778,11 @@
         public boolean insertSettingLocked(int type, int userId, String name, String value,
                 String tag, boolean makeDefault, boolean forceNonSystemPackage, String packageName,
                 boolean forceNotify, Set<String> criticalSettings, boolean overrideableByRestore) {
+            if (overrideableByRestore != Settings.DEFAULT_OVERRIDEABLE_BY_RESTORE) {
+                getContext().enforceCallingOrSelfPermission(
+                        Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE,
+                        "Caller is not allowed to modify settings overrideable by restore");
+            }
             final int key = makeKey(type, userId);
 
             boolean success = false;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index cd62420..2d351c7 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -1259,7 +1259,8 @@
         public boolean reset() {
             // overrideableByRestore = true as resetting to default value isn't considered a
             // modification.
-            return update(this.defaultValue, false, packageName, null, true, true);
+            return update(this.defaultValue, false, packageName, null, true, true,
+                    /* resetToDefault */ true);
         }
 
         public boolean isTransient() {
@@ -1272,6 +1273,13 @@
 
         public boolean update(String value, boolean setDefault, String packageName, String tag,
                 boolean forceNonSystemPackage, boolean overrideableByRestore) {
+            return update(value, setDefault, packageName, tag, forceNonSystemPackage,
+                    overrideableByRestore, /* resetToDefault */ false);
+        }
+
+        private boolean update(String value, boolean setDefault, String packageName, String tag,
+                boolean forceNonSystemPackage, boolean overrideableByRestore,
+                boolean resetToDefault) {
             if (NULL_VALUE.equals(value)) {
                 value = null;
             }
@@ -1305,7 +1313,7 @@
             }
 
             // isValuePreservedInRestore shouldn't change back to false if it has been set to true.
-            boolean isPreserved = this.isValuePreservedInRestore || !overrideableByRestore;
+            boolean isPreserved = shouldPreserveSetting(overrideableByRestore, resetToDefault);
 
             // Is something gonna change?
             if (Objects.equals(value, this.value)
@@ -1329,6 +1337,17 @@
                     + " packageName=" + packageName + " tag=" + tag
                     + " defaultFromSystem=" + defaultFromSystem + "}";
         }
+
+        private boolean shouldPreserveSetting(boolean overrideableByRestore,
+                boolean resetToDefault) {
+            if (resetToDefault) {
+                // By default settings are not marked as preserved.
+                return false;
+            }
+
+            // isValuePreservedInRestore shouldn't change back to false if it has been set to true.
+            return this.isValuePreservedInRestore || !overrideableByRestore;
+        }
     }
 
     /**
diff --git a/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java b/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java
index bb9e6f6..9134d87 100644
--- a/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java
@@ -277,6 +277,27 @@
     }
 
     @Test
+    public void testPositiveLongValidator_zero() {
+        assertTrue(SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR.validate("0"));
+    }
+
+    @Test
+    public void testPositiveLongValidator_negative() {
+        assertFalse(SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR.validate("-5"));
+    }
+
+
+    @Test
+    public void testPositiveLongValidator_positive() {
+        assertTrue(SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR.validate("5"));
+    }
+
+    @Test
+    public void testPositiveLongValidator_floatFormat() {
+        assertFalse(SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR.validate("4.4756"));
+    }
+
+    @Test
     public void testTTSListValidator_withNullInput_returnsFalse() {
         assertFalse(SettingsValidators.TTS_LIST_VALIDATOR.validate(null));
     }
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
index b855d87..6a3c661 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
@@ -241,6 +241,18 @@
         assertTrue(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
     }
 
+    public void testResetSetting_preservedFlagIsReset() {
+        SettingsState settingsState = getSettingStateObject();
+        // Initialize the setting.
+        settingsState.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
+        // Update the setting so that preserved flag is set.
+        settingsState.insertSettingLocked(SETTING_NAME, "2", null, false, TEST_PACKAGE);
+
+        settingsState.resetSettingLocked(SETTING_NAME);
+        assertFalse(settingsState.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
+
+    }
+
     private SettingsState getSettingStateObject() {
         SettingsState settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1,
                 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 181e0c0..8f859b2 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -275,6 +275,9 @@
     <!-- Permission needed to test registering pull atom callbacks -->
     <uses-permission android:name="android.permission.REGISTER_STATS_PULL_ATOM" />
 
+    <!-- Permission needed to modify settings overrideable by restore in CTS tests -->
+    <uses-permission android:name="android.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE" />
+
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index cff958f..c036b04 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -12,6 +12,9 @@
             "include-annotation": "android.platform.test.scenario.annotation.Scenario"
         },
         {
+            "exclude-annotation": "org.junit.Ignore"
+        },
+        {
             "exclude-annotation": "androidx.test.filters.FlakyTest"
         }
       ]
@@ -28,6 +31,9 @@
             "include-annotation": "android.platform.test.scenario.annotation.Scenario"
         },
         {
+            "exclude-annotation": "org.junit.Ignore"
+        },
+        {
             "exclude-annotation": "androidx.test.filters.FlakyTest"
         },
         {
diff --git a/packages/SystemUI/res/color/control_background.xml b/packages/SystemUI/res/color/control_background.xml
deleted file mode 100644
index 977310c..0000000
--- a/packages/SystemUI/res/color/control_background.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-  <item android:state_enabled="false"
-        android:color="@color/control_default_background" />
-  <item android:color="@color/GM2_blue_200"
-        android:alpha="0.2" />
-</selector>
diff --git a/packages/SystemUI/res/color/light_background.xml b/packages/SystemUI/res/color/light_background.xml
deleted file mode 100644
index 1299464..0000000
--- a/packages/SystemUI/res/color/light_background.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-  <item android:state_enabled="false"
-        android:color="@color/control_default_background" />
-  <item android:color="@color/GM2_yellow_200"
-        android:alpha="0.2" />
-</selector>
diff --git a/packages/SystemUI/res/color/thermo_cool_background.xml b/packages/SystemUI/res/color/thermo_cool_background.xml
deleted file mode 100644
index 977310c..0000000
--- a/packages/SystemUI/res/color/thermo_cool_background.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-  <item android:state_enabled="false"
-        android:color="@color/control_default_background" />
-  <item android:color="@color/GM2_blue_200"
-        android:alpha="0.2" />
-</selector>
diff --git a/packages/SystemUI/res/color/thermo_heat_background.xml b/packages/SystemUI/res/color/thermo_heat_background.xml
deleted file mode 100644
index 2709ebe..0000000
--- a/packages/SystemUI/res/color/thermo_heat_background.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-  <item android:state_enabled="false"
-        android:color="@color/control_default_background" />
-  <item android:color="@color/GM2_red_200"
-        android:alpha="0.2" />
-</selector>
diff --git a/packages/SystemUI/res/layout/controls_base_item.xml b/packages/SystemUI/res/layout/controls_base_item.xml
index 374e3b1..c58f572 100644
--- a/packages/SystemUI/res/layout/controls_base_item.xml
+++ b/packages/SystemUI/res/layout/controls_base_item.xml
@@ -21,8 +21,9 @@
     android:layout_weight="1"
     android:layout_height="@dimen/control_height"
     android:padding="@dimen/control_padding"
-    android:clickable="true"
+    android:clickable="false"
     android:focusable="true"
+    android:screenReaderFocusable="true"
     android:layout_marginLeft="@dimen/control_base_item_margin"
     android:layout_marginRight="@dimen/control_base_item_margin"
     android:background="@drawable/control_background">
@@ -32,6 +33,8 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:paddingTop="@dimen/control_padding_adjustment"
+        android:clickable="false"
+        android:focusable="false"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent" />
 
@@ -42,6 +45,8 @@
         android:textAppearance="@style/TextAppearance.Control.Status"
         android:paddingTop="@dimen/control_padding_adjustment"
         android:paddingStart="@dimen/control_status_padding"
+        android:clickable="false"
+        android:focusable="false"
         app:layout_constraintBottom_toBottomOf="@+id/icon"
         app:layout_constraintStart_toEndOf="@+id/icon" />
 
@@ -52,6 +57,8 @@
         android:textAppearance="@style/TextAppearance.Control.Status"
         android:paddingTop="@dimen/control_padding_adjustment"
         android:paddingStart="@dimen/control_status_padding"
+        android:clickable="false"
+        android:focusable="false"
         app:layout_constraintBottom_toBottomOf="@+id/icon"
         app:layout_constraintStart_toEndOf="@+id/status" />
 
@@ -62,6 +69,8 @@
         android:textAppearance="@style/TextAppearance.Control.Title"
         android:paddingLeft="@dimen/control_padding_adjustment"
         android:paddingRight="@dimen/control_padding_adjustment"
+        android:clickable="false"
+        android:focusable="false"
         app:layout_constraintBottom_toTopOf="@+id/subtitle"
         app:layout_constraintStart_toStartOf="parent" />
 
@@ -73,6 +82,8 @@
         android:paddingLeft="@dimen/control_padding_adjustment"
         android:paddingRight="@dimen/control_padding_adjustment"
         android:paddingBottom="@dimen/control_padding_adjustment"
+        android:clickable="false"
+        android:focusable="false"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintStart_toStartOf="parent"/>
 
diff --git a/packages/SystemUI/res/layout/controls_onboarding.xml b/packages/SystemUI/res/layout/controls_onboarding.xml
new file mode 100644
index 0000000..577a3b4
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_onboarding.xml
@@ -0,0 +1,65 @@
+<?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:layout_height="wrap_content"
+    android:layout_width="wrap_content"
+    android:padding="4dp"
+    android:orientation="vertical">
+
+    <View
+        android:id="@+id/arrow"
+        android:elevation="2dp"
+        android:layout_width="10dp"
+        android:layout_height="8dp"
+        android:layout_marginBottom="-2dp"
+        android:layout_gravity="center_horizontal"/>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingStart="24dp"
+        android:paddingEnd="4dp"
+        android:background="@drawable/recents_onboarding_toast_rounded_background"
+        android:layout_gravity="center_horizontal"
+        android:elevation="2dp"
+        android:orientation="horizontal">
+
+        <TextView
+            android:id="@+id/onboarding_text"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_gravity="center_vertical"
+            android:textColor="?attr/wallpaperTextColor"
+            android:textSize="16sp"/>
+        <ImageView
+            android:id="@+id/dismiss"
+            android:layout_width="40dp"
+            android:layout_height="40dp"
+            android:layout_gravity="center_vertical"
+            android:padding="10dp"
+            android:layout_marginStart="2dp"
+            android:layout_marginEnd="2dp"
+            android:alpha="0.7"
+            android:src="@drawable/ic_close_white"
+            android:tint="?attr/wallpaperTextColor"
+            android:background="?android:attr/selectableItemBackgroundBorderless"
+            android:contentDescription="@string/accessibility_desc_close"/>
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/controls_structure_page.xml b/packages/SystemUI/res/layout/controls_structure_page.xml
index 2c7e168..047ab98 100644
--- a/packages/SystemUI/res/layout/controls_structure_page.xml
+++ b/packages/SystemUI/res/layout/controls_structure_page.xml
@@ -15,17 +15,10 @@
   ~ limitations under the License.
   -->
 
-<androidx.core.widget.NestedScrollView
+<androidx.recyclerview.widget.RecyclerView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/listAll"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
-    android:layout_marginTop="@dimen/controls_management_list_margin">
-
-    <androidx.recyclerview.widget.RecyclerView
-        android:id="@+id/listAll"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-    />
-
-</androidx.core.widget.NestedScrollView>
\ No newline at end of file
+    android:layout_marginTop="@dimen/controls_management_list_margin"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 80c1ac8..56e2b06 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -225,4 +225,8 @@
     <color name="control_default_background">@color/GM2_grey_900</color>
     <color name="control_list_popup_background">@*android:color/background_floating_material_dark</color>
     <color name="control_spinner_dropdown">@*android:color/foreground_material_dark</color>
+    <color name="control_enabled_light_background">@color/GM2_yellow_200</color>
+    <color name="control_enabled_thermo_heat_background">@color/GM2_red_200</color>
+    <color name="control_enabled_thermo_cool_background">@color/GM2_blue_200</color>
+    <color name="control_enabled_default_background">@color/GM2_blue_200</color>
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index d160829..217abbe 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -117,7 +117,7 @@
 
     <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
     <string name="quick_settings_tiles_stock" translatable="false">
-        wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,controls,screenrecord
+        wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord
     </string>
 
     <!-- The tiles to display in QuickSettings -->
@@ -468,6 +468,9 @@
     <!-- On debuggable builds, alert the user if SystemUI PSS goes over this number (in kb) -->
     <integer name="watch_heap_limit">256000</integer>
 
+    <!-- Animation duration for resizing of PIP when entering/exiting. -->
+    <integer name="config_pipResizeAnimationDuration">425</integer>
+
     <!-- Allow dragging the PIP to a location to close it -->
     <bool name="config_pipEnableDismissDragToEdge">true</bool>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 291db65..e45cbec 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -124,9 +124,6 @@
     <!-- Increased height of a collapsed media notification in the status bar -->
     <dimen name="notification_min_height_media">160dp</dimen>
 
-    <!-- Increased height of a collapsed messaging notification in the status bar -->
-    <dimen name="notification_min_height_messaging">118dp</dimen>
-
     <!-- Height of a small notification in the status bar which was used before android N -->
     <dimen name="notification_min_height_legacy">64dp</dimen>
 
@@ -1234,7 +1231,7 @@
     <dimen name="control_height">106dp</dimen>
     <dimen name="control_padding">12dp</dimen>
     <dimen name="control_padding_adjustment">4dp</dimen>
-    <dimen name="control_status_normal">12sp</dimen>
+    <dimen name="control_status_normal">14sp</dimen>
     <dimen name="control_status_expanded">18sp</dimen>
     <dimen name="control_base_item_margin">2dp</dimen>
     <dimen name="control_status_padding">3dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index caf22fe..3543073 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2631,10 +2631,7 @@
     <string name="controls_favorite_default_title">Controls</string>
     <!-- Controls management controls screen subtitle [CHAR LIMIT=NONE] -->
     <string name="controls_favorite_subtitle">Choose controls for quick access</string>
-    <!-- Controls management controls screen favorites header [CHAR LIMIT=50] -->
-    <string name="controls_favorite_header_favorites">Favorites</string>
-    <!-- Controls management controls screen all header [CHAR LIMIT=50] -->
-    <string name="controls_favorite_header_all">All</string>
+
     <!-- Controls management controls screen error on load message [CHAR LIMIT=60] -->
     <string name="controls_favorite_load_error">The list of all controls could not be loaded.</string>
     <!-- Controls management controls screen header for Other zone [CHAR LIMIT=60] -->
@@ -2653,4 +2650,7 @@
     <string name="controls_pin_verify">Verify device PIN</string>
     <!-- Controls PIN entry dialog, text hint [CHAR LIMIT=30] -->
     <string name="controls_pin_instructions">Enter PIN</string>
+
+    <!-- Tooltip to show in management screen when there are multiple structures [CHAR_LIMIT=50] -->
+    <string name="controls_structure_tooltip">Swipe to see other structures</string>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 125dd8f..20b88a1 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -650,6 +650,7 @@
     <!-- Controls styles -->
     <style name="Theme.ControlsManagement" parent="@android:style/Theme.DeviceDefault.NoActionBar">
         <item name="android:windowIsTranslucent">false</item>
+        <item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item>
     </style>
 
     <style name="TextAppearance.Control">
@@ -676,7 +677,7 @@
 
     <style name="TextAppearance.Control.Status">
         <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
-        <item name="android:textSize">@dimen/control_text_size</item>
+        <item name="android:textSize">@dimen/control_status_normal</item>
         <item name="android:textColor">@color/control_primary_text</item>
     </style>
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
index 1c6223b..9e9b9dc 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
@@ -37,7 +37,8 @@
     public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) { }
     public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { }
     public void onActivityUnpinned() { }
-    public void onPinnedActivityRestartAttempt(boolean clearedTask) { }
+    public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
+            boolean clearedTask) { }
     public void onActivityForcedResizable(String packageName, int taskId, int reason) { }
     public void onActivityDismissingDockedStack() { }
     public void onActivityLaunchOnSecondaryDisplayFailed() { }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index cbdd3f8..ce9cbab 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -30,6 +30,7 @@
 import android.os.Trace;
 import android.util.Log;
 
+import com.android.internal.os.SomeArgs;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 
 import java.util.ArrayList;
@@ -120,11 +121,14 @@
     }
 
     @Override
-    public void onPinnedActivityRestartAttempt(boolean clearedTask)
-            throws RemoteException {
-        mHandler.removeMessages(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
-        mHandler.obtainMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT, clearedTask ? 1 : 0, 0)
-                .sendToTarget();
+    public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
+            boolean clearedTask) throws RemoteException {
+        final SomeArgs args = SomeArgs.obtain();
+        args.arg1 = task;
+        args.argi1 = homeTaskVisible ? 1 : 0;
+        args.argi2 = clearedTask ? 1 : 0;
+        mHandler.removeMessages(H.ON_ACTIVITY_RESTART_ATTEMPT);
+        mHandler.obtainMessage(H.ON_ACTIVITY_RESTART_ATTEMPT, args).sendToTarget();
     }
 
     @Override
@@ -236,7 +240,7 @@
         private static final int ON_TASK_STACK_CHANGED = 1;
         private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
         private static final int ON_ACTIVITY_PINNED = 3;
-        private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 4;
+        private static final int ON_ACTIVITY_RESTART_ATTEMPT = 4;
         private static final int ON_ACTIVITY_FORCED_RESIZABLE = 6;
         private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 7;
         private static final int ON_TASK_PROFILE_LOCKED = 8;
@@ -296,10 +300,14 @@
                         }
                         break;
                     }
-                    case ON_PINNED_ACTIVITY_RESTART_ATTEMPT: {
+                    case ON_ACTIVITY_RESTART_ATTEMPT: {
+                        final SomeArgs args = (SomeArgs) msg.obj;
+                        final RunningTaskInfo task = (RunningTaskInfo) args.arg1;
+                        final boolean homeTaskVisible = args.argi1 != 0;
+                        final boolean clearedTask = args.argi2 != 0;
                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
-                            mTaskStackListeners.get(i).onPinnedActivityRestartAttempt(
-                                    msg.arg1 != 0);
+                            mTaskStackListeners.get(i).onActivityRestartAttempt(task,
+                                    homeTaskVisible, clearedTask);
                         }
                         break;
                     }
@@ -419,6 +427,9 @@
                     }
                 }
             }
+            if (msg.obj instanceof SomeArgs) {
+                ((SomeArgs) msg.obj).recycle();
+            }
         }
     }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperManagerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperManagerCompat.java
index b813e21..7570c2c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperManagerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperManagerCompat.java
@@ -18,7 +18,11 @@
 
 import android.app.WallpaperManager;
 import android.content.Context;
+import android.os.IBinder;
 
+/**
+ * @see WallpaperManager
+ */
 public class WallpaperManagerCompat {
     private final WallpaperManager mWallpaperManager;
 
@@ -26,7 +30,10 @@
         mWallpaperManager = context.getSystemService(WallpaperManager.class);
     }
 
-    public void setWallpaperZoomOut(float zoom) {
-        mWallpaperManager.setWallpaperZoomOut(zoom);
+    /**
+     * @see WallpaperManager#setWallpaperZoomOut(IBinder, float)
+     */
+    public void setWallpaperZoomOut(IBinder windowToken, float zoom) {
+        mWallpaperManager.setWallpaperZoomOut(windowToken, zoom);
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 3cf07d1..ba8a1a9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -19,9 +19,7 @@
 import static android.view.ViewRootImpl.sNewInsetsMode;
 import static android.view.WindowInsets.Type.ime;
 import static android.view.WindowInsets.Type.systemBars;
-
 import static com.android.systemui.DejankUtils.whitelistIpcs;
-
 import static java.lang.Integer.max;
 
 import android.app.Activity;
@@ -30,6 +28,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.ColorStateList;
+import android.graphics.Rect;
 import android.metrics.LogMaker;
 import android.os.Handler;
 import android.os.Looper;
@@ -512,8 +511,6 @@
         boolean finish = false;
         boolean strongAuth = false;
         int eventSubtype = -1;
-        mCurrentSecuritySelection = whitelistIpcs(() ->
-                mSecurityModel.getSecurityMode(targetUserId));
         if (mUpdateMonitor.getUserHasTrust(targetUserId)) {
             finish = true;
             eventSubtype = BOUNCER_DISMISS_EXTENDED_ACCESS;
@@ -521,8 +518,13 @@
             finish = true;
             eventSubtype = BOUNCER_DISMISS_BIOMETRIC;
         } else if (SecurityMode.None == mCurrentSecuritySelection) {
-            finish = true; // no security required
-            eventSubtype = BOUNCER_DISMISS_NONE_SECURITY;
+            SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
+            if (SecurityMode.None == securityMode) {
+                finish = true; // no security required
+                eventSubtype = BOUNCER_DISMISS_NONE_SECURITY;
+            } else {
+                showSecurityScreen(securityMode); // switch to the alternate security view
+            }
         } else if (authenticated) {
             switch (mCurrentSecuritySelection) {
                 case Pattern:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 4508fc7..431c451 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -379,7 +379,7 @@
         if (DEBUG_SIM_STATES) {
             Log.v(TAG, "onSubscriptionInfoChanged()");
             List<SubscriptionInfo> sil = mSubscriptionManager
-                    .getActiveAndHiddenSubscriptionInfoList();
+                    .getCompleteActiveSubscriptionInfoList();
             if (sil != null) {
                 for (SubscriptionInfo subInfo : sil) {
                     Log.v(TAG, "SubInfo:" + subInfo);
@@ -433,10 +433,10 @@
     public List<SubscriptionInfo> getSubscriptionInfo(boolean forceReload) {
         List<SubscriptionInfo> sil = mSubscriptionInfo;
         if (sil == null || forceReload) {
-            sil = mSubscriptionManager.getActiveAndHiddenSubscriptionInfoList();
+            sil = mSubscriptionManager.getCompleteActiveSubscriptionInfoList();
         }
         if (sil == null) {
-            // getActiveAndHiddenSubscriptionInfoList was null callers expect an empty list.
+            // getCompleteActiveSubscriptionInfoList was null callers expect an empty list.
             mSubscriptionInfo = new ArrayList<SubscriptionInfo>();
         } else {
             mSubscriptionInfo = sil;
@@ -1086,7 +1086,7 @@
                 mHandler.sendEmptyMessage(MSG_TIME_UPDATE);
             } else if (Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
                 final Message msg = mHandler.obtainMessage(
-                        MSG_TIMEZONE_UPDATE, intent.getStringExtra("time-zone"));
+                        MSG_TIMEZONE_UPDATE, intent.getStringExtra(Intent.EXTRA_TIMEZONE));
                 mHandler.sendMessage(msg);
             } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
 
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index a868cf5..b6152da 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -71,12 +71,11 @@
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ChannelEditorDialogController;
 import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
@@ -289,7 +288,6 @@
     @Inject Lazy<NotificationLogger> mNotificationLogger;
     @Inject Lazy<NotificationViewHierarchyManager> mNotificationViewHierarchyManager;
     @Inject Lazy<NotificationFilter> mNotificationFilter;
-    @Inject Lazy<NotificationInterruptionStateProvider> mNotificationInterruptionStateProvider;
     @Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil;
     @Inject Lazy<SmartReplyController> mSmartReplyController;
     @Inject Lazy<RemoteInputQuickSettingsDisabler> mRemoteInputQuickSettingsDisabler;
@@ -489,8 +487,6 @@
         mProviders.put(NotificationViewHierarchyManager.class,
                 mNotificationViewHierarchyManager::get);
         mProviders.put(NotificationFilter.class, mNotificationFilter::get);
-        mProviders.put(NotificationInterruptionStateProvider.class,
-                mNotificationInterruptionStateProvider::get);
         mProviders.put(KeyguardDismissUtil.class, mKeyguardDismissUtil::get);
         mProviders.put(SmartReplyController.class, mSmartReplyController::get);
         mProviders.put(RemoteInputQuickSettingsDisabler.class,
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index dbcdead..23fa645 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -203,6 +203,11 @@
             }
         }
 
+        @Override
+        public boolean shouldZoomOutWallpaper() {
+            return true;
+        }
+
         private void waitForBackgroundRendering() {
             synchronized (mMonitor) {
                 try {
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 5e6589f..6aa2326 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -59,7 +59,8 @@
             Key.TOUCHED_RINGER_TOGGLE,
             Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP,
             Key.HAS_SEEN_BUBBLES_EDUCATION,
-            Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION
+            Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION,
+            Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT
     })
     public @interface Key {
         @Deprecated
@@ -107,6 +108,7 @@
         String HAS_SEEN_ODI_CAPTIONS_TOOLTIP = "HasSeenODICaptionsTooltip";
         String HAS_SEEN_BUBBLES_EDUCATION = "HasSeenBubblesOnboarding";
         String HAS_SEEN_BUBBLES_MANAGE_EDUCATION = "HasSeenBubblesManageOnboarding";
+        String CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT = "ControlsStructureSwipeTooltipCount";
     }
 
     public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) {
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index cab9f18..537a812 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -48,6 +48,7 @@
 import android.graphics.Path;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.graphics.Region;
 import android.hardware.display.DisplayManager;
 import android.os.Handler;
@@ -725,7 +726,8 @@
         private final Rect mBoundingRect = new Rect();
         private final Path mBoundingPath = new Path();
         // Don't initialize these yet because they may never exist
-        private Rect mProtectionRect;
+        private RectF mProtectionRect;
+        private RectF mProtectionRectOrig;
         private Path mProtectionPath;
         private Path mProtectionPathOrig;
         private Rect mTotalBounds = new Rect();
@@ -818,7 +820,11 @@
                 mProtectionPath = new Path();
             }
             mProtectionPathOrig.set(protectionPath);
-            mProtectionRect = pathBounds;
+            if (mProtectionRectOrig == null) {
+                mProtectionRectOrig = new RectF();
+                mProtectionRect = new RectF();
+            }
+            mProtectionRectOrig.set(pathBounds);
         }
 
         void setShowProtection(boolean shouldShow) {
@@ -898,6 +904,7 @@
                 // Reset the protection path so we don't aggregate rotations
                 mProtectionPath.set(mProtectionPathOrig);
                 mProtectionPath.transform(m);
+                m.mapRect(mProtectionRect, mProtectionRectOrig);
             }
         }
 
@@ -964,7 +971,8 @@
             if (mShowProtection) {
                 // Make sure that our measured height encompases the protection
                 mTotalBounds.union(mBoundingRect);
-                mTotalBounds.union(mProtectionRect);
+                mTotalBounds.union((int) mProtectionRect.left, (int) mProtectionRect.top,
+                        (int) mProtectionRect.right, (int) mProtectionRect.bottom);
                 setMeasuredDimension(
                         resolveSizeAndState(mTotalBounds.width(), widthMeasureSpec, 0),
                         resolveSizeAndState(mTotalBounds.height(), heightMeasureSpec, 0));
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 22c2c7e..406e7ce 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -83,11 +83,11 @@
 import com.android.systemui.statusbar.NotificationRemoveInterceptor;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.ShadeController;
@@ -169,7 +169,7 @@
     // Callback that updates BubbleOverflowActivity on data change.
     @Nullable private Runnable mOverflowCallback = null;
 
-    private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+    private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
     private IStatusBarService mBarService;
 
     // Used for determining view rect for touch interaction
@@ -279,7 +279,7 @@
             ShadeController shadeController,
             BubbleData data,
             ConfigurationController configurationController,
-            NotificationInterruptionStateProvider interruptionStateProvider,
+            NotificationInterruptStateProvider interruptionStateProvider,
             ZenModeController zenModeController,
             NotificationLockscreenUserManager notifUserManager,
             NotificationGroupManager groupManager,
@@ -304,7 +304,7 @@
             BubbleData data,
             @Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
             ConfigurationController configurationController,
-            NotificationInterruptionStateProvider interruptionStateProvider,
+            NotificationInterruptStateProvider interruptionStateProvider,
             ZenModeController zenModeController,
             NotificationLockscreenUserManager notifUserManager,
             NotificationGroupManager groupManager,
@@ -316,7 +316,7 @@
         dumpManager.registerDumpable(TAG, this);
         mContext = context;
         mShadeController = shadeController;
-        mNotificationInterruptionStateProvider = interruptionStateProvider;
+        mNotificationInterruptStateProvider = interruptionStateProvider;
         mNotifUserManager = notifUserManager;
         mZenModeController = zenModeController;
         mFloatingContentCoordinator = floatingContentCoordinator;
@@ -632,7 +632,7 @@
         for (NotificationEntry e :
                 mNotificationEntryManager.getActiveNotificationsForCurrentUser()) {
             if (savedBubbleKeys.contains(e.getKey())
-                    && mNotificationInterruptionStateProvider.shouldBubbleUp(e)
+                    && mNotificationInterruptStateProvider.shouldBubbleUp(e)
                     && canLaunchInActivityView(mContext, e)) {
                 updateBubble(e, /* suppressFlyout= */ true);
             }
@@ -894,7 +894,7 @@
         boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
                 mContext, entry, previouslyUserCreated, userBlocked);
 
-        if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
+        if (mNotificationInterruptStateProvider.shouldBubbleUp(entry)
                 && (canLaunchInActivityView(mContext, entry) || wasAdjusted)) {
             if (wasAdjusted && !previouslyUserCreated) {
                 // Gotta treat the auto-bubbled / whitelisted packaged bubbles as usercreated
@@ -910,7 +910,7 @@
         boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
                 mContext, entry, previouslyUserCreated, userBlocked);
 
-        boolean shouldBubble = mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
+        boolean shouldBubble = mNotificationInterruptStateProvider.shouldBubbleUp(entry)
                 && (canLaunchInActivityView(mContext, entry) || wasAdjusted);
         if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.getKey())) {
             // It was previously a bubble but no longer a bubble -- lets remove it
@@ -1227,6 +1227,17 @@
         }
 
         @Override
+        public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
+                boolean clearedTask) {
+            for (Bubble b : mBubbleData.getBubbles()) {
+                if (b.getDisplayId() == task.displayId) {
+                    expandStackAndSelectBubble(b.getKey());
+                    return;
+                }
+            }
+        }
+
+        @Override
         public void onActivityLaunchOnSecondaryDisplayRerouted() {
             if (mStackView != null) {
                 mBubbleData.setExpanded(false);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index aedd2db..0778d5b 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -424,6 +424,7 @@
 
     private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
 
+    @Nullable
     private BubbleOverflow mBubbleOverflow;
 
     private boolean mShouldShowUserEducation;
@@ -1386,6 +1387,11 @@
         if (DEBUG_BUBBLE_STACK_VIEW) {
             Log.d(TAG, "onBubbleDragStart: bubble=" + bubble);
         }
+
+        if (mBubbleOverflow != null && bubble.equals(mBubbleOverflow.getIconView())) {
+            return;
+        }
+
         mExpandedAnimationController.prepareForBubbleDrag(bubble, mMagneticTarget);
 
         // We're dragging an individual bubble, so set the magnetized object to the magnetized
@@ -1398,7 +1404,8 @@
 
     /** Called with the coordinates to which an individual bubble has been dragged. */
     public void onBubbleDragged(View bubble, float x, float y) {
-        if (!mIsExpanded || mIsExpansionAnimating) {
+        if (!mIsExpanded || mIsExpansionAnimating
+                || (mBubbleOverflow != null && bubble.equals(mBubbleOverflow.getIconView()))) {
             return;
         }
 
@@ -1413,7 +1420,8 @@
             Log.d(TAG, "onBubbleDragFinish: bubble=" + bubble);
         }
 
-        if (!mIsExpanded || mIsExpansionAnimating) {
+        if (!mIsExpanded || mIsExpansionAnimating
+                || (mBubbleOverflow != null && bubble.equals(mBubbleOverflow.getIconView()))) {
             return;
         }
 
@@ -1421,6 +1429,18 @@
         hideDismissTarget();
     }
 
+    /** Expands the clicked bubble. */
+    public void expandBubble(Bubble bubble) {
+        if (bubble.equals(mBubbleData.getSelectedBubble())) {
+            // If the bubble we're supposed to expand is the selected bubble, that means the
+            // overflow bubble is currently expanded. Don't tell BubbleData to set this bubble as
+            // selected, since it already is. Just call the stack's setSelectedBubble to expand it.
+            setSelectedBubble(bubble);
+        } else {
+            mBubbleData.setSelectedBubble(bubble);
+        }
+    }
+
     void onDragStart() {
         if (DEBUG_BUBBLE_STACK_VIEW) {
             Log.d(TAG, "onDragStart()");
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
index 0c5bef4..132c45f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
@@ -189,7 +189,7 @@
                     if (key == BubbleOverflow.KEY) {
                         mStack.showOverflow();
                     } else {
-                        mBubbleData.setSelectedBubble(mBubbleData.getBubbleWithKey(key));
+                        mStack.expandBubble(mBubbleData.getBubbleWithKey(key));
                     }
                 }
                 resetForNextGesture();
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 ac97d8a..27c9e98 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
@@ -25,8 +25,8 @@
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.ShadeController;
@@ -54,7 +54,7 @@
             ShadeController shadeController,
             BubbleData data,
             ConfigurationController configurationController,
-            NotificationInterruptionStateProvider interruptionStateProvider,
+            NotificationInterruptStateProvider interruptionStateProvider,
             ZenModeController zenModeController,
             NotificationLockscreenUserManager notifUserManager,
             NotificationGroupManager groupManager,
diff --git a/packages/SystemUI/src/com/android/systemui/controls/TooltipManager.kt b/packages/SystemUI/src/com/android/systemui/controls/TooltipManager.kt
new file mode 100644
index 0000000..6e17bc9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/TooltipManager.kt
@@ -0,0 +1,160 @@
+/*
+ * 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.controls
+
+import android.annotation.StringRes
+import android.content.Context
+import android.graphics.CornerPathEffect
+import android.graphics.drawable.ShapeDrawable
+import android.util.TypedValue
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.animation.AccelerateInterpolator
+import android.view.animation.DecelerateInterpolator
+import android.widget.TextView
+import com.android.systemui.Prefs
+import com.android.systemui.R
+import com.android.systemui.recents.TriangleShape
+
+/**
+ * Manager for showing an onboarding tooltip on screen.
+ *
+ * The tooltip can be made to appear below or above a point. The number of times it will appear
+ * is determined by an shared preference (defined in [Prefs]).
+ *
+ * @property context A context to use to inflate the views and retrieve shared preferences from
+ * @property preferenceName name of the preference to use to track the number of times the tooltip
+ *                          has been shown.
+ * @property maxTimesShown the maximum number of times to show the tooltip
+ * @property below whether the tooltip should appear below (with up pointing arrow) or above (down
+ *                 pointing arrow) the specified point.
+ * @see [TooltipManager.show]
+ */
+class TooltipManager(
+    context: Context,
+    private val preferenceName: String,
+    private val maxTimesShown: Int = 2,
+    private val below: Boolean = true
+) {
+
+    companion object {
+        private const val SHOW_DELAY_MS: Long = 500
+        private const val SHOW_DURATION_MS: Long = 300
+        private const val HIDE_DURATION_MS: Long = 100
+    }
+
+    private var shown = Prefs.getInt(context, preferenceName, 0)
+
+    val layout: ViewGroup =
+        LayoutInflater.from(context).inflate(R.layout.controls_onboarding, null) as ViewGroup
+    val preferenceStorer = { num: Int ->
+        Prefs.putInt(context, preferenceName, num)
+    }
+
+    init {
+        layout.alpha = 0f
+    }
+
+    private val textView = layout.requireViewById<TextView>(R.id.onboarding_text)
+    private val dismissView = layout.requireViewById<View>(R.id.dismiss).apply {
+        setOnClickListener {
+            hide(true)
+        }
+    }
+
+    private val arrowView = layout.requireViewById<View>(R.id.arrow).apply {
+        val typedValue = TypedValue()
+        context.theme.resolveAttribute(android.R.attr.colorAccent, typedValue, true)
+        val toastColor = context.resources.getColor(typedValue.resourceId, context.theme)
+        val arrowRadius = context.resources.getDimensionPixelSize(
+            R.dimen.recents_onboarding_toast_arrow_corner_radius)
+        val arrowLp = layoutParams
+        val arrowDrawable = ShapeDrawable(TriangleShape.create(
+            arrowLp.width.toFloat(), arrowLp.height.toFloat(), below))
+        val arrowPaint = arrowDrawable.paint
+        arrowPaint.color = toastColor
+        // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable.
+        arrowPaint.pathEffect = CornerPathEffect(arrowRadius.toFloat())
+        setBackground(arrowDrawable)
+    }
+
+    init {
+        if (!below) {
+            layout.removeView(arrowView)
+            layout.addView(arrowView)
+            (arrowView.layoutParams as ViewGroup.MarginLayoutParams).apply {
+                bottomMargin = topMargin
+                topMargin = 0
+            }
+        }
+    }
+
+    /**
+     * Show the tooltip
+     *
+     * @param stringRes the id of the string to show in the tooltip
+     * @param x horizontal position (w.r.t. screen) for the arrow point
+     * @param y vertical position (w.r.t. screen) for the arrow point
+     */
+    fun show(@StringRes stringRes: Int, x: Int, y: Int) {
+        if (!shouldShow()) return
+        textView.setText(stringRes)
+        shown++
+        preferenceStorer(shown)
+        layout.post {
+            val p = IntArray(2)
+            layout.getLocationOnScreen(p)
+            layout.translationX = (x - p[0] - layout.width / 2).toFloat()
+            layout.translationY = (y - p[1]).toFloat() - if (!below) layout.height else 0
+            if (layout.alpha == 0f) {
+                layout.animate()
+                    .alpha(1f)
+                    .withLayer()
+                    .setStartDelay(SHOW_DELAY_MS)
+                    .setDuration(SHOW_DURATION_MS)
+                    .setInterpolator(DecelerateInterpolator())
+                    .start()
+            }
+        }
+    }
+
+    /**
+     * Hide the tooltip
+     *
+     * @param animate whether to animate the fade out
+     */
+    fun hide(animate: Boolean = false) {
+        if (layout.alpha == 0f) return
+        layout.post {
+            if (animate) {
+                layout.animate()
+                    .alpha(0f)
+                    .withLayer()
+                    .setStartDelay(0)
+                    .setDuration(HIDE_DURATION_MS)
+                    .setInterpolator(AccelerateInterpolator())
+                    .start()
+            } else {
+                layout.animate().cancel()
+                layout.alpha = 0f
+            }
+        }
+    }
+
+    private fun shouldShow() = shown < maxTimesShown
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
index fd6e256..c5af436 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
@@ -38,8 +38,9 @@
      *
      * @param component The [ComponentName] of the service to bind
      * @param callback a callback to return the loaded controls to (or an error).
+     * @return a runnable to cancel the load
      */
-    fun bindAndLoad(component: ComponentName, callback: LoadCallback)
+    fun bindAndLoad(component: ComponentName, callback: LoadCallback): Runnable
 
     /**
      * Request to bind to the given service.
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
index 8f02c25..f8d4a39 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
@@ -31,7 +31,6 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.util.concurrency.DelayableExecutor
 import dagger.Lazy
-import java.util.concurrent.atomic.AtomicBoolean
 import javax.inject.Inject
 import javax.inject.Singleton
 
@@ -47,8 +46,6 @@
         private const val TAG = "ControlsBindingControllerImpl"
     }
 
-    private val refreshing = AtomicBoolean(false)
-
     private var currentUser = UserHandle.of(ActivityManager.getCurrentUser())
 
     override val currentUserId: Int
@@ -56,6 +53,12 @@
 
     private var currentProvider: ControlsProviderLifecycleManager? = null
 
+    /*
+     * Will track any active subscriber for subscribe/unsubscribe requests coming into
+     * this controller. Only one can be active at any time
+     */
+    private var statefulControlSubscriber: StatefulControlSubscriber? = null
+
     private val actionCallbackService = object : IControlsActionCallback.Stub() {
         override fun accept(
             token: IBinder,
@@ -66,27 +69,6 @@
         }
     }
 
-    private val subscriberService = object : IControlsSubscriber.Stub() {
-        override fun onSubscribe(token: IBinder, subs: IControlsSubscription) {
-            backgroundExecutor.execute(OnSubscribeRunnable(token, subs))
-        }
-
-        override fun onNext(token: IBinder, c: Control) {
-            if (!refreshing.get()) {
-                Log.d(TAG, "Refresh outside of window for token:$token")
-            } else {
-                backgroundExecutor.execute(OnNextRunnable(token, c))
-            }
-        }
-        override fun onError(token: IBinder, s: String) {
-            backgroundExecutor.execute(OnErrorRunnable(token, s))
-        }
-
-        override fun onComplete(token: IBinder) {
-            backgroundExecutor.execute(OnCompleteRunnable(token))
-        }
-    }
-
     @VisibleForTesting
     internal open fun createProviderManager(component: ComponentName):
             ControlsProviderLifecycleManager {
@@ -94,43 +76,45 @@
                 context,
                 backgroundExecutor,
                 actionCallbackService,
-                subscriberService,
                 currentUser,
                 component
         )
     }
 
     private fun retrieveLifecycleManager(component: ComponentName):
-            ControlsProviderLifecycleManager? {
+            ControlsProviderLifecycleManager {
         if (currentProvider != null && currentProvider?.componentName != component) {
             unbind()
         }
 
-        if (currentProvider == null) {
-            currentProvider = createProviderManager(component)
-        }
+        val provider = currentProvider ?: createProviderManager(component)
+        currentProvider = provider
 
-        return currentProvider
+        return provider
     }
 
     override fun bindAndLoad(
         component: ComponentName,
         callback: ControlsBindingController.LoadCallback
-    ) {
-        retrieveLifecycleManager(component)?.maybeBindAndLoad(LoadSubscriber(callback))
+    ): Runnable {
+        val subscriber = LoadSubscriber(callback)
+        retrieveLifecycleManager(component).maybeBindAndLoad(subscriber)
+        return subscriber.loadCancel()
     }
 
     override fun subscribe(structureInfo: StructureInfo) {
-        if (refreshing.compareAndSet(false, true)) {
-            val provider = retrieveLifecycleManager(structureInfo.componentName)
-            provider?.maybeBindAndSubscribe(structureInfo.controls.map { it.controlId })
-        }
+        // make sure this has happened. only allow one active subscription
+        unsubscribe()
+
+        statefulControlSubscriber = null
+        val provider = retrieveLifecycleManager(structureInfo.componentName)
+        val scs = StatefulControlSubscriber(lazyController.get(), provider, backgroundExecutor)
+        statefulControlSubscriber = scs
+        provider.maybeBindAndSubscribe(structureInfo.controls.map { it.controlId }, scs)
     }
 
     override fun unsubscribe() {
-        if (refreshing.compareAndSet(true, false)) {
-            currentProvider?.unsubscribe()
-        }
+        statefulControlSubscriber?.cancel()
     }
 
     override fun action(
@@ -138,20 +122,24 @@
         controlInfo: ControlInfo,
         action: ControlAction
     ) {
-        retrieveLifecycleManager(componentName)
-            ?.maybeBindAndSendAction(controlInfo.controlId, action)
+        if (statefulControlSubscriber == null) {
+            Log.w(TAG, "No actions can occur outside of an active subscription. Ignoring.")
+        } else {
+            retrieveLifecycleManager(componentName)
+                .maybeBindAndSendAction(controlInfo.controlId, action)
+        }
     }
 
     override fun bindService(component: ComponentName) {
-        retrieveLifecycleManager(component)?.bindService()
+        retrieveLifecycleManager(component).bindService()
     }
 
     override fun changeUser(newUser: UserHandle) {
         if (newUser == currentUser) return
 
+        unsubscribe()
         unbind()
-
-        refreshing.set(false)
+        currentProvider = null
         currentUser = newUser
     }
 
@@ -172,8 +160,8 @@
 
     override fun toString(): String {
         return StringBuilder("  ControlsBindingController:\n").apply {
-            append("    refreshing=${refreshing.get()}\n")
             append("    currentUser=$currentUser\n")
+            append("    StatefulControlSubscriber=$statefulControlSubscriber")
             append("    Providers=$currentProvider\n")
         }.toString()
     }
@@ -208,22 +196,6 @@
     ) : CallbackRunnable(token) {
         override fun doRun() {
             callback.accept(list)
-            provider?.unbindService()
-        }
-    }
-
-    private inner class OnNextRunnable(
-        token: IBinder,
-        val control: Control
-    ) : CallbackRunnable(token) {
-        override fun doRun() {
-            if (!refreshing.get()) {
-                Log.d(TAG, "onRefresh outside of window from:${provider?.componentName}")
-            }
-
-            provider?.let {
-                lazyController.get().refreshStatus(it.componentName, control)
-            }
         }
     }
 
@@ -232,33 +204,7 @@
         val subscription: IControlsSubscription
     ) : CallbackRunnable(token) {
         override fun doRun() {
-            if (!refreshing.get()) {
-                Log.d(TAG, "onRefresh outside of window from '${provider?.componentName}'")
-            }
-            provider?.let {
-                it.startSubscription(subscription)
-            }
-        }
-    }
-
-    private inner class OnCompleteRunnable(
-        token: IBinder
-    ) : CallbackRunnable(token) {
-        override fun doRun() {
-            provider?.let {
-                Log.i(TAG, "onComplete receive from '${it.componentName}'")
-            }
-        }
-    }
-
-    private inner class OnErrorRunnable(
-        token: IBinder,
-        val error: String
-    ) : CallbackRunnable(token) {
-        override fun doRun() {
-            provider?.let {
-                Log.e(TAG, "onError receive from '${it.componentName}': $error")
-            }
+            provider?.startSubscription(subscription)
         }
     }
 
@@ -292,8 +238,14 @@
     ) : IControlsSubscriber.Stub() {
         val loadedControls = ArrayList<Control>()
         var hasError = false
+        private var _loadCancelInternal: (() -> Unit)? = null
+        fun loadCancel() = Runnable {
+                Log.d(TAG, "Cancel load requested")
+                _loadCancelInternal?.invoke()
+            }
 
         override fun onSubscribe(token: IBinder, subs: IControlsSubscription) {
+            _loadCancelInternal = subs::cancel
             backgroundExecutor.execute(OnSubscribeRunnable(token, subs))
         }
 
@@ -302,11 +254,15 @@
         }
         override fun onError(token: IBinder, s: String) {
             hasError = true
+            _loadCancelInternal = {}
+            currentProvider?.cancelLoadTimeout()
             backgroundExecutor.execute(OnLoadErrorRunnable(token, s, callback))
         }
 
         override fun onComplete(token: IBinder) {
+            _loadCancelInternal = {}
             if (!hasError) {
+                currentProvider?.cancelLoadTimeout()
                 backgroundExecutor.execute(OnLoadRunnable(token, loadedControls, callback))
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
index 7eafe2e..9e0d26c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -59,6 +59,11 @@
     )
 
     /**
+     * Cancels a pending load call
+     */
+    fun cancelLoad()
+
+    /**
      * Request to subscribe for favorited controls per structure
      *
      * @param structureInfo structure to limit the subscription to
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index e5d36f9..9cb902f 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -72,6 +72,8 @@
 
     private var userChanging: Boolean = true
 
+    private var loadCanceller: Runnable? = null
+
     private var currentUser = UserHandle.of(ActivityManager.getCurrentUser())
     override val currentUserId
         get() = currentUser.identifier
@@ -213,8 +215,9 @@
         if (!confirmAvailability()) {
             if (userChanging) {
                 // Try again later, userChanging should not last forever. If so, we have bigger
-                // problems
-                executor.executeDelayed(
+                // problems. This will return a runnable that allows to cancel the delayed version,
+                // it will not be able to cancel the load if
+                loadCanceller = executor.executeDelayed(
                         { loadForComponent(componentName, dataCallback) },
                         USER_CHANGE_RETRY_DELAY,
                         TimeUnit.MILLISECONDS
@@ -224,10 +227,11 @@
             }
             return
         }
-        bindingController.bindAndLoad(
+        loadCanceller = bindingController.bindAndLoad(
                 componentName,
                 object : ControlsBindingController.LoadCallback {
                     override fun accept(controls: List<Control>) {
+                        loadCanceller = null
                         executor.execute {
                             val favoritesForComponentKeys = Favorites
                                 .getControlsForComponent(componentName).map { it.controlId }
@@ -251,12 +255,12 @@
                                 controlsWithFavorite,
                                 favoritesForComponentKeys
                             )
-
                             dataCallback.accept(loadData)
                         }
                     }
 
                     override fun error(message: String) {
+                        loadCanceller = null
                         executor.execute {
                             val loadData = Favorites.getControlsForComponent(componentName)
                                 .let { controls ->
@@ -269,7 +273,6 @@
                                         true
                                 )
                             }
-
                             dataCallback.accept(loadData)
                         }
                     }
@@ -277,6 +280,12 @@
         )
     }
 
+    override fun cancelLoad() {
+        loadCanceller?.let {
+            executor.execute(it)
+        }
+    }
+
     private fun createRemovedStatus(
         componentName: ComponentName,
         controlInfo: ControlInfo,
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
index 86e8e83..4918bd7 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
@@ -58,7 +58,6 @@
     private val context: Context,
     private val executor: DelayableExecutor,
     private val actionCallbackService: IControlsActionCallback.Stub,
-    private val subscriberService: IControlsSubscriber.Stub,
     val user: UserHandle,
     val componentName: ComponentName
 ) : IBinder.DeathRecipient {
@@ -157,10 +156,9 @@
             load(msg.subscriber)
         }
 
-        queue.filter { it is Message.Subscribe }.flatMap { (it as Message.Subscribe).list }.run {
-            if (this.isNotEmpty()) {
-                subscribe(this)
-            }
+        queue.filter { it is Message.Subscribe }.forEach {
+            val msg = it as Message.Subscribe
+            subscribe(msg.list, msg.subscriber)
         }
         queue.filter { it is Message.Action }.forEach {
             val msg = it as Message.Action
@@ -185,9 +183,9 @@
         }
     }
 
-    private fun unqueueMessage(message: Message) {
+    private fun unqueueMessageType(type: Int) {
         synchronized(queuedMessages) {
-            queuedMessages.removeIf { it.type == message.type }
+            queuedMessages.removeIf { it.type == type }
         }
     }
 
@@ -219,7 +217,7 @@
      * @param subscriber the subscriber that manages coordination for loading controls
      */
     fun maybeBindAndLoad(subscriber: IControlsSubscriber.Stub) {
-        unqueueMessage(Message.Unbind)
+        unqueueMessageType(MSG_UNBIND)
         onLoadCanceller = executor.executeDelayed({
             // Didn't receive a response in time, log and send back error
             Log.d(TAG, "Timeout waiting onLoad for $componentName")
@@ -230,6 +228,11 @@
         invokeOrQueue({ load(subscriber) }, Message.Load(subscriber))
     }
 
+    fun cancelLoadTimeout() {
+        onLoadCanceller?.run()
+        onLoadCanceller = null
+    }
+
     /**
      * Request a subscription to the [Publisher] returned by [ControlsProviderService.publisherFor]
      *
@@ -237,16 +240,20 @@
      *
      * @param controlIds a list of the ids of controls to send status back.
      */
-    fun maybeBindAndSubscribe(controlIds: List<String>) {
-        invokeOrQueue({ subscribe(controlIds) }, Message.Subscribe(controlIds))
+    fun maybeBindAndSubscribe(controlIds: List<String>, subscriber: IControlsSubscriber) {
+        invokeOrQueue(
+            { subscribe(controlIds, subscriber) },
+            Message.Subscribe(controlIds, subscriber)
+        )
     }
 
-    private fun subscribe(controlIds: List<String>) {
+    private fun subscribe(controlIds: List<String>, subscriber: IControlsSubscriber) {
         if (DEBUG) {
             Log.d(TAG, "subscribe $componentName - $controlIds")
         }
-        if (!(wrapper?.subscribe(controlIds, subscriberService) ?: false)) {
-            queueMessage(Message.Subscribe(controlIds))
+
+        if (!(wrapper?.subscribe(controlIds, subscriber) ?: false)) {
+            queueMessage(Message.Subscribe(controlIds, subscriber))
             binderDied()
         }
     }
@@ -276,10 +283,13 @@
     /**
      * Starts the subscription to the [ControlsProviderService] and requests status of controls.
      *
-     * @param subscription the subscriber to use to request controls
+     * @param subscription the subscription to use to request controls
      * @see maybeBindAndLoad
      */
     fun startSubscription(subscription: IControlsSubscription) {
+        if (DEBUG) {
+            Log.d(TAG, "startSubscription: $subscription")
+        }
         synchronized(subscriptions) {
             subscriptions.add(subscription)
         }
@@ -287,30 +297,26 @@
     }
 
     /**
-     * Unsubscribe from this service, cancelling all status requests.
+     * Cancels the subscription to the [ControlsProviderService].
+     *
+     * @param subscription the subscription to cancel
+     * @see maybeBindAndLoad
      */
-    fun unsubscribe() {
+    fun cancelSubscription(subscription: IControlsSubscription) {
         if (DEBUG) {
-            Log.d(TAG, "unsubscribe $componentName")
+            Log.d(TAG, "cancelSubscription: $subscription")
         }
-        unqueueMessage(Message.Subscribe(emptyList())) // Removes all subscribe messages
-
-        val subs = synchronized(subscriptions) {
-            ArrayList(subscriptions).also {
-                subscriptions.clear()
-            }
+        synchronized(subscriptions) {
+            subscriptions.remove(subscription)
         }
-
-        subs.forEach {
-            wrapper?.cancel(it)
-        }
+        wrapper?.cancel(subscription)
     }
 
     /**
      * Request bind to the service.
      */
     fun bindService() {
-        unqueueMessage(Message.Unbind)
+        unqueueMessageType(MSG_UNBIND)
         bindService(true)
     }
 
@@ -321,8 +327,16 @@
         onLoadCanceller?.run()
         onLoadCanceller = null
 
-        // just in case this wasn't called already
-        unsubscribe()
+        // be sure to cancel all subscriptions
+        val subs = synchronized(subscriptions) {
+            ArrayList(subscriptions).also {
+                subscriptions.clear()
+            }
+        }
+
+        subs.forEach {
+            wrapper?.cancel(it)
+        }
 
         bindService(false)
     }
@@ -346,7 +360,7 @@
         object Unbind : Message() {
             override val type = MSG_UNBIND
         }
-        class Subscribe(val list: List<String>) : Message() {
+        class Subscribe(val list: List<String>, val subscriber: IControlsSubscriber) : Message() {
             override val type = MSG_SUBSCRIBE
         }
         class Action(val id: String, val action: ControlAction) : Message() {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/StatefulControlSubscriber.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/StatefulControlSubscriber.kt
new file mode 100644
index 0000000..a371aa6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/StatefulControlSubscriber.kt
@@ -0,0 +1,96 @@
+/*
+ * 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.controls.controller
+
+import android.os.IBinder
+import android.service.controls.Control
+import android.service.controls.IControlsSubscriber
+import android.service.controls.IControlsSubscription
+import android.util.Log
+import com.android.systemui.util.concurrency.DelayableExecutor
+
+/**
+ * A single subscriber, supporting stateful controls for publishers created by
+ * {@link ControlsProviderService#createPublisherFor}. In general, this subscription will remain
+ * active until the SysUi chooses to cancel it.
+ */
+class StatefulControlSubscriber(
+    private val controller: ControlsController,
+    private val provider: ControlsProviderLifecycleManager,
+    private val bgExecutor: DelayableExecutor
+) : IControlsSubscriber.Stub() {
+    private var subscriptionOpen = false
+    private var subscription: IControlsSubscription? = null
+
+    companion object {
+        private const val TAG = "StatefulControlSubscriber"
+    }
+
+    private fun run(token: IBinder, f: () -> Unit) {
+        if (provider.token == token) {
+            bgExecutor.execute { f() }
+        }
+    }
+
+    override fun onSubscribe(token: IBinder, subs: IControlsSubscription) {
+        run(token) {
+            subscriptionOpen = true
+            subscription = subs
+            provider.startSubscription(subs)
+        }
+    }
+
+    override fun onNext(token: IBinder, control: Control) {
+        run(token) {
+            if (!subscriptionOpen) {
+                Log.w(TAG, "Refresh outside of window for token:$token")
+            } else {
+                controller.refreshStatus(provider.componentName, control)
+            }
+        }
+    }
+    override fun onError(token: IBinder, error: String) {
+        run(token) {
+            if (subscriptionOpen) {
+                subscriptionOpen = false
+                Log.e(TAG, "onError receive from '${provider.componentName}': $error")
+            }
+        }
+    }
+
+    override fun onComplete(token: IBinder) {
+        run(token) {
+            if (subscriptionOpen) {
+                subscriptionOpen = false
+                Log.i(TAG, "onComplete receive from '${provider.componentName}'")
+            }
+        }
+    }
+
+    fun cancel() {
+        if (!subscriptionOpen) return
+        bgExecutor.execute {
+            if (subscriptionOpen) {
+                subscriptionOpen = false
+                subscription?.let {
+                    provider.cancelSubscription(it)
+                }
+                subscription = null
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index 04715ab..f2303e6 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -19,22 +19,27 @@
 import android.app.Activity
 import android.content.ComponentName
 import android.content.Intent
+import android.content.res.Configuration
 import android.graphics.drawable.Drawable
 import android.os.Bundle
 import android.text.TextUtils
+import android.view.Gravity
 import android.view.View
+import android.view.ViewGroup
 import android.view.ViewStub
 import android.widget.Button
+import android.widget.FrameLayout
 import android.widget.ImageView
 import android.widget.TextView
 import androidx.viewpager2.widget.ViewPager2
+import com.android.systemui.Prefs
 import com.android.systemui.R
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.controls.ControlsServiceInfo
+import com.android.systemui.controls.TooltipManager
 import com.android.systemui.controls.controller.ControlsControllerImpl
 import com.android.systemui.controls.controller.StructureInfo
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.qs.PageIndicator
 import com.android.systemui.settings.CurrentUserTracker
 import java.text.Collator
 import java.util.concurrent.Executor
@@ -51,6 +56,8 @@
     companion object {
         private const val TAG = "ControlsFavoritingActivity"
         const val EXTRA_APP = "extra_app_label"
+        private const val TOOLTIP_PREFS_KEY = Prefs.Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT
+        private const val TOOLTIP_MAX_SHOWN = 2
     }
 
     private var component: ComponentName? = null
@@ -61,7 +68,9 @@
     private lateinit var titleView: TextView
     private lateinit var iconView: ImageView
     private lateinit var iconFrame: View
-    private lateinit var pageIndicator: PageIndicator
+    private lateinit var pageIndicator: ManagementPageIndicator
+    private var mTooltipManager: TooltipManager? = null
+    private lateinit var doneButton: View
     private var listOfStructures = emptyList<StructureContainer>()
 
     private lateinit var comparator: Comparator<StructureContainer>
@@ -129,6 +138,7 @@
                     StructureContainer(it.key, AllModel(it.value, favoriteKeys, emptyZoneString))
                 }.sortedWith(comparator)
                 executor.execute {
+                    doneButton.isEnabled = true
                     structurePager.adapter = StructureAdapter(listOfStructures)
                     if (error) {
                         statusText.text = resources.getText(R.string.controls_favorite_load_error)
@@ -174,7 +184,47 @@
         }
 
         statusText = requireViewById(R.id.status_message)
-        pageIndicator = requireViewById(R.id.structure_page_indicator)
+        if (shouldShowTooltip()) {
+            mTooltipManager = TooltipManager(statusText.context,
+                TOOLTIP_PREFS_KEY, TOOLTIP_MAX_SHOWN)
+            addContentView(
+                mTooltipManager?.layout,
+                FrameLayout.LayoutParams(
+                    ViewGroup.LayoutParams.WRAP_CONTENT,
+                    ViewGroup.LayoutParams.WRAP_CONTENT,
+                    Gravity.TOP or Gravity.LEFT
+                )
+            )
+        }
+        pageIndicator = requireViewById<ManagementPageIndicator>(
+            R.id.structure_page_indicator).apply {
+            addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
+                override fun onLayoutChange(
+                    v: View,
+                    left: Int,
+                    top: Int,
+                    right: Int,
+                    bottom: Int,
+                    oldLeft: Int,
+                    oldTop: Int,
+                    oldRight: Int,
+                    oldBottom: Int
+                ) {
+                    if (v.visibility == View.VISIBLE && mTooltipManager != null) {
+                        val p = IntArray(2)
+                        v.getLocationOnScreen(p)
+                        val x = p[0] + (right - left) / 2
+                        val y = p[1] + bottom - top
+                        mTooltipManager?.show(R.string.controls_structure_tooltip, x, y)
+                    }
+                }
+            })
+            visibilityListener = {
+                if (it != View.VISIBLE) {
+                    mTooltipManager?.hide(true)
+                }
+            }
+        }
 
         titleView = requireViewById<TextView>(R.id.title).apply {
             text = appName ?: resources.getText(R.string.controls_favorite_default_title)
@@ -184,6 +234,12 @@
         iconView = requireViewById(com.android.internal.R.id.icon)
         iconFrame = requireViewById(R.id.icon_frame)
         structurePager = requireViewById<ViewPager2>(R.id.structure_pager)
+        structurePager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
+            override fun onPageSelected(position: Int) {
+                super.onPageSelected(position)
+                mTooltipManager?.hide(true)
+            }
+        })
         bindButtons()
     }
 
@@ -195,23 +251,41 @@
             }
         }
 
-        requireViewById<Button>(R.id.done).setOnClickListener {
-            if (component == null) return@setOnClickListener
-            listOfStructures.forEach {
-                val favoritesForStorage = it.model.favorites.map { it.build() }
-                controller.replaceFavoritesForStructure(StructureInfo(component!!, it.structureName,
-                        favoritesForStorage))
+        doneButton = requireViewById<Button>(R.id.done).apply {
+            isEnabled = false
+            setOnClickListener {
+                if (component == null) return@setOnClickListener
+                listOfStructures.forEach {
+                    val favoritesForStorage = it.model.favorites.map { it.build() }
+                    controller.replaceFavoritesForStructure(
+                        StructureInfo(component!!, it.structureName, favoritesForStorage)
+                    )
+                }
+                finishAffinity()
             }
-
-            finishAffinity()
         }
     }
 
+    override fun onPause() {
+        super.onPause()
+        mTooltipManager?.hide(false)
+    }
+
+    override fun onConfigurationChanged(newConfig: Configuration) {
+        super.onConfigurationChanged(newConfig)
+        mTooltipManager?.hide(false)
+    }
+
     override fun onDestroy() {
         currentUserTracker.stopTracking()
         listingController.removeCallback(listingCallback)
+        controller.cancelLoad()
         super.onDestroy()
     }
+
+    private fun shouldShowTooltip(): Boolean {
+        return Prefs.getInt(applicationContext, TOOLTIP_PREFS_KEY, 0) < TOOLTIP_MAX_SHOWN
+    }
 }
 
 data class StructureContainer(val structureName: CharSequence, val model: ControlsModel)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ManagementPageIndicator.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ManagementPageIndicator.kt
index 4289274..72b1098 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ManagementPageIndicator.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ManagementPageIndicator.kt
@@ -40,4 +40,13 @@
             super.setLocation(location)
         }
     }
+
+    var visibilityListener: (Int) -> Unit = {}
+
+    override fun onVisibilityChanged(changedView: View, visibility: Int) {
+        super.onVisibilityChanged(changedView, visibility)
+        if (changedView == this) {
+            visibilityListener(visibility)
+        }
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index fc5663f..b439206 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.controls.ui
 
 import android.content.Context
-import android.graphics.BlendMode
 import android.graphics.drawable.ClipDrawable
+import android.graphics.drawable.GradientDrawable
 import android.graphics.drawable.LayerDrawable
 import android.service.controls.Control
 import android.service.controls.actions.ControlAction
@@ -39,6 +39,8 @@
 import kotlin.reflect.KClass
 
 private const val UPDATE_DELAY_IN_MILLIS = 3000L
+private const val ALPHA_ENABLED = (255.0 * 0.2).toInt()
+private const val ALPHA_DISABLED = 255
 
 class ControlViewHolder(
     val layout: ViewGroup,
@@ -69,7 +71,7 @@
 
         cancelUpdate?.run()
 
-        val (status, template) = cws.control?.let {
+        val (controlStatus, template) = cws.control?.let {
             title.setText(it.getTitle())
             subtitle.setText(it.getSubtitle())
             Pair(it.getStatus(), it.getControlTemplate())
@@ -80,20 +82,28 @@
         }
 
         cws.control?.let {
+            layout.setClickable(true)
             layout.setOnLongClickListener(View.OnLongClickListener() {
                 ControlActionCoordinator.longPress(this@ControlViewHolder)
                 true
             })
         }
 
-        val clazz = findBehavior(status, template)
+        val clazz = findBehavior(controlStatus, template)
         if (behavior == null || behavior!!::class != clazz) {
             // Behavior changes can signal a change in template from the app or
             // first time setup
             behavior = clazz.java.newInstance()
             behavior?.initialize(this)
+
+            // let behaviors define their own, if necessary, and clear any existing ones
+            layout.setAccessibilityDelegate(null)
         }
+
         behavior?.bind(cws)
+
+        layout.setContentDescription(
+            "${title.text} ${subtitle.text} ${status.text} ${statusExtra.text}")
     }
 
     fun actionResponse(@ControlAction.ResponseResult response: Int) {
@@ -136,16 +146,21 @@
         val ri = RenderInfo.lookup(context, cws.componentName, deviceType, enabled, offset)
 
         val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme())
-        val bg = context.getResources().getColorStateList(ri.background, context.getTheme())
+        val (bg, alpha) = if (enabled) {
+            Pair(ri.enabledBackground, ALPHA_ENABLED)
+        } else {
+            Pair(R.color.control_default_background, ALPHA_DISABLED)
+        }
+
         status.setTextColor(fg)
         statusExtra.setTextColor(fg)
 
         icon.setImageDrawable(ri.icon)
         icon.setImageTintList(fg)
 
-        clipLayer.getDrawable().apply {
-            setTintBlendMode(BlendMode.HUE)
-            setTintList(bg)
+        (clipLayer.getDrawable() as GradientDrawable).apply {
+            setColor(context.getResources().getColor(bg, context.getTheme()))
+            setAlpha(alpha)
         }
     }
 
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 bde966c..138cd47 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -406,7 +406,7 @@
             setText(item.getTitle())
         }
         view.requireViewById<ImageView>(R.id.app_icon).apply {
-            setContentDescription(item.getTitle())
+            setContentDescription(item.appName)
             setImageDrawable(item.icon)
         }
         return view
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
index 56267be..27e4649 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.controls.ui
 
+import android.annotation.ColorRes
 import android.annotation.MainThread
 import android.content.ComponentName
 import android.content.Context
@@ -37,8 +38,11 @@
     }
 }
 
-data class RenderInfo(val icon: Drawable, val foreground: Int, val background: Int) {
-
+data class RenderInfo(
+    val icon: Drawable,
+    val foreground: Int,
+    @ColorRes val enabledBackground: Int
+) {
     companion object {
         const val APP_ICON_ID = -1
         private val iconMap = SparseArray<Drawable>()
@@ -72,6 +76,7 @@
                 icon = iconMap.get(resourceId)
                 if (icon == null) {
                     icon = context.resources.getDrawable(resourceId, null)
+                    icon.mutate()
                     iconMap.put(resourceId, icon)
                 }
             }
@@ -94,12 +99,13 @@
 
 private val deviceColorMap = mapOf<Int, Pair<Int, Int>>(
     (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_HEAT) to
-        Pair(R.color.thermo_heat_foreground, R.color.thermo_heat_background),
+        Pair(R.color.thermo_heat_foreground, R.color.control_enabled_thermo_heat_background),
     (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_COOL) to
-        Pair(R.color.thermo_cool_foreground, R.color.thermo_cool_background),
-    DeviceTypes.TYPE_LIGHT to Pair(R.color.light_foreground, R.color.light_background)
+        Pair(R.color.thermo_cool_foreground, R.color.control_enabled_thermo_cool_background),
+    DeviceTypes.TYPE_LIGHT
+        to Pair(R.color.light_foreground, R.color.control_enabled_light_background)
 ).withDefault {
-        Pair(R.color.control_foreground, R.color.control_background)
+        Pair(R.color.control_foreground, R.color.control_enabled_default_background)
 }
 
 private val deviceIconMap = mapOf<Int, IconState>(
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
index 6595b55..c495c58 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.controls.ui
 
+import android.os.Bundle
 import android.content.Context
 import android.graphics.drawable.Drawable
 import android.graphics.drawable.LayerDrawable
@@ -24,6 +25,9 @@
 import android.view.GestureDetector.SimpleOnGestureListener
 import android.view.MotionEvent
 import android.view.View
+import android.view.ViewGroup
+import android.view.accessibility.AccessibilityEvent
+import android.view.accessibility.AccessibilityNodeInfo
 import android.widget.TextView
 import android.service.controls.Control
 import android.service.controls.actions.FloatAction
@@ -94,6 +98,71 @@
         updateRange(currentRatio, checked)
 
         cvh.applyRenderInfo(checked)
+
+        /*
+         * This is custom widget behavior, so add a new accessibility delegate to
+         * handle clicks and range events. Present as a seek bar control.
+         */
+        cvh.layout.setAccessibilityDelegate(object : View.AccessibilityDelegate() {
+            override fun onInitializeAccessibilityNodeInfo(
+                host: View,
+                info: AccessibilityNodeInfo
+            ) {
+                super.onInitializeAccessibilityNodeInfo(host, info)
+
+                val min = levelToRangeValue(MIN_LEVEL)
+                val current = levelToRangeValue(clipLayer.getLevel())
+                val max = levelToRangeValue(MAX_LEVEL)
+
+                val step = rangeTemplate.getStepValue().toDouble()
+                val type = if (step == Math.floor(step)) {
+                    AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_INT
+                } else {
+                    AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_FLOAT
+                }
+
+                val rangeInfo = AccessibilityNodeInfo.RangeInfo.obtain(type, min, max, current)
+                info.setRangeInfo(rangeInfo)
+                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_PROGRESS)
+            }
+
+            override fun performAccessibilityAction(
+                host: View,
+                action: Int,
+                arguments: Bundle?
+            ): Boolean {
+                val handled = when (action) {
+                    AccessibilityNodeInfo.ACTION_CLICK -> {
+                        ControlActionCoordinator.toggle(cvh, template.getTemplateId(),
+                            template.isChecked())
+                        true
+                    }
+                    AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_PROGRESS.getId() -> {
+                        if (arguments == null || !arguments.containsKey(
+                                AccessibilityNodeInfo.ACTION_ARGUMENT_PROGRESS_VALUE)) {
+                            false
+                        } else {
+                            val value = arguments.getFloat(
+                                AccessibilityNodeInfo.ACTION_ARGUMENT_PROGRESS_VALUE)
+                            val ratioDiff = (value - rangeTemplate.getCurrentValue()) /
+                                (rangeTemplate.getMaxValue() - rangeTemplate.getMinValue())
+                            updateRange(ratioDiff, template.isChecked())
+                            endUpdateRange()
+                            true
+                        }
+                    }
+                    else -> false
+                }
+
+                return handled || super.performAccessibilityAction(host, action, arguments)
+            }
+
+            override fun onRequestSendAccessibilityEvent(
+                host: ViewGroup,
+                child: View,
+                event: AccessibilityEvent
+            ): Boolean = false
+        })
     }
 
     fun beginUpdateRange() {
@@ -108,7 +177,7 @@
         clipLayer.setLevel(newLevel)
 
         if (checked) {
-            val newValue = levelToRangeValue()
+            val newValue = levelToRangeValue(clipLayer.getLevel())
             val formattedNewValue = format(rangeTemplate.getFormatString().toString(),
                     DEFAULT_FORMAT, newValue)
 
@@ -133,8 +202,8 @@
         }
     }
 
-    private fun levelToRangeValue(): Float {
-        val ratio = clipLayer.getLevel().toFloat() / MAX_LEVEL
+    private fun levelToRangeValue(i: Int): Float {
+        val ratio = i.toFloat() / MAX_LEVEL
         return rangeTemplate.getMinValue() +
             (ratio * (rangeTemplate.getMaxValue() - rangeTemplate.getMinValue()))
     }
@@ -143,7 +212,8 @@
         statusExtra.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
                 .getDimensionPixelSize(R.dimen.control_status_normal).toFloat())
         status.setVisibility(View.VISIBLE)
-        cvh.action(FloatAction(rangeTemplate.getTemplateId(), findNearestStep(levelToRangeValue())))
+        cvh.action(FloatAction(rangeTemplate.getTemplateId(),
+            findNearestStep(levelToRangeValue(clipLayer.getLevel()))))
     }
 
     fun findNearestStep(value: Float): Float {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 6c502d2..3a4b273 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -90,7 +90,7 @@
 
     /** */
     @Provides
-    public AmbientDisplayConfiguration provideAmbientDispalyConfiguration(Context context) {
+    public AmbientDisplayConfiguration provideAmbientDisplayConfiguration(Context context) {
         return new AmbientDisplayConfiguration(context);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 513580f..2e9ce12 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -19,7 +19,6 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.view.Choreographer;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.BootCompleteCache;
@@ -31,22 +30,16 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.stackdivider.Divider;
-import com.android.systemui.statusbar.BlurUtils;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.NotificationShadeWindowBlurController;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
 import com.android.systemui.statusbar.notification.people.PeopleHubModule;
 import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
 import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
-import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.KeyguardLiftController;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.ConcurrencyModule;
 import com.android.systemui.util.sensors.AsyncSensorManager;
 import com.android.systemui.util.time.SystemClock;
@@ -98,21 +91,6 @@
                 keyguardUpdateMonitor, dumpManager);
     }
 
-    @Singleton
-    @Provides
-    @Nullable
-    static NotificationShadeWindowBlurController providesBlurController(BlurUtils blurUtils,
-            SysuiStatusBarStateController statusBarStateController,
-            DumpManager dumpManager, BiometricUnlockController biometricUnlockController,
-            KeyguardStateController keyguardStateController,
-            NotificationShadeWindowController notificationShadeWindowController,
-            Choreographer choreographer) {
-        return blurUtils.supportsBlursOnWindows() ? new NotificationShadeWindowBlurController(
-                statusBarStateController, blurUtils, biometricUnlockController,
-                keyguardStateController, notificationShadeWindowController, choreographer,
-                dumpManager) : null;
-    }
-
     /** */
     @Binds
     public abstract NotificationRowBinder bindNotificationRowBinder(
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index c45063a..f6fccc0 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -161,15 +161,8 @@
             }
 
             int scrimOpacity = -1;
-            if (mPaused || mScreenOff) {
-                // If AOD is paused, force the screen black until the
-                // sensor reports a new brightness. This ensures that when the screen comes on
-                // again, it will only show after the brightness sensor has stabilized,
-                // avoiding a potential flicker.
-                scrimOpacity = 255;
-            } else if (!mScreenOff && mLightSensor == null) {
-                // No light sensor but previous state turned the screen black. Make the scrim
-                // transparent and below views visible.
+            if (mLightSensor == null) {
+                // No light sensor, scrims are always transparent.
                 scrimOpacity = 0;
             } else if (brightnessReady) {
                 // Only unblank scrim once brightness is ready.
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 786ad2c..59b28b4 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -99,7 +99,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
 import com.android.systemui.plugins.GlobalActionsPanelPlugin;
-import com.android.systemui.statusbar.BlurUtils;
+import com.android.systemui.statusbar.NotificationShadeDepthController;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -162,7 +162,7 @@
     private final IActivityManager mIActivityManager;
     private final TelecomManager mTelecomManager;
     private final MetricsLogger mMetricsLogger;
-    private final BlurUtils mBlurUtils;
+    private final NotificationShadeDepthController mDepthController;
 
     private ArrayList<Action> mItems;
     private ActionsDialog mDialog;
@@ -207,7 +207,7 @@
             KeyguardStateController keyguardStateController, UserManager userManager,
             TrustManager trustManager, IActivityManager iActivityManager,
             @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger,
-            BlurUtils blurUtils, SysuiColorExtractor colorExtractor,
+            NotificationShadeDepthController depthController, SysuiColorExtractor colorExtractor,
             IStatusBarService statusBarService,
             NotificationShadeWindowController notificationShadeWindowController,
             ControlsUiController controlsUiController, IWindowManager iWindowManager,
@@ -229,7 +229,7 @@
         mIActivityManager = iActivityManager;
         mTelecomManager = telecomManager;
         mMetricsLogger = metricsLogger;
-        mBlurUtils = blurUtils;
+        mDepthController = depthController;
         mSysuiColorExtractor = colorExtractor;
         mStatusBarService = statusBarService;
         mNotificationShadeWindowController = notificationShadeWindowController;
@@ -437,7 +437,7 @@
                         : null;
 
         ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, panelViewController,
-                mBlurUtils, mSysuiColorExtractor, mStatusBarService,
+                mDepthController, mSysuiColorExtractor, mStatusBarService,
                 mNotificationShadeWindowController,
                 shouldShowControls() ? mControlsUiController : null);
         dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
@@ -1570,20 +1570,21 @@
         private ResetOrientationData mResetOrientationData;
         private boolean mHadTopUi;
         private final NotificationShadeWindowController mNotificationShadeWindowController;
-        private final BlurUtils mBlurUtils;
+        private final NotificationShadeDepthController mDepthController;
 
         private ControlsUiController mControlsUiController;
         private ViewGroup mControlsView;
 
         ActionsDialog(Context context, MyAdapter adapter,
-                GlobalActionsPanelPlugin.PanelViewController plugin, BlurUtils blurUtils,
+                GlobalActionsPanelPlugin.PanelViewController plugin,
+                NotificationShadeDepthController depthController,
                 SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
                 NotificationShadeWindowController notificationShadeWindowController,
                 ControlsUiController controlsUiController) {
             super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
             mContext = context;
             mAdapter = adapter;
-            mBlurUtils = blurUtils;
+            mDepthController = depthController;
             mColorExtractor = sysuiColorExtractor;
             mStatusBarService = statusBarService;
             mNotificationShadeWindowController = notificationShadeWindowController;
@@ -1792,8 +1793,8 @@
                         float animatedValue = animation.getAnimatedFraction();
                         int alpha = (int) (animatedValue * mScrimAlpha * 255);
                         mBackgroundDrawable.setAlpha(alpha);
-                        mBlurUtils.applyBlur(mGlobalActionsLayout.getViewRootImpl(),
-                                mBlurUtils.radiusForRatio(animatedValue));
+                        mDepthController.updateGlobalDialogVisibility(animatedValue,
+                                mGlobalActionsLayout);
                     })
                     .start();
             if (mControlsUiController != null) {
@@ -1822,8 +1823,8 @@
                         float animatedValue = 1f - animation.getAnimatedFraction();
                         int alpha = (int) (animatedValue * mScrimAlpha * 255);
                         mBackgroundDrawable.setAlpha(alpha);
-                        mBlurUtils.applyBlur(mGlobalActionsLayout.getViewRootImpl(),
-                                mBlurUtils.radiusForRatio(animatedValue));
+                        mDepthController.updateGlobalDialogVisibility(animatedValue,
+                                mGlobalActionsLayout);
                     })
                     .start();
             dismissPanel();
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index 280a248..bbb57bd 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -151,7 +151,7 @@
         if (mBlurUtils.supportsBlursOnWindows()) {
             background.setAlpha((int) (ScrimController.BUSY_SCRIM_ALPHA * 255));
             mBlurUtils.applyBlur(d.getWindow().getDecorView().getViewRootImpl(),
-                        mBlurUtils.radiusForRatio(1));
+                        mBlurUtils.blurRadiusOfRatio(1));
         } else {
             background.setAlpha((int) (SHUTDOWN_SCRIM_ALPHA * 255));
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
index ae380b7..c281ece 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
@@ -93,7 +93,7 @@
             return mIatm.startActivityAsUser(
                     mContext.getIApplicationThread() /*caller*/,
                     mContext.getBasePackageName() /*callingPackage*/,
-                    mContext.getFeatureId() /*callingFeatureId*/,
+                    mContext.getAttributionTag() /*callingAttributionTag*/,
                     intent /*intent*/,
                     intent.resolveTypeIfNeeded(mContext.getContentResolver()) /*resolvedType*/,
                     null /*resultTo*/,
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
index 67802bc..41bace7 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
@@ -37,7 +37,6 @@
     private static final float FRACTION_START = 0f;
     private static final float FRACTION_END = 1f;
 
-    public static final int DURATION_DEFAULT_MS = 425;
     public static final int ANIM_TYPE_BOUNDS = 0;
     public static final int ANIM_TYPE_ALPHA = 1;
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 00b977e..90b6570 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -18,7 +18,6 @@
 
 import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_ALPHA;
 import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
-import static com.android.systemui.pip.PipAnimationController.DURATION_DEFAULT_MS;
 import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_NONE;
 import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_SAME;
 import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_FULLSCREEN;
@@ -81,6 +80,7 @@
     private final Rect mLastReportedBounds = new Rect();
     private final int mCornerRadius;
     private final Map<IBinder, Rect> mBoundsToRestore = new HashMap<>();
+    private final int mEnterExitAnimationDuration;
 
     // These callbacks are called on the update thread
     private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
@@ -180,6 +180,8 @@
         mPipBoundsHandler = boundsHandler;
         mPipAnimationController = new PipAnimationController(context);
         mCornerRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius);
+        mEnterExitAnimationDuration = context.getResources()
+                .getInteger(R.integer.config_pipResizeAnimationDuration);
     }
 
     public Handler getUpdateHandler() {
@@ -235,14 +237,14 @@
         mBoundsToRestore.put(mToken.asBinder(), currentBounds);
         if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
             scheduleAnimateResizePip(currentBounds, destinationBounds,
-                    TRANSITION_DIRECTION_TO_PIP, DURATION_DEFAULT_MS, null);
+                    TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration, null);
         } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
             mUpdateHandler.post(() -> mPipAnimationController
                     .getAnimator(mLeash, destinationBounds, 0f, 1f)
                     .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
                     .setCornerRadius(mCornerRadius)
                     .setPipAnimationCallback(mPipAnimationCallback)
-                    .setDuration(DURATION_DEFAULT_MS)
+                    .setDuration(mEnterExitAnimationDuration)
                     .start());
             mOneShotAnimationType = ANIM_TYPE_BOUNDS;
         } else {
@@ -260,7 +262,7 @@
         }
         final Rect boundsToRestore = mBoundsToRestore.remove(mToken.asBinder());
         scheduleAnimateResizePip(mLastReportedBounds, boundsToRestore,
-                TRANSITION_DIRECTION_TO_FULLSCREEN, DURATION_DEFAULT_MS, null);
+                TRANSITION_DIRECTION_TO_FULLSCREEN, mEnterExitAnimationDuration, null);
         mInPip = false;
     }
 
@@ -278,7 +280,7 @@
         final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
                 getAspectRatioOrDefault(newParams), null /* bounds */);
         Objects.requireNonNull(destinationBounds, "Missing destination bounds");
-        scheduleAnimateResizePip(destinationBounds, DURATION_DEFAULT_MS, null);
+        scheduleAnimateResizePip(destinationBounds, mEnterExitAnimationDuration, null);
     }
 
     /**
@@ -415,11 +417,16 @@
         }
         mLastReportedBounds.set(destinationBounds);
         try {
+            // If we are animating to fullscreen, then we need to reset the override bounds on the
+            // task to ensure that the task "matches" the parent's bounds
+            Rect taskBounds = direction == TRANSITION_DIRECTION_TO_FULLSCREEN
+                    ? null
+                    : destinationBounds;
             final WindowContainerTransaction wct = new WindowContainerTransaction();
             if (direction == TRANSITION_DIRECTION_TO_PIP) {
-                wct.scheduleFinishEnterPip(mToken, destinationBounds);
+                wct.scheduleFinishEnterPip(mToken, taskBounds);
             } else {
-                wct.setBounds(mToken, destinationBounds);
+                wct.setBounds(mToken, taskBounds);
             }
             wct.setBoundsChangeTransaction(mToken, tx);
             mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java
index b09d6e1..7dfd99c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java
@@ -44,7 +44,7 @@
             try {
                 // Dismiss the PiP once the user disables the app ops setting for that package
                 final Pair<ComponentName, Integer> topPipActivityInfo =
-                        PipUtils.getTopPinnedActivity(mContext, mActivityManager);
+                        PipUtils.getTopPipActivity(mContext, mActivityManager);
                 if (topPipActivityInfo.first != null) {
                     final ApplicationInfo appInfo = mContext.getPackageManager()
                             .getApplicationInfoAsUser(packageName, 0, topPipActivityInfo.second);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index eed81bf..2aac188 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -115,7 +115,7 @@
 
         @Override
         public void onActivityUnpinned() {
-            final Pair<ComponentName, Integer> topPipActivityInfo = PipUtils.getTopPinnedActivity(
+            final Pair<ComponentName, Integer> topPipActivityInfo = PipUtils.getTopPipActivity(
                     mContext, mActivityManager);
             final ComponentName topActivity = topPipActivityInfo.first;
             mMenuController.onActivityUnpinned();
@@ -128,7 +128,12 @@
         }
 
         @Override
-        public void onPinnedActivityRestartAttempt(boolean clearedTask) {
+        public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
+                boolean homeTaskVisible, boolean clearedTask) {
+            if (task.configuration.windowConfiguration.getWindowingMode()
+                    != WINDOWING_MODE_PINNED) {
+                return;
+            }
             mTouchHandler.getMotionHelper().expandPip(clearedTask /* skipAnimation */);
         }
     };
@@ -232,6 +237,12 @@
                 mTouchHandler.getMotionHelper());
         displayController.addDisplayChangingController(mRotationController);
 
+        // Ensure that we have the display info in case we get calls to update the bounds before the
+        // listener calls back
+        final DisplayInfo displayInfo = new DisplayInfo();
+        context.getDisplay().getDisplayInfo(displayInfo);
+        mPipBoundsHandler.onDisplayInfoChanged(displayInfo);
+
         try {
             ActivityTaskManager.getTaskOrganizerController().registerTaskOrganizer(
                     mPipTaskOrganizer, WINDOWING_MODE_PINNED);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
index e57b416..849a62a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
@@ -231,7 +231,7 @@
      */
     private void resolveActiveMediaController(List<MediaController> controllers) {
         if (controllers != null) {
-            final ComponentName topActivity = PipUtils.getTopPinnedActivity(mContext,
+            final ComponentName topActivity = PipUtils.getTopPipActivity(mContext,
                     mActivityManager).first;
             if (topActivity != null) {
                 for (int i = 0; i < controllers.size(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index fc04f79..2b9b171 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -569,7 +569,7 @@
 
     private void showSettings() {
         final Pair<ComponentName, Integer> topPipActivityInfo =
-                PipUtils.getTopPinnedActivity(this, ActivityManager.getService());
+                PipUtils.getTopPipActivity(this, ActivityManager.getService());
         if (topPipActivityInfo.first != null) {
             final UserHandle user = UserHandle.of(topPipActivityInfo.second);
             final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS,
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 90db91a..b5fb1a9 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -31,6 +31,7 @@
 import android.os.Handler;
 import android.os.RemoteException;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Size;
 import android.view.IPinnedStackController;
 import android.view.InputEvent;
@@ -148,8 +149,11 @@
 
         @Override
         public void onPipDismiss() {
-            MetricsLoggerWrapper.logPictureInPictureDismissByTap(mContext,
-                    PipUtils.getTopPinnedActivity(mContext, mActivityManager));
+            Pair<ComponentName, Integer> topPipActivity = PipUtils.getTopPipActivity(mContext,
+                    mActivityManager);
+            if (topPipActivity.first != null) {
+                MetricsLoggerWrapper.logPictureInPictureDismissByTap(mContext, topPipActivity);
+            }
             mMotionHelper.dismissPip();
         }
 
@@ -653,7 +657,7 @@
                 // Check if the user dragged or flung the PiP offscreen to dismiss it
                 if (mMotionHelper.shouldDismissPip() || isFlingToBot) {
                     MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext,
-                            PipUtils.getTopPinnedActivity(mContext, mActivityManager));
+                            PipUtils.getTopPipActivity(mContext, mActivityManager));
                     mMotionHelper.animateDismiss(
                             vel.x, vel.y,
                             PipTouchHandler.this::updateDismissFraction /* updateAction */);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
index 1ed1904..4cfec01 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
@@ -36,7 +36,7 @@
      * @return the ComponentName and user id of the top non-SystemUI activity in the pinned stack.
      *         The component name may be null if no such activity exists.
      */
-    public static Pair<ComponentName, Integer> getTopPinnedActivity(Context context,
+    public static Pair<ComponentName, Integer> getTopPipActivity(Context context,
             IActivityManager activityManager) {
         try {
             final String sysUiPackageName = context.getPackageName();
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index a5e9dbc..6206dd5 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -20,8 +20,6 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 
-import static com.android.systemui.pip.PipAnimationController.DURATION_DEFAULT_MS;
-
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManager.StackInfo;
 import android.app.ActivityTaskManager;
@@ -136,6 +134,7 @@
     private String[] mLastPackagesResourceGranted;
     private PipNotification mPipNotification;
     private ParceledListSlice mCustomActions;
+    private int mResizeAnimationDuration;
 
     // Used to calculate the movement bounds
     private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
@@ -238,6 +237,8 @@
         mInitialized = true;
         mContext = context;
         mPipBoundsHandler = pipBoundsHandler;
+        mResizeAnimationDuration = context.getResources()
+                .getInteger(R.integer.config_pipResizeAnimationDuration);
         mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler);
         mPipTaskOrganizer.registerPipTransitionCallback(this);
         mActivityTaskManager = ActivityTaskManager.getService();
@@ -436,7 +437,8 @@
                 mCurrentPipBounds = mPipBounds;
                 break;
         }
-        mPipTaskOrganizer.scheduleAnimateResizePip(mCurrentPipBounds, DURATION_DEFAULT_MS, null);
+        mPipTaskOrganizer.scheduleAnimateResizePip(mCurrentPipBounds, mResizeAnimationDuration,
+                null);
     }
 
     /**
@@ -680,7 +682,12 @@
         }
 
         @Override
-        public void onPinnedActivityRestartAttempt(boolean clearedTask) {
+        public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
+                boolean clearedTask) {
+            if (task.configuration.windowConfiguration.getWindowingMode()
+                    != WINDOWING_MODE_PINNED) {
+                return;
+            }
             if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()");
 
             // If PIPed activity is launched again by Launcher or intent, make it fullscreen.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 17ac5e5..fab7191 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -274,8 +274,8 @@
                 try {
                     tile = createTile(tileSpec);
                     if (tile != null) {
+                        tile.setTileSpec(tileSpec);
                         if (tile.isAvailable()) {
-                            tile.setTileSpec(tileSpec);
                             newTiles.put(tileSpec, tile);
                             mQSLogger.logTileAdded(tileSpec);
                         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
index f3e2f10..5bf44c6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
@@ -20,6 +20,8 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 
+import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.trust.TrustManager;
@@ -37,6 +39,7 @@
 import com.android.systemui.R;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.phone.StatusBar;
 
@@ -63,6 +66,21 @@
     private TrustManager mTrustManager;
     private OverviewProxyService mOverviewProxyService;
 
+    private TaskStackChangeListener mListener = new TaskStackChangeListener() {
+        @Override
+        public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
+                boolean homeTaskVisible, boolean clearedTask) {
+            if (task.configuration.windowConfiguration.getWindowingMode()
+                    != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+                return;
+            }
+
+            if (homeTaskVisible) {
+                showRecentApps(false /* triggeredFromAltTab */);
+            }
+        }
+    };
+
     @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
     @Inject
     public OverviewProxyRecentsImpl(Optional<Lazy<StatusBar>> statusBarLazy,
@@ -77,6 +95,7 @@
         mHandler = new Handler();
         mTrustManager = (TrustManager) context.getSystemService(Context.TRUST_SERVICE);
         mOverviewProxyService = Dependency.get(OverviewProxyService.class);
+        ActivityManagerWrapper.getInstance().registerTaskStackListener(mListener);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index fb68153..ea5ec05 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -148,6 +148,8 @@
          * regardless of what has focus.
          */
         private boolean mTargetShown = false;
+        private float mTargetPrimaryDim = 0.f;
+        private float mTargetSecondaryDim = 0.f;
 
         // The following are the current (most recent) states set during animation
         /** {@code true} if the secondary split has IME focus. */
@@ -186,8 +188,12 @@
         @Override
         public void onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
                 boolean imeShouldShow, SurfaceControl.Transaction t) {
+            if (!inSplitMode()) {
+                return;
+            }
+            final boolean splitIsVisible = !mView.isHidden();
             mSecondaryHasFocus = getSecondaryHasFocus(displayId);
-            mTargetAdjusted = imeShouldShow && mSecondaryHasFocus
+            mTargetAdjusted = splitIsVisible && imeShouldShow && mSecondaryHasFocus
                     && !mSplitLayout.mDisplayLayout.isLandscape();
             mHiddenTop = hiddenTop;
             mShownTop = shownTop;
@@ -195,6 +201,10 @@
             if (mLastAdjustTop < 0) {
                 mLastAdjustTop = imeShouldShow ? hiddenTop : shownTop;
             }
+            mTargetPrimaryDim = (mSecondaryHasFocus && mTargetShown && splitIsVisible)
+                    ? ADJUSTED_NONFOCUS_DIM : 0.f;
+            mTargetSecondaryDim = (!mSecondaryHasFocus && mTargetShown && splitIsVisible)
+                    ? ADJUSTED_NONFOCUS_DIM : 0.f;
             if (mAnimation != null || (mImeWasShown && imeShouldShow
                     && mTargetAdjusted != mAdjusted)) {
                 // We need to animate adjustment independently of the IME position, so
@@ -202,7 +212,11 @@
                 // different split's editor has gained focus while the IME is still visible.
                 startAsyncAnimation();
             }
-            updateImeAdjustState();
+            if (splitIsVisible) {
+                // If split is hidden, we don't want to trigger any relayouts that would cause the
+                // divider to show again.
+                updateImeAdjustState();
+            }
         }
 
         private void updateImeAdjustState() {
@@ -245,7 +259,7 @@
         @Override
         public void onImePositionChanged(int displayId, int imeTop,
                 SurfaceControl.Transaction t) {
-            if (mAnimation != null) {
+            if (mAnimation != null || !inSplitMode()) {
                 // Not synchronized with IME anymore, so return.
                 return;
             }
@@ -257,7 +271,7 @@
         @Override
         public void onImeEndPositioning(int displayId, boolean cancelled,
                 SurfaceControl.Transaction t) {
-            if (mAnimation != null) {
+            if (mAnimation != null || !inSplitMode()) {
                 // Not synchronized with IME anymore, so return.
                 return;
             }
@@ -273,14 +287,10 @@
                         mSplitLayout.mAdjustedSecondary);
             }
             final float invProg = 1.f - progress;
-            final float targetPrimaryDim =
-                    (mSecondaryHasFocus && mTargetShown) ? ADJUSTED_NONFOCUS_DIM : 0.f;
-            final float targetSecondaryDim =
-                    (!mSecondaryHasFocus && mTargetShown) ? ADJUSTED_NONFOCUS_DIM : 0.f;
             mView.setResizeDimLayer(t, true /* primary */,
-                    mLastPrimaryDim * invProg + progress * targetPrimaryDim);
+                    mLastPrimaryDim * invProg + progress * mTargetPrimaryDim);
             mView.setResizeDimLayer(t, false /* primary */,
-                    mLastSecondaryDim * invProg + progress * targetSecondaryDim);
+                    mLastSecondaryDim * invProg + progress * mTargetSecondaryDim);
         }
 
         private void onEnd(boolean cancelled, SurfaceControl.Transaction t) {
@@ -289,10 +299,8 @@
                 mAdjusted = mTargetAdjusted;
                 mImeWasShown = mTargetShown;
                 mLastAdjustTop = mAdjusted ? mShownTop : mHiddenTop;
-                mLastPrimaryDim =
-                        (mSecondaryHasFocus && mTargetShown) ? ADJUSTED_NONFOCUS_DIM : 0.f;
-                mLastSecondaryDim =
-                        (!mSecondaryHasFocus && mTargetShown) ? ADJUSTED_NONFOCUS_DIM : 0.f;
+                mLastPrimaryDim = mTargetPrimaryDim;
+                mLastSecondaryDim = mTargetSecondaryDim;
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 477cbb7..4114bb9 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -165,6 +165,10 @@
     // The view is removed or in the process of been removed from the system.
     private boolean mRemoved;
 
+    // Whether the surface for this view has been hidden regardless of actual visibility. This is
+    // used interact with keyguard.
+    private boolean mSurfaceHidden = false;
+
     private final Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
@@ -414,6 +418,10 @@
 
     /** Unlike setVisible, this directly hides the surface without changing view visibility. */
     void setHidden(boolean hidden) {
+        if (mSurfaceHidden == hidden) {
+            return;
+        }
+        mSurfaceHidden = hidden;
         post(() -> {
             final SurfaceControl sc = getWindowSurfaceControl();
             if (sc == null) {
@@ -430,6 +438,10 @@
         });
     }
 
+    boolean isHidden() {
+        return mSurfaceHidden;
+    }
+
     public boolean startDragging(boolean animate, boolean touching) {
         cancelFlingAnimation();
         if (touching) {
@@ -1071,7 +1083,7 @@
 
     void setResizeDimLayer(Transaction t, boolean primary, float alpha) {
         SurfaceControl dim = primary ? mTiles.mPrimaryDim : mTiles.mSecondaryDim;
-        if (alpha <= 0.f) {
+        if (alpha <= 0.001f) {
             t.hide(dim);
         } else {
             t.setAlpha(dim, alpha);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index 34c0860..c523b7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -50,7 +50,7 @@
     /**
      * Translates a ratio from 0 to 1 to a blur radius in pixels.
      */
-    fun radiusForRatio(ratio: Float): Int {
+    fun blurRadiusOfRatio(ratio: Float): Int {
         if (ratio == 0f) {
             return 0
         }
@@ -58,6 +58,17 @@
     }
 
     /**
+     * Translates a blur radius in pixels to a ratio between 0 to 1.
+     */
+    fun ratioOfBlurRadius(blur: Int): Float {
+        if (blur == 0) {
+            return 0f
+        }
+        return MathUtils.map(minBlurRadius.toFloat(), maxBlurRadius.toFloat(),
+                0f /* maxStart */, 1f /* maxStop */, blur.toFloat())
+    }
+
+    /**
      * Applies background blurs to a {@link ViewRootImpl}.
      *
      * @param viewRootImpl The window root.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
index 0bfcdbd..4759d56 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
@@ -23,6 +23,7 @@
 import android.text.TextUtils;
 import android.view.NotificationHeaderView;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.TextView;
 
@@ -182,24 +183,24 @@
 
     private void sanitizeChild(View child) {
         if (child != null) {
-            NotificationHeaderView header = (NotificationHeaderView) child.findViewById(
+            ViewGroup header = child.findViewById(
                     com.android.internal.R.id.notification_header);
             sanitizeHeader(header);
         }
     }
 
-    private void sanitizeHeader(NotificationHeaderView rowHeader) {
+    private void sanitizeHeader(ViewGroup rowHeader) {
         if (rowHeader == null) {
             return;
         }
         final int childCount = rowHeader.getChildCount();
         View time = rowHeader.findViewById(com.android.internal.R.id.time);
         boolean hasVisibleText = false;
-        for (int i = 1; i < childCount - 1 ; i++) {
+        for (int i = 0; i < childCount; i++) {
             View child = rowHeader.getChildAt(i);
             if (child instanceof TextView
                     && child.getVisibility() != View.GONE
-                    && !mDividers.contains(Integer.valueOf(child.getId()))
+                    && !mDividers.contains(child.getId())
                     && child != time) {
                 hasVisibleText = true;
                 break;
@@ -212,14 +213,14 @@
         time.setVisibility(timeVisibility);
         View left = null;
         View right;
-        for (int i = 1; i < childCount - 1 ; i++) {
+        for (int i = 0; i < childCount; i++) {
             View child = rowHeader.getChildAt(i);
-            if (mDividers.contains(Integer.valueOf(child.getId()))) {
+            if (mDividers.contains(child.getId())) {
                 boolean visible = false;
                 // Lets find the item to the right
-                for (i++; i < childCount - 1; i++) {
+                for (i++; i < childCount; i++) {
                     right = rowHeader.getChildAt(i);
-                    if (mDividers.contains(Integer.valueOf(right.getId()))) {
+                    if (mDividers.contains(right.getId())) {
                         // A divider was found, this needs to be hidden
                         i--;
                         break;
@@ -276,14 +277,18 @@
             if (!mApply) {
                 return;
             }
-            NotificationHeaderView header = row.getContractedNotificationHeader();
-            if (header == null) {
-                // No header found. We still consider this to be the same to avoid weird flickering
+            View contractedChild = row.getPrivateLayout().getContractedChild();
+            if (contractedChild == null) {
+                return;
+            }
+            View ownView = contractedChild.findViewById(mId);
+            if (ownView == null) {
+                // No view found. We still consider this to be the same to avoid weird flickering
                 // when for example showing an undo notification
                 return;
             }
             Object childData = mExtractor == null ? null : mExtractor.extractData(row);
-            mApply = mComparator.compare(mParentView, header.findViewById(mId),
+            mApply = mComparator.compare(mParentView, ownView,
                     mParentData, childData);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowBlurController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
similarity index 79%
rename from packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowBlurController.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 6e905a3..eb8526d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowBlurController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -19,6 +19,7 @@
 import android.animation.Animator
 import android.animation.AnimatorListenerAdapter
 import android.animation.ValueAnimator
+import android.app.WallpaperManager
 import android.view.Choreographer
 import android.view.View
 import androidx.dynamicanimation.animation.FloatPropertyCompat
@@ -30,7 +31,6 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.statusbar.phone.BiometricUnlockController
 import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController
 import com.android.systemui.statusbar.phone.PanelExpansionListener
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import java.io.FileDescriptor
@@ -43,13 +43,13 @@
  * Controller responsible for statusbar window blur.
  */
 @Singleton
-class NotificationShadeWindowBlurController @Inject constructor(
+class NotificationShadeDepthController @Inject constructor(
     private val statusBarStateController: SysuiStatusBarStateController,
     private val blurUtils: BlurUtils,
     private val biometricUnlockController: BiometricUnlockController,
     private val keyguardStateController: KeyguardStateController,
-    private val notificationShadeWindowController: NotificationShadeWindowController,
     private val choreographer: Choreographer,
+    private val wallpaperManager: WallpaperManager,
     dumpManager: DumpManager
 ) : PanelExpansionListener, Dumpable {
     companion object {
@@ -58,20 +58,22 @@
     }
 
     lateinit var root: View
+    private var blurRoot: View? = null
     private var keyguardAnimator: Animator? = null
     private var notificationAnimator: Animator? = null
     private var updateScheduled: Boolean = false
     private var shadeExpansion = 1.0f
     private val shadeSpring = SpringAnimation(this, object :
-            FloatPropertyCompat<NotificationShadeWindowBlurController>("shadeBlurRadius") {
-        override fun setValue(rect: NotificationShadeWindowBlurController?, value: Float) {
+            FloatPropertyCompat<NotificationShadeDepthController>("shadeBlurRadius") {
+        override fun setValue(rect: NotificationShadeDepthController?, value: Float) {
             shadeBlurRadius = value.toInt()
         }
 
-        override fun getValue(rect: NotificationShadeWindowBlurController?): Float {
+        override fun getValue(rect: NotificationShadeDepthController?): Float {
             return shadeBlurRadius.toFloat()
         }
     })
+    private val zoomInterpolator = Interpolators.ACCELERATE_DECELERATE
     private var shadeBlurRadius = 0
         set(value) {
             if (field == value) return
@@ -84,12 +86,7 @@
             field = value
             scheduleUpdate()
         }
-    private var incomingNotificationBlurRadius = 0
-        set(value) {
-            if (field == value) return
-            field = value
-            scheduleUpdate()
-        }
+    private var globalDialogVisibility = 0f
 
     /**
      * Callback that updates the window blur value and is called only once per frame.
@@ -97,13 +94,12 @@
     private val updateBlurCallback = Choreographer.FrameCallback {
         updateScheduled = false
 
-        var notificationBlur = 0
-        if (statusBarStateController.state == StatusBarState.KEYGUARD) {
-            notificationBlur = (incomingNotificationBlurRadius * shadeExpansion).toInt()
-        }
-
-        val blur = max(max(shadeBlurRadius, wakeAndUnlockBlurRadius), notificationBlur)
-        blurUtils.applyBlur(root.viewRootImpl, blur)
+        val blur = max(shadeBlurRadius,
+                max(wakeAndUnlockBlurRadius, blurUtils.blurRadiusOfRatio(globalDialogVisibility)))
+        blurUtils.applyBlur(blurRoot?.viewRootImpl ?: root.viewRootImpl, blur)
+        val rawZoom = max(blurUtils.ratioOfBlurRadius(blur), globalDialogVisibility)
+        wallpaperManager.setWallpaperZoomOut(root.windowToken,
+                zoomInterpolator.getInterpolation(rawZoom))
     }
 
     /**
@@ -123,7 +119,7 @@
                 interpolator = Interpolators.DECELERATE_QUINT
                 addUpdateListener { animation: ValueAnimator ->
                     wakeAndUnlockBlurRadius =
-                            blurUtils.radiusForRatio(animation.animatedValue as Float)
+                            blurUtils.blurRadiusOfRatio(animation.animatedValue as Float)
                 }
                 addListener(object : AnimatorListenerAdapter() {
                     override fun onAnimationEnd(animation: Animator?) {
@@ -163,7 +159,7 @@
 
         var newBlur = 0
         if (statusBarStateController.state == StatusBarState.SHADE) {
-            newBlur = blurUtils.radiusForRatio(expansion)
+            newBlur = blurUtils.blurRadiusOfRatio(expansion)
         }
 
         if (shadeBlurRadius == newBlur) {
@@ -172,20 +168,29 @@
         shadeSpring.animateToFinalPosition(newBlur.toFloat())
     }
 
-    private fun scheduleUpdate() {
+    private fun scheduleUpdate(viewToBlur: View? = null) {
         if (updateScheduled) {
             return
         }
         updateScheduled = true
+        blurRoot = viewToBlur
         choreographer.postFrameCallback(updateBlurCallback)
     }
 
+    fun updateGlobalDialogVisibility(visibility: Float, dialogView: View) {
+        if (visibility == globalDialogVisibility) {
+            return
+        }
+        globalDialogVisibility = visibility
+        scheduleUpdate(dialogView)
+    }
+
     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
-        IndentingPrintWriter(pw, "  ").use {
+        IndentingPrintWriter(pw, "  ").let {
             it.println("StatusBarWindowBlurController:")
             it.increaseIndent()
             it.println("shadeBlurRadius: $shadeBlurRadius")
             it.println("wakeAndUnlockBlur: $wakeAndUnlockBlurRadius")
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
index 7b5a70e..2a45bc2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
@@ -41,6 +41,7 @@
     private static final int TAG_CONTAINS_TRANSFORMED_VIEW = R.id.contains_transformed_view;
 
     private ArrayMap<Integer, View> mTransformedViews = new ArrayMap<>();
+    private ArraySet<Integer> mKeysTransformingToSimilar = new ArraySet<>();
     private ArrayMap<Integer, CustomTransformation> mCustomTransformations = new ArrayMap<>();
     private ValueAnimator mViewTransformationAnimation;
 
@@ -48,8 +49,22 @@
         mTransformedViews.put(key, transformedView);
     }
 
+    /**
+     * Add a view that transforms to a similar sibling, meaning that we should consider any mapping
+     * found treated as the same viewType. This is useful for imageViews, where it's hard to compare
+     * if the source images are the same when they are bitmap based.
+     *
+     * @param key The key how this is added
+     * @param transformedView the view that is added
+     */
+    public void addViewTransformingToSimilar(int key, View transformedView) {
+        addTransformedView(key, transformedView);
+        mKeysTransformingToSimilar.add(key);
+    }
+
     public void reset() {
         mTransformedViews.clear();
+        mKeysTransformingToSimilar.clear();
     }
 
     public void setCustomTransformation(CustomTransformation transformation, int viewType) {
@@ -60,7 +75,11 @@
     public TransformState getCurrentState(int fadingView) {
         View view = mTransformedViews.get(fadingView);
         if (view != null && view.getVisibility() != View.GONE) {
-            return TransformState.createFrom(view, this);
+            TransformState transformState = TransformState.createFrom(view, this);
+            if (mKeysTransformingToSimilar.contains(fadingView)) {
+                transformState.setIsSameAsAnyView(true);
+            }
+            return transformState;
         }
         return null;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
index b732966..9383f53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
@@ -21,9 +21,9 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.internal.widget.IMessagingLayout;
 import com.android.internal.widget.MessagingGroup;
 import com.android.internal.widget.MessagingImageMessage;
-import com.android.internal.widget.MessagingLayout;
 import com.android.internal.widget.MessagingLinearLayout;
 import com.android.internal.widget.MessagingMessage;
 import com.android.internal.widget.MessagingPropertyAnimator;
@@ -41,7 +41,7 @@
     private static Pools.SimplePool<MessagingLayoutTransformState> sInstancePool
             = new Pools.SimplePool<>(40);
     private MessagingLinearLayout mMessageContainer;
-    private MessagingLayout mMessagingLayout;
+    private IMessagingLayout mMessagingLayout;
     private HashMap<MessagingGroup, MessagingGroup> mGroupMap = new HashMap<>();
     private float mRelativeTranslationOffset;
 
@@ -266,8 +266,9 @@
             transformView(transformationAmount, to, child, otherChild, false, /* sameAsAny */
                     useLinearTransformation);
             boolean otherIsIsolated = otherGroup.getIsolatedMessage() == otherChild;
-            if (transformationAmount == 0.0f && otherIsIsolated) {
-                ownGroup.setTransformingImages(true);
+            if (transformationAmount == 0.0f
+                    && (otherIsIsolated || otherGroup.isSingleLine())) {
+                ownGroup.setClippingDisabled(true);
             }
             if (otherChild == null) {
                 child.setTranslationY(previousTranslation);
@@ -291,11 +292,20 @@
         if (useLinearTransformation) {
             ownState.setDefaultInterpolator(Interpolators.LINEAR);
         }
-        ownState.setIsSameAsAnyView(sameAsAny);
+        ownState.setIsSameAsAnyView(sameAsAny && !isGone(otherView));
         if (to) {
             if (otherView != null) {
                 TransformState otherState = TransformState.createFrom(otherView, mTransformInfo);
-                ownState.transformViewTo(otherState, transformationAmount);
+                if (!isGone(otherView)) {
+                    ownState.transformViewTo(otherState, transformationAmount);
+                } else {
+                    if (!isGone(ownView)) {
+                        ownState.disappear(transformationAmount, null);
+                    }
+                    // We still want to transform vertically if the view is gone,
+                    // since avatars serve as anchors for the rest of the layout transition
+                    ownState.transformViewVerticalTo(otherState, transformationAmount);
+                }
                 otherState.recycle();
             } else {
                 ownState.disappear(transformationAmount, null);
@@ -303,7 +313,16 @@
         } else {
             if (otherView != null) {
                 TransformState otherState = TransformState.createFrom(otherView, mTransformInfo);
-                ownState.transformViewFrom(otherState, transformationAmount);
+                if (!isGone(otherView)) {
+                    ownState.transformViewFrom(otherState, transformationAmount);
+                } else {
+                    if (!isGone(ownView)) {
+                        ownState.appear(transformationAmount, null);
+                    }
+                    // We still want to transform vertically if the view is gone,
+                    // since avatars serve as anchors for the rest of the layout transition
+                    ownState.transformViewVerticalFrom(otherState, transformationAmount);
+                }
                 otherState.recycle();
             } else {
                 ownState.appear(transformationAmount, null);
@@ -337,6 +356,9 @@
     }
 
     private boolean isGone(View view) {
+        if (view == null) {
+            return true;
+        }
         if (view.getVisibility() == View.GONE) {
             return true;
         }
@@ -408,7 +430,7 @@
                 ownGroup.getMessageContainer().setTranslationY(0);
                 ownGroup.getSenderView().setTranslationY(0);
             }
-            ownGroup.setTransformingImages(false);
+            ownGroup.setClippingDisabled(false);
             ownGroup.updateClipRect();
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 8a23e37..2747696 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.statusbar.notification
 
 import android.animation.ObjectAnimator
-import android.content.Context
 import android.util.FloatProperty
 import com.android.systemui.Interpolators
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -26,10 +25,10 @@
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator
 import com.android.systemui.statusbar.phone.DozeParameters
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.phone.NotificationIconAreaController
 import com.android.systemui.statusbar.phone.PanelExpansionListener
+import com.android.systemui.statusbar.policy.HeadsUpManager
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
 
 import javax.inject.Inject
@@ -37,15 +36,14 @@
 
 @Singleton
 class NotificationWakeUpCoordinator @Inject constructor(
-        private val mHeadsUpManagerPhone: HeadsUpManagerPhone,
-        private val statusBarStateController: StatusBarStateController,
-        private val bypassController: KeyguardBypassController,
-        private val dozeParameters: DozeParameters)
-    : OnHeadsUpChangedListener, StatusBarStateController.StateListener,
-        PanelExpansionListener {
+    private val mHeadsUpManager: HeadsUpManager,
+    private val statusBarStateController: StatusBarStateController,
+    private val bypassController: KeyguardBypassController,
+    private val dozeParameters: DozeParameters
+) : OnHeadsUpChangedListener, StatusBarStateController.StateListener, PanelExpansionListener {
 
-    private val mNotificationVisibility
-            = object : FloatProperty<NotificationWakeUpCoordinator>("notificationVisibility") {
+    private val mNotificationVisibility = object : FloatProperty<NotificationWakeUpCoordinator>(
+        "notificationVisibility") {
 
         override fun setValue(coordinator: NotificationWakeUpCoordinator, value: Float) {
             coordinator.setVisibilityAmount(value)
@@ -78,10 +76,10 @@
             field = value
             willWakeUp = false
             if (value) {
-                if (mNotificationsVisible && !mNotificationsVisibleForExpansion
-                        && !bypassController.bypassEnabled) {
+                if (mNotificationsVisible && !mNotificationsVisibleForExpansion &&
+                    !bypassController.bypassEnabled) {
                     // We're waking up while pulsing, let's make sure the animation looks nice
-                    mStackScroller.wakeUpFromPulse();
+                    mStackScroller.wakeUpFromPulse()
                 }
                 if (bypassController.bypassEnabled && !mNotificationsVisible) {
                     // Let's make sure our huns become visible once we are waking up in case
@@ -100,7 +98,7 @@
         }
 
     private var collapsedEnoughToHide: Boolean = false
-    lateinit var iconAreaController : NotificationIconAreaController
+    lateinit var iconAreaController: NotificationIconAreaController
 
     var pulsing: Boolean = false
         set(value) {
@@ -132,8 +130,8 @@
             var canShow = pulsing
             if (bypassController.bypassEnabled) {
                 // We also allow pulsing on the lock screen!
-                canShow = canShow || (wakingUp || willWakeUp || fullyAwake)
-                        && statusBarStateController.state == StatusBarState.KEYGUARD
+                canShow = canShow || (wakingUp || willWakeUp || fullyAwake) &&
+                    statusBarStateController.state == StatusBarState.KEYGUARD
                 // We want to hide the notifications when collapsed too much
                 if (collapsedEnoughToHide) {
                     canShow = false
@@ -143,7 +141,7 @@
         }
 
     init {
-        mHeadsUpManagerPhone.addListener(this)
+        mHeadsUpManager.addListener(this)
         statusBarStateController.addCallback(this)
         addListener(object : WakeUpListener {
             override fun onFullyHiddenChanged(isFullyHidden: Boolean) {
@@ -155,7 +153,7 @@
                             increaseSpeed = false)
                 }
             }
-        });
+        })
     }
 
     fun setStackScroller(stackScroller: NotificationStackScrollLayout) {
@@ -178,46 +176,55 @@
      * @param animate should this change be animated
      * @param increaseSpeed should the speed be increased of the animation
      */
-    fun setNotificationsVisibleForExpansion(visible: Boolean, animate: Boolean,
-                                                    increaseSpeed: Boolean) {
+    fun setNotificationsVisibleForExpansion(
+        visible: Boolean,
+        animate: Boolean,
+        increaseSpeed: Boolean
+    ) {
         mNotificationsVisibleForExpansion = visible
         updateNotificationVisibility(animate, increaseSpeed)
         if (!visible && mNotificationsVisible) {
             // If we stopped expanding and we're still visible because we had a pulse that hasn't
             // times out, let's release them all to make sure were not stuck in a state where
             // notifications are visible
-            mHeadsUpManagerPhone.releaseAllImmediately()
+            mHeadsUpManager.releaseAllImmediately()
         }
     }
 
     fun addListener(listener: WakeUpListener) {
-        wakeUpListeners.add(listener);
+        wakeUpListeners.add(listener)
     }
 
     fun removeListener(listener: WakeUpListener) {
-        wakeUpListeners.remove(listener);
+        wakeUpListeners.remove(listener)
     }
 
-    private fun updateNotificationVisibility(animate: Boolean, increaseSpeed: Boolean) {
+    private fun updateNotificationVisibility(
+        animate: Boolean,
+        increaseSpeed: Boolean
+    ) {
         // TODO: handle Lockscreen wakeup for bypass when we're not pulsing anymore
-        var visible = mNotificationsVisibleForExpansion || mHeadsUpManagerPhone.hasNotifications()
+        var visible = mNotificationsVisibleForExpansion || mHeadsUpManager.hasNotifications()
         visible = visible && canShowPulsingHuns
 
         if (!visible && mNotificationsVisible && (wakingUp || willWakeUp) && mDozeAmount != 0.0f) {
             // let's not make notifications invisible while waking up, otherwise the animation
             // is strange
-            return;
+            return
         }
         setNotificationsVisible(visible, animate, increaseSpeed)
     }
 
-    private fun setNotificationsVisible(visible: Boolean, animate: Boolean,
-                                        increaseSpeed: Boolean) {
+    private fun setNotificationsVisible(
+        visible: Boolean,
+        animate: Boolean,
+        increaseSpeed: Boolean
+    ) {
         if (mNotificationsVisible == visible) {
             return
         }
         mNotificationsVisible = visible
-        mVisibilityAnimator?.cancel();
+        mVisibilityAnimator?.cancel()
         if (animate) {
             notifyAnimationStart(visible)
             startVisibilityAnimation(increaseSpeed)
@@ -230,8 +237,8 @@
         if (updateDozeAmountIfBypass()) {
             return
         }
-        if (linear != 1.0f && linear != 0.0f
-                && (mLinearDozeAmount == 0.0f || mLinearDozeAmount == 1.0f)) {
+        if (linear != 1.0f && linear != 0.0f &&
+            (mLinearDozeAmount == 0.0f || mLinearDozeAmount == 1.0f)) {
             // Let's notify the scroller that an animation started
             notifyAnimationStart(mLinearDozeAmount == 1.0f)
         }
@@ -245,17 +252,17 @@
         mStackScroller.setDozeAmount(mDozeAmount)
         updateHideAmount()
         if (changed && linear == 0.0f) {
-            setNotificationsVisible(visible = false, animate = false, increaseSpeed = false);
+            setNotificationsVisible(visible = false, animate = false, increaseSpeed = false)
             setNotificationsVisibleForExpansion(visible = false, animate = false,
                     increaseSpeed = false)
         }
     }
 
     override fun onStateChanged(newState: Int) {
-        updateDozeAmountIfBypass();
+        updateDozeAmountIfBypass()
         if (bypassController.bypassEnabled &&
-                newState == StatusBarState.KEYGUARD && state == StatusBarState.SHADE_LOCKED
-                && (!statusBarStateController.isDozing || shouldAnimateVisibility())) {
+                newState == StatusBarState.KEYGUARD && state == StatusBarState.SHADE_LOCKED &&
+            (!statusBarStateController.isDozing || shouldAnimateVisibility())) {
             // We're leaving shade locked. Let's animate the notifications away
             setNotificationsVisible(visible = true, increaseSpeed = false, animate = false)
             setNotificationsVisible(visible = false, increaseSpeed = false, animate = true)
@@ -266,23 +273,23 @@
     override fun onPanelExpansionChanged(expansion: Float, tracking: Boolean) {
         val collapsedEnough = expansion <= 0.9f
         if (collapsedEnough != this.collapsedEnoughToHide) {
-            val couldShowPulsingHuns = canShowPulsingHuns;
+            val couldShowPulsingHuns = canShowPulsingHuns
             this.collapsedEnoughToHide = collapsedEnough
             if (couldShowPulsingHuns && !canShowPulsingHuns) {
                 updateNotificationVisibility(animate = true, increaseSpeed = true)
-                mHeadsUpManagerPhone.releaseAllImmediately()
+                mHeadsUpManager.releaseAllImmediately()
             }
         }
     }
 
     private fun updateDozeAmountIfBypass(): Boolean {
         if (bypassController.bypassEnabled) {
-            var amount = 1.0f;
-            if (statusBarStateController.state == StatusBarState.SHADE
-                    || statusBarStateController.state == StatusBarState.SHADE_LOCKED) {
-                amount = 0.0f;
+            var amount = 1.0f
+            if (statusBarStateController.state == StatusBarState.SHADE ||
+                statusBarStateController.state == StatusBarState.SHADE_LOCKED) {
+                amount = 0.0f
             }
-            setDozeAmount(amount,  amount)
+            setDozeAmount(amount, amount)
             return true
         }
         return false
@@ -300,7 +307,7 @@
         visibilityAnimator.setInterpolator(Interpolators.LINEAR)
         var duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP.toLong()
         if (increaseSpeed) {
-            duration = (duration.toFloat() / 1.5F).toLong();
+            duration = (duration.toFloat() / 1.5F).toLong()
         }
         visibilityAnimator.setDuration(duration)
         visibilityAnimator.start()
@@ -311,7 +318,7 @@
         mLinearVisibilityAmount = visibilityAmount
         mVisibilityAmount = mVisibilityInterpolator.getInterpolation(
                 visibilityAmount)
-        handleAnimationFinished();
+        handleAnimationFinished()
         updateHideAmount()
     }
 
@@ -322,7 +329,7 @@
         }
     }
 
-    fun getWakeUpHeight() : Float {
+    fun getWakeUpHeight(): Float {
         return mStackScroller.wakeUpHeight
     }
 
@@ -330,7 +337,7 @@
         val linearAmount = Math.min(1.0f - mLinearVisibilityAmount, mLinearDozeAmount)
         val amount = Math.min(1.0f - mVisibilityAmount, mDozeAmount)
         mStackScroller.setHideAmount(linearAmount, amount)
-        notificationsFullyHidden = linearAmount == 1.0f;
+        notificationsFullyHidden = linearAmount == 1.0f
     }
 
     private fun notifyAnimationStart(awake: Boolean) {
@@ -361,7 +368,7 @@
                     // if we animate, we see the shelf briefly visible. Instead we fully animate
                     // the notification and its background out
                     animate = false
-                } else if (!wakingUp && !willWakeUp){
+                } else if (!wakingUp && !willWakeUp) {
                     // TODO: look that this is done properly and not by anyone else
                     entry.setHeadsUpAnimatingAway(true)
                     mEntrySetToClearWhenFinished.add(entry)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index e8a62e4..4beeede 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -37,8 +37,8 @@
 import com.android.systemui.statusbar.NotificationUiAdjustment;
 import com.android.systemui.statusbar.notification.InflationException;
 import com.android.systemui.statusbar.notification.NotificationClicker;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
 import com.android.systemui.statusbar.notification.row.NotifBindPipeline;
@@ -66,7 +66,7 @@
 
     private static final String TAG = "NotificationViewManager";
 
-    private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+    private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
 
     private final Context mContext;
     private final NotifBindPipeline mNotifBindPipeline;
@@ -97,7 +97,7 @@
             StatusBarStateController statusBarStateController,
             NotificationGroupManager notificationGroupManager,
             NotificationGutsManager notificationGutsManager,
-            NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+            NotificationInterruptStateProvider notificationInterruptionStateProvider,
             Provider<RowInflaterTask> rowInflaterTaskProvider,
             ExpandableNotificationRowComponent.Builder expandableNotificationRowComponentBuilder) {
         mContext = context;
@@ -106,7 +106,7 @@
         mMessagingUtil = notificationMessagingUtil;
         mNotificationRemoteInputManager = notificationRemoteInputManager;
         mNotificationLockscreenUserManager = notificationLockscreenUserManager;
-        mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
+        mNotificationInterruptStateProvider = notificationInterruptionStateProvider;
         mRowInflaterTaskProvider = rowInflaterTaskProvider;
         mExpandableNotificationRowComponentBuilder = expandableNotificationRowComponentBuilder;
     }
@@ -243,7 +243,7 @@
         params.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
         params.setUseLowPriority(entry.isAmbient());
 
-        if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
+        if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
             params.requireContentViews(FLAG_CONTENT_VIEW_HEADS_UP);
         }
         //TODO: Replace this API with RowContentBindParams directly
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index e425ee9..1d8e979 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -31,10 +31,8 @@
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
@@ -44,6 +42,9 @@
 import com.android.systemui.statusbar.notification.init.NotificationsController;
 import com.android.systemui.statusbar.notification.init.NotificationsControllerImpl;
 import com.android.systemui.statusbar.notification.init.NotificationsControllerStub;
+import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger;
 import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl;
@@ -58,6 +59,7 @@
 
 import javax.inject.Singleton;
 
+import dagger.Binds;
 import dagger.Lazy;
 import dagger.Module;
 import dagger.Provides;
@@ -127,7 +129,7 @@
             NotificationRemoteInputManager remoteInputManager,
             VisualStabilityManager visualStabilityManager,
             StatusBarStateController statusBarStateController,
-            NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+            NotificationInterruptStateProvider notificationInterruptStateProvider,
             NotificationListener notificationListener,
             HeadsUpManager headsUpManager) {
         return new NotificationAlertingManager(
@@ -135,7 +137,7 @@
                 remoteInputManager,
                 visualStabilityManager,
                 statusBarStateController,
-                notificationInterruptionStateProvider,
+                notificationInterruptStateProvider,
                 notificationListener,
                 headsUpManager);
     }
@@ -210,4 +212,9 @@
             NotificationEntryManager entryManager) {
         return featureFlags.isNewNotifPipelineRenderingEnabled() ? pipeline.get() : entryManager;
     }
+
+    /** */
+    @Binds
+    NotificationInterruptStateProvider bindNotificationInterruptStateProvider(
+            NotificationInterruptStateProviderImpl notificationInterruptStateProviderImpl);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
index 269a7a5..88888d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
@@ -14,7 +14,7 @@
  * limitations under the License
  */
 
-package com.android.systemui.statusbar.notification
+package com.android.systemui.statusbar.notification.interruption
 
 import android.content.Context
 import android.media.MediaMetadata
@@ -24,6 +24,7 @@
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.NotificationMediaManager
 import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.notification.NotificationEntryManager
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
 import com.android.systemui.statusbar.phone.KeyguardBypassController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationAlertingManager.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationAlertingManager.java
index df21f0b..b572502 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationAlertingManager.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.interruption;
 
 import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
@@ -27,6 +27,9 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -39,7 +42,7 @@
     private final NotificationRemoteInputManager mRemoteInputManager;
     private final VisualStabilityManager mVisualStabilityManager;
     private final StatusBarStateController mStatusBarStateController;
-    private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+    private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
     private final NotificationListener mNotificationListener;
 
     private HeadsUpManager mHeadsUpManager;
@@ -52,13 +55,13 @@
             NotificationRemoteInputManager remoteInputManager,
             VisualStabilityManager visualStabilityManager,
             StatusBarStateController statusBarStateController,
-            NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+            NotificationInterruptStateProvider notificationInterruptionStateProvider,
             NotificationListener notificationListener,
             HeadsUpManager headsUpManager) {
         mRemoteInputManager = remoteInputManager;
         mVisualStabilityManager = visualStabilityManager;
         mStatusBarStateController = statusBarStateController;
-        mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
+        mNotificationInterruptStateProvider = notificationInterruptionStateProvider;
         mNotificationListener = notificationListener;
         mHeadsUpManager = headsUpManager;
 
@@ -94,7 +97,7 @@
         if (entry.getRow().getPrivateLayout().getHeadsUpChild() != null) {
             // Possible for shouldHeadsUp to change between the inflation starting and ending.
             // If it does and we no longer need to heads up, we should free the view.
-            if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
+            if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
                 mHeadsUpManager.showNotification(entry);
                 if (!mStatusBarStateController.isDozing()) {
                     // Mark as seen immediately
@@ -109,7 +112,7 @@
     private void updateAlertState(NotificationEntry entry) {
         boolean alertAgain = alertAgain(entry, entry.getSbn().getNotification());
         // includes check for whether this notification should be filtered:
-        boolean shouldAlert = mNotificationInterruptionStateProvider.shouldHeadsUp(entry);
+        boolean shouldAlert = mNotificationInterruptStateProvider.shouldHeadsUp(entry);
         final boolean wasAlerting = mHeadsUpManager.isAlerting(entry.getKey());
         if (wasAlerting) {
             if (shouldAlert) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
new file mode 100644
index 0000000..3292a8f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
@@ -0,0 +1,59 @@
+/*
+ * 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.interruption;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+/**
+ * Provides bubble-up and heads-up state for notification entries.
+ *
+ * When a notification is heads-up when dozing, this is also called "pulsing."
+ */
+public interface NotificationInterruptStateProvider {
+    /**
+     * If the device is awake (not dozing):
+     *  Whether the notification should peek in from the top and alert the user.
+     *
+     * If the device is dozing:
+     *  Whether the notification should show the ambient view of the notification ("pulse").
+     *
+     * @param entry the entry to check
+     * @return true if the entry should heads up, false otherwise
+     */
+    boolean shouldHeadsUp(NotificationEntry entry);
+
+    /**
+     * Whether the notification should appear as a bubble with a fly-out on top of the screen.
+     *
+     * @param entry the entry to check
+     * @return true if the entry should bubble up, false otherwise
+     */
+    boolean shouldBubbleUp(NotificationEntry entry);
+
+    /**
+     * Whether to launch the entry's full screen intent when the entry is added.
+     *
+     * @param entry the entry that was added
+     * @return {@code true} if we should launch the full screen intent
+     */
+    boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry);
+
+    /**
+     * Add a component that can suppress visual interruptions.
+     */
+    void addSuppressor(NotificationInterruptSuppressor suppressor);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
similarity index 63%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index bbf2dde..46d5044 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -14,33 +14,35 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.interruption;
 
 import static com.android.systemui.statusbar.StatusBarState.SHADE;
 
 import android.app.NotificationManager;
-import android.content.Context;
+import android.content.ContentResolver;
 import android.database.ContentObserver;
 import android.hardware.display.AmbientDisplayConfiguration;
+import android.os.Handler;
 import android.os.PowerManager;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.service.dreams.DreamService;
 import android.service.dreams.IDreamManager;
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dependency;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.NotificationFilter;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
@@ -48,120 +50,84 @@
  * Provides heads-up and pulsing state for notification entries.
  */
 @Singleton
-public class NotificationInterruptionStateProvider {
-
+public class NotificationInterruptStateProviderImpl implements NotificationInterruptStateProvider {
     private static final String TAG = "InterruptionStateProvider";
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = true; //false;
     private static final boolean DEBUG_HEADS_UP = true;
     private static final boolean ENABLE_HEADS_UP = true;
     private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
 
+    private final List<NotificationInterruptSuppressor> mSuppressors = new ArrayList<>();
     private final StatusBarStateController mStatusBarStateController;
     private final NotificationFilter mNotificationFilter;
-    private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
-
-    private final Context mContext;
+    private final ContentResolver mContentResolver;
     private final PowerManager mPowerManager;
     private final IDreamManager mDreamManager;
+    private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
     private final BatteryController mBatteryController;
-
-    private NotificationPresenter mPresenter;
+    private final ContentObserver mHeadsUpObserver;
     private HeadsUpManager mHeadsUpManager;
-    private HeadsUpSuppressor mHeadsUpSuppressor;
 
-    private ContentObserver mHeadsUpObserver;
     @VisibleForTesting
     protected boolean mUseHeadsUp = false;
-    private boolean mDisableNotificationAlerts;
 
     @Inject
-    public NotificationInterruptionStateProvider(Context context, NotificationFilter filter,
-            StatusBarStateController stateController, BatteryController batteryController) {
-        this(context,
-                (PowerManager) context.getSystemService(Context.POWER_SERVICE),
-                IDreamManager.Stub.asInterface(
-                        ServiceManager.checkService(DreamService.DREAM_SERVICE)),
-                new AmbientDisplayConfiguration(context),
-                filter,
-                batteryController,
-                stateController);
-    }
-
-    @VisibleForTesting
-    protected NotificationInterruptionStateProvider(
-            Context context,
+    public NotificationInterruptStateProviderImpl(
+            ContentResolver contentResolver,
             PowerManager powerManager,
             IDreamManager dreamManager,
             AmbientDisplayConfiguration ambientDisplayConfiguration,
             NotificationFilter notificationFilter,
             BatteryController batteryController,
-            StatusBarStateController statusBarStateController) {
-        mContext = context;
+            StatusBarStateController statusBarStateController,
+            HeadsUpManager headsUpManager,
+            @Main Handler mainHandler) {
+        mContentResolver = contentResolver;
         mPowerManager = powerManager;
         mDreamManager = dreamManager;
         mBatteryController = batteryController;
         mAmbientDisplayConfiguration = ambientDisplayConfiguration;
         mNotificationFilter = notificationFilter;
         mStatusBarStateController = statusBarStateController;
-    }
-
-    /** Sets up late-binding dependencies for this component. */
-    public void setUpWithPresenter(
-            NotificationPresenter notificationPresenter,
-            HeadsUpManager headsUpManager,
-            HeadsUpSuppressor headsUpSuppressor) {
-        setUpWithPresenter(notificationPresenter, headsUpManager, headsUpSuppressor,
-                new ContentObserver(Dependency.get(Dependency.MAIN_HANDLER)) {
-                    @Override
-                    public void onChange(boolean selfChange) {
-                        boolean wasUsing = mUseHeadsUp;
-                        mUseHeadsUp = ENABLE_HEADS_UP && !mDisableNotificationAlerts
-                                && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
-                                mContext.getContentResolver(),
-                                Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
-                                Settings.Global.HEADS_UP_OFF);
-                        Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
-                        if (wasUsing != mUseHeadsUp) {
-                            if (!mUseHeadsUp) {
-                                Log.d(TAG,
-                                        "dismissing any existing heads up notification on disable"
-                                                + " event");
-                                mHeadsUpManager.releaseAllImmediately();
-                            }
-                        }
-                    }
-                });
-    }
-
-    /** Sets up late-binding dependencies for this component. */
-    public void setUpWithPresenter(
-            NotificationPresenter notificationPresenter,
-            HeadsUpManager headsUpManager,
-            HeadsUpSuppressor headsUpSuppressor,
-            ContentObserver observer) {
-        mPresenter = notificationPresenter;
         mHeadsUpManager = headsUpManager;
-        mHeadsUpSuppressor = headsUpSuppressor;
-        mHeadsUpObserver = observer;
+        mHeadsUpObserver = new ContentObserver(mainHandler) {
+            @Override
+            public void onChange(boolean selfChange) {
+                boolean wasUsing = mUseHeadsUp;
+                mUseHeadsUp = ENABLE_HEADS_UP
+                        && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
+                        mContentResolver,
+                        Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
+                        Settings.Global.HEADS_UP_OFF);
+                Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
+                if (wasUsing != mUseHeadsUp) {
+                    if (!mUseHeadsUp) {
+                        Log.d(TAG, "dismissing any existing heads up notification on "
+                                + "disable event");
+                        mHeadsUpManager.releaseAllImmediately();
+                    }
+                }
+            }
+        };
 
         if (ENABLE_HEADS_UP) {
-            mContext.getContentResolver().registerContentObserver(
+            mContentResolver.registerContentObserver(
                     Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED),
                     true,
                     mHeadsUpObserver);
-            mContext.getContentResolver().registerContentObserver(
+            mContentResolver.registerContentObserver(
                     Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
                     mHeadsUpObserver);
         }
         mHeadsUpObserver.onChange(true); // set up
     }
 
-    /**
-     * Whether the notification should appear as a bubble with a fly-out on top of the screen.
-     *
-     * @param entry the entry to check
-     * @return true if the entry should bubble up, false otherwise
-     */
+    @Override
+    public void addSuppressor(NotificationInterruptSuppressor suppressor) {
+        mSuppressors.add(suppressor);
+    }
+
+    @Override
     public boolean shouldBubbleUp(NotificationEntry entry) {
         final StatusBarNotification sbn = entry.getSbn();
 
@@ -201,12 +167,8 @@
         return true;
     }
 
-    /**
-     * Whether the notification should peek in from the top and alert the user.
-     *
-     * @param entry the entry to check
-     * @return true if the entry should heads up, false otherwise
-     */
+
+    @Override
     public boolean shouldHeadsUp(NotificationEntry entry) {
         if (mStatusBarStateController.isDozing()) {
             return shouldHeadsUpWhenDozing(entry);
@@ -215,6 +177,17 @@
         }
     }
 
+    /**
+     * When an entry was added, should we launch its fullscreen intent? Examples are Alarms or
+     * incoming calls.
+     */
+    @Override
+    public boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry) {
+        return entry.getSbn().getNotification().fullScreenIntent != null
+                && (!shouldHeadsUp(entry)
+                || mStatusBarStateController.getState() == StatusBarState.KEYGUARD);
+    }
+
     private boolean shouldHeadsUpWhenAwake(NotificationEntry entry) {
         StatusBarNotification sbn = entry.getSbn();
 
@@ -271,13 +244,15 @@
             return false;
         }
 
-        if (!mHeadsUpSuppressor.canHeadsUp(entry, sbn)) {
-            if (DEBUG_HEADS_UP) {
-                Log.d(TAG, "No heads up: aborted by suppressor: " + sbn.getKey());
+        for (int i = 0; i < mSuppressors.size(); i++) {
+            if (mSuppressors.get(i).suppressAwakeHeadsUp(entry)) {
+                if (DEBUG_HEADS_UP) {
+                    Log.d(TAG, "No heads up: aborted by suppressor: "
+                            + mSuppressors.get(i).getName() + " sbnKey=" + sbn.getKey());
+                }
+                return false;
             }
-            return false;
         }
-
         return true;
     }
 
@@ -325,7 +300,7 @@
             }
             return false;
         }
-         return true;
+        return true;
     }
 
     /**
@@ -334,8 +309,7 @@
      * @param entry the entry to check
      * @return true if these checks pass, false if the notification should not alert
      */
-    @VisibleForTesting
-    public boolean canAlertCommon(NotificationEntry entry) {
+    private boolean canAlertCommon(NotificationEntry entry) {
         StatusBarNotification sbn = entry.getSbn();
 
         if (mNotificationFilter.shouldFilterOut(entry)) {
@@ -352,6 +326,16 @@
             }
             return false;
         }
+
+        for (int i = 0; i < mSuppressors.size(); i++) {
+            if (mSuppressors.get(i).suppressInterruptions(entry)) {
+                if (DEBUG_HEADS_UP) {
+                    Log.d(TAG, "No alerting: aborted by suppressor: "
+                            + mSuppressors.get(i).getName() + " sbnKey=" + sbn.getKey());
+                }
+                return false;
+            }
+        }
         return true;
     }
 
@@ -361,15 +345,17 @@
      * @param entry the entry to check
      * @return true if these checks pass, false if the notification should not alert
      */
-    @VisibleForTesting
-    public boolean canAlertAwakeCommon(NotificationEntry entry) {
+    private boolean canAlertAwakeCommon(NotificationEntry entry) {
         StatusBarNotification sbn = entry.getSbn();
 
-        if (mPresenter.isDeviceInVrMode()) {
-            if (DEBUG_HEADS_UP) {
-                Log.d(TAG, "No alerting: no huns or vr mode");
+        for (int i = 0; i < mSuppressors.size(); i++) {
+            if (mSuppressors.get(i).suppressAwakeInterruptions(entry)) {
+                if (DEBUG_HEADS_UP) {
+                    Log.d(TAG, "No alerting: aborted by suppressor: "
+                            + mSuppressors.get(i).getName() + " sbnKey=" + sbn.getKey());
+                }
+                return false;
             }
-            return false;
         }
 
         if (isSnoozedPackage(sbn)) {
@@ -392,54 +378,4 @@
     private boolean isSnoozedPackage(StatusBarNotification sbn) {
         return mHeadsUpManager.isSnoozed(sbn.getPackageName());
     }
-
-    /** Sets whether to disable all alerts. */
-    public void setDisableNotificationAlerts(boolean disableNotificationAlerts) {
-        mDisableNotificationAlerts = disableNotificationAlerts;
-        mHeadsUpObserver.onChange(true);
-    }
-
-    /** Whether all alerts are disabled. */
-    @VisibleForTesting
-    public boolean areNotificationAlertsDisabled() {
-        return mDisableNotificationAlerts;
-    }
-
-    /** Whether HUNs should be used. */
-    @VisibleForTesting
-    public boolean getUseHeadsUp() {
-        return mUseHeadsUp;
-    }
-
-    protected NotificationPresenter getPresenter() {
-        return mPresenter;
-    }
-
-    /**
-     * When an entry was added, should we launch its fullscreen intent? Examples are Alarms or
-     * incoming calls.
-     *
-     * @param entry the entry that was added
-     * @return {@code true} if we should launch the full screen intent
-     */
-    public boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry) {
-        return entry.getSbn().getNotification().fullScreenIntent != null
-            && (!shouldHeadsUp(entry)
-                || mStatusBarStateController.getState() == StatusBarState.KEYGUARD);
-    }
-
-    /** A component which can suppress heads-up notifications due to the overall state of the UI. */
-    public interface HeadsUpSuppressor {
-        /**
-         * Returns false if the provided notification is ineligible for heads-up according to this
-         * component.
-         *
-         * @param entry entry of the notification that might be heads upped
-         * @param sbn   notification that might be heads upped
-         * @return false if the notification can not be heads upped
-         */
-        boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn);
-
-    }
-
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptSuppressor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptSuppressor.java
new file mode 100644
index 0000000..c19f8bd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptSuppressor.java
@@ -0,0 +1,64 @@
+/*
+ * 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.interruption;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+/** A component which can suppress visual interruptions of notifications such as heads-up and
+ *  bubble-up.
+ */
+public interface NotificationInterruptSuppressor {
+    /**
+     * A unique name to identify this suppressor.
+     */
+    default String getName() {
+        return this.getClass().getName();
+    }
+
+    /**
+     * Returns true if the provided notification is, when the device is awake, ineligible for
+     * heads-up according to this component.
+     *
+     * @param entry entry of the notification that might heads-up
+     * @return true if the heads up interruption should be suppressed when the device is awake
+     */
+    default boolean suppressAwakeHeadsUp(NotificationEntry entry) {
+        return false;
+    }
+
+    /**
+     * Returns true if the provided notification is, when the device is awake, ineligible for
+     * heads-up or bubble-up according to this component.
+     *
+     * @param entry entry of the notification that might heads-up or bubble-up
+     * @return true if interruptions should be suppressed when the device is awake
+     */
+    default boolean suppressAwakeInterruptions(NotificationEntry entry) {
+        return false;
+    }
+
+    /**
+     * Returns true if the provided notification is, regardless of awake/dozing state,
+     * ineligible for heads-up or bubble-up according to this component.
+     *
+     * @param entry entry of the notification that might heads-up or bubble-up
+     * @return true if interruptions should be suppressed
+     */
+    default boolean suppressInterruptions(NotificationEntry entry) {
+        return false;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
index 597bdb9..be3873a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
@@ -83,7 +83,7 @@
 
     private val Ranking.personTypeInfo
         get() = when {
-            channel.isImportantConversation -> TYPE_IMPORTANT_PERSON
+            channel?.isImportantConversation == true -> TYPE_IMPORTANT_PERSON
             isConversation -> TYPE_PERSON
             else -> TYPE_NON_PERSON
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 9a4e789..f61fe98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -31,7 +31,6 @@
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.Notification;
 import android.app.NotificationChannel;
 import android.content.Context;
 import android.content.pm.PackageInfo;
@@ -151,7 +150,6 @@
     private int mNotificationMinHeight;
     private int mNotificationMinHeightLarge;
     private int mNotificationMinHeightMedia;
-    private int mNotificationMinHeightMessaging;
     private int mNotificationMaxHeight;
     private int mIncreasedPaddingBetweenElements;
     private int mNotificationLaunchHeight;
@@ -640,16 +638,10 @@
                 && expandedView.findViewById(com.android.internal.R.id.media_actions) != null;
         boolean showCompactMediaSeekbar = mMediaManager.getShowCompactMediaSeekbar();
 
-        Class<? extends Notification.Style> style =
-                mEntry.getSbn().getNotification().getNotificationStyle();
-        boolean isMessagingLayout = Notification.MessagingStyle.class.equals(style);
-
         if (customView && beforeP && !mIsSummaryWithChildren) {
             minHeight = beforeN ? mNotificationMinHeightBeforeN : mNotificationMinHeightBeforeP;
         } else if (isMediaLayout && showCompactMediaSeekbar) {
             minHeight = mNotificationMinHeightMedia;
-        } else if (isMessagingLayout) {
-            minHeight = mNotificationMinHeightMessaging;
         } else if (mUseIncreasedCollapsedHeight && layout == mPrivateLayout) {
             minHeight = mNotificationMinHeightLarge;
         } else {
@@ -1057,19 +1049,6 @@
         return getShowingLayout().getVisibleNotificationHeader();
     }
 
-
-    /**
-     * @return the contracted notification header. This can be different from
-     * {@link #getNotificationHeader()} and also {@link #getVisibleNotificationHeader()} and only
-     * returns the contracted version.
-     */
-    public NotificationHeaderView getContractedNotificationHeader() {
-        if (mIsSummaryWithChildren) {
-            return mChildrenContainer.getHeaderView();
-        }
-        return mPrivateLayout.getContractedNotificationHeader();
-    }
-
     public void setLongPressListener(LongPressListener longPressListener) {
         mLongPressListener = longPressListener;
     }
@@ -1654,8 +1633,6 @@
                 R.dimen.notification_min_height_increased);
         mNotificationMinHeightMedia = NotificationUtils.getFontScaledHeight(mContext,
                 R.dimen.notification_min_height_media);
-        mNotificationMinHeightMessaging = NotificationUtils.getFontScaledHeight(mContext,
-                R.dimen.notification_min_height_messaging);
         mNotificationMaxHeight = NotificationUtils.getFontScaledHeight(mContext,
                 R.dimen.notification_max_height);
         mMaxHeadsUpHeightBeforeN = NotificationUtils.getFontScaledHeight(mContext,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 6dd4ff9..91cf285 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -25,6 +25,8 @@
 import android.annotation.Nullable;
 import android.app.Notification;
 import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.ApplicationInfo;
 import android.os.AsyncTask;
 import android.os.CancellationSignal;
 import android.service.notification.StatusBarNotification;
@@ -716,6 +718,10 @@
                         sbn.getNotification());
 
                 Context packageContext = sbn.getPackageContext(mContext);
+                if (recoveredBuilder.usesTemplate()) {
+                    // For all of our templates, we want it to be RTL
+                    packageContext = new RtlEnabledContext(packageContext);
+                }
                 Notification notification = sbn.getNotification();
                 if (notification.isMediaNotification()) {
                     MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
@@ -782,6 +788,19 @@
             // try to purge unnecessary cached entries.
             mRow.getImageResolver().purgeCache();
         }
+
+        private class RtlEnabledContext extends ContextWrapper {
+            private RtlEnabledContext(Context packageContext) {
+                super(packageContext);
+            }
+
+            @Override
+            public ApplicationInfo getApplicationInfo() {
+                ApplicationInfo applicationInfo = super.getApplicationInfo();
+                applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_RTL;
+                return applicationInfo;
+            }
+        }
     }
 
     @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 27fd1b2..8b8a901 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -1492,13 +1492,6 @@
         }
     }
 
-    public NotificationHeaderView getContractedNotificationHeader() {
-        if (mContractedChild != null) {
-            return mContractedWrapper.getNotificationHeader();
-        }
-        return null;
-    }
-
     public NotificationHeaderView getVisibleNotificationHeader() {
         NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
         return wrapper == null ? null : wrapper.getNotificationHeader();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
new file mode 100644
index 0000000..1e2571b5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -0,0 +1,100 @@
+/*
+ * 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.row.wrapper
+
+import android.content.Context
+import android.view.View
+
+import com.android.internal.widget.ConversationLayout
+import com.android.internal.widget.MessagingLinearLayout
+import com.android.systemui.R
+import com.android.systemui.statusbar.notification.NotificationUtils
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+
+/**
+ * Wraps a notification containing a converation template
+ */
+class NotificationConversationTemplateViewWrapper constructor(
+    ctx: Context,
+    view: View,
+    row: ExpandableNotificationRow
+)
+    : NotificationTemplateViewWrapper(ctx, view, row) {
+
+    private val minHeightWithActions: Int
+    private val conversationLayout: ConversationLayout
+    private var conversationIcon: View? = null
+    private var conversationBadge: View? = null
+    private var expandButton: View? = null
+    private var messagingLinearLayout: MessagingLinearLayout? = null
+
+    init {
+        conversationLayout = view as ConversationLayout
+        minHeightWithActions = NotificationUtils.getFontScaledHeight(ctx,
+                R.dimen.notification_messaging_actions_min_height)
+    }
+
+    private fun resolveViews() {
+        messagingLinearLayout = conversationLayout.messagingLinearLayout
+        conversationIcon = conversationLayout.requireViewById(
+                com.android.internal.R.id.conversation_icon)
+        conversationBadge = conversationLayout.requireViewById(
+                com.android.internal.R.id.conversation_icon_badge)
+        expandButton = conversationLayout.requireViewById(
+                com.android.internal.R.id.expand_button)
+    }
+
+    override fun onContentUpdated(row: ExpandableNotificationRow) {
+        // Reinspect the notification. Before the super call, because the super call also updates
+        // the transformation types and we need to have our values set by then.
+        resolveViews()
+        super.onContentUpdated(row)
+    }
+
+    override fun updateTransformedTypes() {
+        // This also clears the existing types
+        super.updateTransformedTypes()
+        messagingLinearLayout?.let {
+            mTransformationHelper.addTransformedView(it.id, it)
+        }
+        conversationIcon?.let {
+            mTransformationHelper.addViewTransformingToSimilar(it.id, it)
+        }
+        conversationBadge?.let {
+            mTransformationHelper.addViewTransformingToSimilar(it.id, it)
+        }
+        expandButton?.let {
+            mTransformationHelper.addViewTransformingToSimilar(it.id, it)
+        }
+    }
+
+    override fun setRemoteInputVisible(visible: Boolean) {
+        conversationLayout.showHistoricMessages(visible)
+    }
+
+    override fun updateExpandability(expandable: Boolean, onClickListener: View.OnClickListener?) {
+        conversationLayout.updateExpandability(expandable, onClickListener)
+    }
+
+    override fun getMinLayoutHeight(): Int {
+        if (mActionsContainer != null && mActionsContainer.visibility != View.GONE) {
+            return minHeightWithActions
+        } else {
+            return super.getMinLayoutHeight()
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 5e52c0a..1d06198 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -53,12 +53,13 @@
     protected final ViewTransformationHelper mTransformationHelper;
 
     protected int mColor;
-    private ImageView mIcon;
 
+    private ImageView mIcon;
     private NotificationExpandButton mExpandButton;
     protected NotificationHeaderView mNotificationHeader;
     private TextView mHeaderText;
     private ImageView mWorkProfileImage;
+
     private boolean mIsLowPriority;
     private boolean mTransformLowPriorityTitle;
     private boolean mShowExpandButtonAtEnd;
@@ -105,12 +106,16 @@
         mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button);
         mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge);
         mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header);
-        mNotificationHeader.setShowExpandButtonAtEnd(mShowExpandButtonAtEnd);
-        mColor = mNotificationHeader.getOriginalIconColor();
+        if (mNotificationHeader != null) {
+            mNotificationHeader.setShowExpandButtonAtEnd(mShowExpandButtonAtEnd);
+            mColor = mNotificationHeader.getOriginalIconColor();
+        }
     }
 
     private void addAppOpsOnClickListener(ExpandableNotificationRow row) {
-        mNotificationHeader.setAppOpsOnClickListener(row.getAppOpsOnClickListener());
+        if (mNotificationHeader != null) {
+            mNotificationHeader.setAppOpsOnClickListener(row.getAppOpsOnClickListener());
+        }
     }
 
     @Override
@@ -127,9 +132,11 @@
         updateCropToPaddingForImageViews();
         Notification notification = row.getEntry().getSbn().getNotification();
         mIcon.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon());
-        // The work profile image is always the same lets just set the icon tag for it not to
-        // animate
-        mWorkProfileImage.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon());
+        if (mWorkProfileImage != null) {
+            // The work profile image is always the same lets just set the icon tag for it not to
+            // animate
+            mWorkProfileImage.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon());
+        }
 
         // We need to reset all views that are no longer transforming in case a view was previously
         // transformed, but now we decided to transform its container instead.
@@ -174,8 +181,9 @@
 
     protected void updateTransformedTypes() {
         mTransformationHelper.reset();
-        mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_ICON, mIcon);
-        if (mIsLowPriority) {
+        mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_ICON,
+                mIcon);
+        if (mIsLowPriority && mHeaderText != null) {
             mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE,
                     mHeaderText);
         }
@@ -184,7 +192,9 @@
     @Override
     public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {
         mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE);
-        mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
+        if (mNotificationHeader != null) {
+            mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
index 0a1a2fe..d41f5af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
@@ -353,8 +353,12 @@
     @Override
     public void setHeaderVisibleAmount(float headerVisibleAmount) {
         super.setHeaderVisibleAmount(headerVisibleAmount);
-        mNotificationHeader.setAlpha(headerVisibleAmount);
-        mHeaderTranslation = (1.0f - headerVisibleAmount) * mFullHeaderTranslation;
+        float headerTranslation = 0f;
+        if (mNotificationHeader != null) {
+            mNotificationHeader.setAlpha(headerVisibleAmount);
+            headerTranslation = (1.0f - headerVisibleAmount) * mFullHeaderTranslation;
+        }
+        mHeaderTranslation = headerTranslation;
         mView.setTranslationY(mHeaderTranslation);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index c2eff8a..c834e4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -35,6 +35,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.ColorUtils;
 import com.android.internal.util.ContrastColorUtil;
+import com.android.internal.widget.ConversationLayout;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.TransformableView;
 import com.android.systemui.statusbar.notification.TransformState;
@@ -61,6 +62,9 @@
                 return new NotificationMediaTemplateViewWrapper(ctx, v, row);
             } else if ("messaging".equals(v.getTag())) {
                 return new NotificationMessagingTemplateViewWrapper(ctx, v, row);
+            } else if ("conversation".equals(v.getTag())) {
+                return new NotificationConversationTemplateViewWrapper(ctx, (ConversationLayout) v,
+                        row);
             }
             Class<? extends Notification.Style> style =
                     row.getEntry().getSbn().getNotification().getNotificationStyle();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index ee31300..90bc075b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -492,7 +492,7 @@
                     try {
                         result = ActivityTaskManager.getService().startActivityAsUser(
                                 null, getContext().getBasePackageName(),
-                                getContext().getFeatureId(), intent,
+                                getContext().getAttributionTag(), intent,
                                 intent.resolveTypeIfNeeded(getContext().getContentResolver()),
                                 null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, o.toBundle(),
                                 UserHandle.CURRENT.getIdentifier());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
index c5c3fff..c54fa29 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
@@ -27,6 +27,8 @@
  */
 public class KeyguardIndicationTextView extends TextView {
 
+    private CharSequence mText = "";
+
     public KeyguardIndicationTextView(Context context) {
         super(context);
     }
@@ -53,10 +55,12 @@
 
         // TODO: Animation, make sure that we will show one indication long enough.
         if (TextUtils.isEmpty(text)) {
+            mText = "";
             setVisibility(View.INVISIBLE);
-        } else {
+        } else if (!TextUtils.equals(text, mText)) {
+            mText = text;
             setVisibility(View.VISIBLE);
-            setText(text);
+            setText(mText);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index f38d416..596a607 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -18,7 +18,6 @@
 
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 
-import android.annotation.Nullable;
 import android.app.StatusBarManager;
 import android.graphics.RectF;
 import android.hardware.display.AmbientDisplayConfiguration;
@@ -44,7 +43,7 @@
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationShadeWindowBlurController;
+import com.android.systemui.statusbar.NotificationShadeDepthController;
 import com.android.systemui.statusbar.PulseExpansionHandler;
 import com.android.systemui.statusbar.SuperStatusBarViewFactory;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -82,7 +81,7 @@
     private final CommandQueue mCommandQueue;
     private final NotificationShadeWindowView mView;
     private final ShadeController mShadeController;
-    private final NotificationShadeWindowBlurController mBlurController;
+    private final NotificationShadeDepthController mDepthController;
 
     private GestureDetector mGestureDetector;
     private View mBrightnessMirror;
@@ -126,7 +125,7 @@
             CommandQueue commandQueue,
             ShadeController shadeController,
             DockManager dockManager,
-            @Nullable NotificationShadeWindowBlurController blurController,
+            NotificationShadeDepthController depthController,
             NotificationShadeWindowView notificationShadeWindowView,
             NotificationPanelViewController notificationPanelViewController,
             SuperStatusBarViewFactory statusBarViewFactory) {
@@ -149,7 +148,7 @@
         mShadeController = shadeController;
         mDockManager = dockManager;
         mNotificationPanelViewController = notificationPanelViewController;
-        mBlurController = blurController;
+        mDepthController = depthController;
         mStatusBarViewFactory = statusBarViewFactory;
 
         // This view is not part of the newly inflated expanded status bar.
@@ -394,10 +393,8 @@
                         mView.getContext(), mView, expandHelperCallback,
                         dragDownCallback, mFalsingManager));
 
-        if (mBlurController != null) {
-            mBlurController.setRoot(mView);
-            mNotificationPanelViewController.addExpansionListener(mBlurController);
-        }
+        mDepthController.setRoot(mView);
+        mNotificationPanelViewController.addExpansionListener(mDepthController);
     }
 
     public NotificationShadeWindowView getView() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index b3a62d8..d343090 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -185,15 +185,15 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
-import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.init.NotificationsController;
+import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
+import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -404,10 +404,9 @@
 
     private final NotificationGutsManager mGutsManager;
     private final NotificationLogger mNotificationLogger;
-    private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
     private final NotificationViewHierarchyManager mViewHierarchyManager;
     private final KeyguardViewMediator mKeyguardViewMediator;
-    private final NotificationAlertingManager mNotificationAlertingManager;
+    protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
 
     // for disabling the status bar
     private int mDisabled1 = 0;
@@ -621,10 +620,10 @@
             RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
             NotificationGutsManager notificationGutsManager,
             NotificationLogger notificationLogger,
-            NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+            NotificationInterruptStateProvider notificationInterruptStateProvider,
             NotificationViewHierarchyManager notificationViewHierarchyManager,
             KeyguardViewMediator keyguardViewMediator,
-            NotificationAlertingManager notificationAlertingManager,
+            NotificationAlertingManager notificationAlertingManager, // need to inject for now
             DisplayMetrics displayMetrics,
             MetricsLogger metricsLogger,
             @UiBackground Executor uiBgExecutor,
@@ -701,10 +700,9 @@
         mRemoteInputQuickSettingsDisabler = remoteInputQuickSettingsDisabler;
         mGutsManager = notificationGutsManager;
         mNotificationLogger = notificationLogger;
-        mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
+        mNotificationInterruptStateProvider = notificationInterruptStateProvider;
         mViewHierarchyManager = notificationViewHierarchyManager;
         mKeyguardViewMediator = keyguardViewMediator;
-        mNotificationAlertingManager = notificationAlertingManager;
         mDisplayMetrics = displayMetrics;
         mMetricsLogger = metricsLogger;
         mUiBgExecutor = uiBgExecutor;
@@ -1238,9 +1236,9 @@
         mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanelViewController,
                 mHeadsUpManager, mNotificationShadeWindowView, mStackScroller, mDozeScrimController,
                 mScrimController, mActivityLaunchAnimator, mDynamicPrivacyController,
-                mNotificationAlertingManager, mKeyguardStateController,
-                mKeyguardIndicationController,
-                this /* statusBar */, mShadeController, mCommandQueue, mInitController);
+                mKeyguardStateController, mKeyguardIndicationController,
+                this /* statusBar */, mShadeController, mCommandQueue, mInitController,
+                mNotificationInterruptStateProvider);
 
         mNotificationShelf.setOnActivatedListener(mPresenter);
         mRemoteInputManager.getController().addCallback(mNotificationShadeWindowController);
@@ -1589,8 +1587,9 @@
         }
 
         if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
-            mNotificationInterruptionStateProvider.setDisableNotificationAlerts(
-                    (state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0);
+            if (areNotificationAlertsDisabled()) {
+                mHeadsUpManager.releaseAllImmediately();
+            }
         }
 
         if ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) {
@@ -1605,6 +1604,10 @@
         }
     }
 
+    boolean areNotificationAlertsDisabled() {
+        return (mDisabled1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
+    }
+
     protected H createHandler() {
         return new StatusBar.H();
     }
@@ -2599,7 +2602,7 @@
             }
             try {
                 result = ActivityTaskManager.getService().startActivityAsUser(
-                        null, mContext.getBasePackageName(), mContext.getFeatureId(),
+                        null, mContext.getBasePackageName(), mContext.getAttributionTag(),
                         intent,
                         intent.resolveTypeIfNeeded(mContext.getContentResolver()),
                         null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index e1a20b6..53fa263 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -68,12 +68,12 @@
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.HeadsUpUtil;
@@ -108,7 +108,7 @@
     private final NotifCollection mNotifCollection;
     private final FeatureFlags mFeatureFlags;
     private final StatusBarStateController mStatusBarStateController;
-    private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+    private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
     private final MetricsLogger mMetricsLogger;
     private final Context mContext;
     private final NotificationPanelViewController mNotificationPanel;
@@ -142,7 +142,7 @@
             NotificationLockscreenUserManager lockscreenUserManager,
             ShadeController shadeController, StatusBar statusBar,
             KeyguardStateController keyguardStateController,
-            NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+            NotificationInterruptStateProvider notificationInterruptStateProvider,
             MetricsLogger metricsLogger, LockPatternUtils lockPatternUtils,
             Handler mainThreadHandler, Handler backgroundHandler, Executor uiBgExecutor,
             ActivityIntentHelper activityIntentHelper, BubbleController bubbleController,
@@ -167,7 +167,7 @@
         mActivityStarter = activityStarter;
         mEntryManager = entryManager;
         mStatusBarStateController = statusBarStateController;
-        mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
+        mNotificationInterruptStateProvider = notificationInterruptStateProvider;
         mMetricsLogger = metricsLogger;
         mAssistManagerLazy = assistManagerLazy;
         mGroupManager = groupManager;
@@ -436,7 +436,7 @@
     }
 
     private void handleFullScreenIntent(NotificationEntry entry) {
-        if (mNotificationInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) {
+        if (mNotificationInterruptStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) {
             if (shouldSuppressFullScreenIntent(entry)) {
                 if (DEBUG) {
                     Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + entry.getKey());
@@ -603,7 +603,7 @@
         private final ActivityIntentHelper mActivityIntentHelper;
         private final BubbleController mBubbleController;
         private NotificationPanelViewController mNotificationPanelViewController;
-        private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+        private NotificationInterruptStateProvider mNotificationInterruptStateProvider;
         private final ShadeController mShadeController;
         private NotificationPresenter mNotificationPresenter;
         private ActivityLaunchAnimator mActivityLaunchAnimator;
@@ -626,7 +626,7 @@
                 NotificationGroupManager groupManager,
                 NotificationLockscreenUserManager lockscreenUserManager,
                 KeyguardStateController keyguardStateController,
-                NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+                NotificationInterruptStateProvider notificationInterruptStateProvider,
                 MetricsLogger metricsLogger,
                 LockPatternUtils lockPatternUtils,
                 @Main Handler mainThreadHandler,
@@ -654,7 +654,7 @@
             mGroupManager = groupManager;
             mLockscreenUserManager = lockscreenUserManager;
             mKeyguardStateController = keyguardStateController;
-            mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
+            mNotificationInterruptStateProvider = notificationInterruptStateProvider;
             mMetricsLogger = metricsLogger;
             mLockPatternUtils = lockPatternUtils;
             mMainThreadHandler = mainThreadHandler;
@@ -712,7 +712,7 @@
                     mShadeController,
                     mStatusBar,
                     mKeyguardStateController,
-                    mNotificationInterruptionStateProvider,
+                    mNotificationInterruptStateProvider,
                     mMetricsLogger,
                     mLockPatternUtils,
                     mMainThreadHandler,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 30d6b507..79cea91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -60,13 +60,13 @@
 import com.android.systemui.statusbar.notification.AboveShelfObserver;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -98,8 +98,6 @@
             (SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class);
     private final NotificationEntryManager mEntryManager =
             Dependency.get(NotificationEntryManager.class);
-    private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
-            Dependency.get(NotificationInterruptionStateProvider.class);
     private final NotificationMediaManager mMediaManager =
             Dependency.get(NotificationMediaManager.class);
     private final VisualStabilityManager mVisualStabilityManager =
@@ -140,13 +138,13 @@
             ScrimController scrimController,
             ActivityLaunchAnimator activityLaunchAnimator,
             DynamicPrivacyController dynamicPrivacyController,
-            NotificationAlertingManager notificationAlertingManager,
             KeyguardStateController keyguardStateController,
             KeyguardIndicationController keyguardIndicationController,
             StatusBar statusBar,
             ShadeController shadeController,
             CommandQueue commandQueue,
-            InitController initController) {
+            InitController initController,
+            NotificationInterruptStateProvider notificationInterruptStateProvider) {
         mContext = context;
         mKeyguardStateController = keyguardStateController;
         mNotificationPanel = panel;
@@ -216,8 +214,7 @@
             mEntryManager.addNotificationLifetimeExtender(mGutsManager);
             mEntryManager.addNotificationLifetimeExtenders(
                     remoteInputManager.getLifetimeExtenders());
-            mNotificationInterruptionStateProvider.setUpWithPresenter(
-                    this, mHeadsUpManager, this::canHeadsUp);
+            notificationInterruptStateProvider.addSuppressor(mInterruptSuppressor);
             mLockscreenUserManager.setUpWithPresenter(this);
             mMediaManager.setUpWithPresenter(this);
             mVisualStabilityManager.setUpWithPresenter(this);
@@ -336,39 +333,6 @@
         return mEntryManager.hasActiveNotifications();
     }
 
-    public boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn) {
-        if (mStatusBar.isOccluded()) {
-            boolean devicePublic = mLockscreenUserManager.
-                    isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
-            boolean userPublic = devicePublic
-                    || mLockscreenUserManager.isLockscreenPublicMode(sbn.getUserId());
-            boolean needsRedaction = mLockscreenUserManager.needsRedaction(entry);
-            if (userPublic && needsRedaction) {
-                // TODO(b/135046837): we can probably relax this with dynamic privacy
-                return false;
-            }
-        }
-
-        if (!mCommandQueue.panelsEnabled()) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: disabled panel : " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (sbn.getNotification().fullScreenIntent != null) {
-            if (mAccessibilityManager.isTouchExplorationEnabled()) {
-                if (DEBUG) Log.d(TAG, "No heads up: accessible fullscreen: " + sbn.getKey());
-                return false;
-            } else {
-                // we only allow head-up on the lockscreen if it doesn't have a fullscreen intent
-                return !mKeyguardStateController.isShowing()
-                        || mStatusBar.isOccluded();
-            }
-        }
-        return true;
-    }
-
     @Override
     public void onUserSwitched(int newUserId) {
         // Begin old BaseStatusBar.userSwitched
@@ -507,4 +471,66 @@
             }
         }
     };
+
+    private final NotificationInterruptSuppressor mInterruptSuppressor =
+            new NotificationInterruptSuppressor() {
+        @Override
+        public String getName() {
+            return TAG;
+        }
+
+        @Override
+        public boolean suppressAwakeHeadsUp(NotificationEntry entry) {
+            final StatusBarNotification sbn = entry.getSbn();
+            if (mStatusBar.isOccluded()) {
+                boolean devicePublic = mLockscreenUserManager
+                        .isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
+                boolean userPublic = devicePublic
+                        || mLockscreenUserManager.isLockscreenPublicMode(sbn.getUserId());
+                boolean needsRedaction = mLockscreenUserManager.needsRedaction(entry);
+                if (userPublic && needsRedaction) {
+                    // TODO(b/135046837): we can probably relax this with dynamic privacy
+                    return true;
+                }
+            }
+
+            if (!mCommandQueue.panelsEnabled()) {
+                if (DEBUG) {
+                    Log.d(TAG, "No heads up: disabled panel : " + sbn.getKey());
+                }
+                return true;
+            }
+
+            if (sbn.getNotification().fullScreenIntent != null) {
+                // we don't allow head-up on the lockscreen (unless there's a
+                // "showWhenLocked" activity currently showing)  if
+                // the potential HUN has a fullscreen intent
+                if (mKeyguardStateController.isShowing() && !mStatusBar.isOccluded()) {
+                    if (DEBUG) {
+                        Log.d(TAG, "No heads up: entry has fullscreen intent on lockscreen "
+                                + sbn.getKey());
+                    }
+                    return true;
+                }
+
+                if (mAccessibilityManager.isTouchExplorationEnabled()) {
+                    if (DEBUG) {
+                        Log.d(TAG, "No heads up: accessible fullscreen: " + sbn.getKey());
+                    }
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public boolean suppressAwakeInterruptions(NotificationEntry entry) {
+            return isDeviceInVrMode();
+        }
+
+        @Override
+        public boolean suppressInterruptions(NotificationEntry entry) {
+            return mStatusBar.areNotificationAlertsDisabled();
+        }
+    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index eec8d50..bbc7e7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -56,13 +56,13 @@
 import com.android.systemui.statusbar.SuperStatusBarViewFactory;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.init.NotificationsController;
+import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
+import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.phone.AutoHideController;
@@ -139,7 +139,7 @@
             RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
             NotificationGutsManager notificationGutsManager,
             NotificationLogger notificationLogger,
-            NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+            NotificationInterruptStateProvider notificationInterruptStateProvider,
             NotificationViewHierarchyManager notificationViewHierarchyManager,
             KeyguardViewMediator keyguardViewMediator,
             NotificationAlertingManager notificationAlertingManager,
@@ -218,7 +218,7 @@
                 remoteInputQuickSettingsDisabler,
                 notificationGutsManager,
                 notificationLogger,
-                notificationInterruptionStateProvider,
+                notificationInterruptStateProvider,
                 notificationViewHierarchyManager,
                 keyguardViewMediator,
                 notificationAlertingManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 759bad4..812ce1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -236,7 +236,7 @@
 
             String action = intent.getAction();
             if (action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {
-                String tz = intent.getStringExtra("time-zone");
+                String tz = intent.getStringExtra(Intent.EXTRA_TIMEZONE);
                 handler.post(() -> {
                     mCalendar = Calendar.getInstance(TimeZone.getTimeZone(tz));
                     if (mClockFormat != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index cebcf76..54e8e72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -577,7 +577,7 @@
     }
 
     boolean isDataDisabled() {
-        return !mPhone.isDataConnectionEnabled();
+        return !mPhone.isDataConnectionAllowed();
     }
 
     @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index b84208c..99709402 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -630,7 +630,7 @@
     @VisibleForTesting
     void doUpdateMobileControllers() {
         List<SubscriptionInfo> subscriptions = mSubscriptionManager
-                .getActiveAndHiddenSubscriptionInfoList();
+                .getCompleteActiveSubscriptionInfoList();
         if (subscriptions == null) {
             subscriptions = Collections.emptyList();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java
index 74739e1..e70e30a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java
@@ -24,6 +24,7 @@
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.annotation.IntDef;
+import android.annotation.UiThread;
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -43,7 +44,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
-import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.Queue;
 import java.util.Set;
@@ -98,8 +98,27 @@
     private TextView mTextView;
 
     @State private int mState = STATE_NOT_SHOWN;
-    private final Set<String> mAudioRecordingApps = new HashSet<>();
-    private final Queue<String> mPendingNotifications = new LinkedList<>();
+    /**
+     * Set of the applications that currently are conducting audio recording.
+     */
+    private final Set<String> mActiveAudioRecordingPackages = new ArraySet<>();
+    /**
+     * Set of applications that we've notified the user about since the indicator came up. Meaning
+     * that if an application is in this list then at some point since the indicator came up, it
+     * was expanded showing this application's title.
+     * Used not to notify the user about the same application again while the indicator is shown.
+     * We empty this set every time the indicator goes off the screen (we always call {@code
+     * mSessionNotifiedPackages.clear()} before calling {@link #hide()}).
+     */
+    private final Set<String> mSessionNotifiedPackages = new ArraySet<>();
+    /**
+     * If an application starts recording while the TV indicator is neither in {@link
+     * #STATE_NOT_SHOWN} nor in {@link #STATE_MINIMIZED}, then we add the application's package
+     * name to the queue, from which we take packages names one by one to disclose the
+     * corresponding applications' titles to the user, whenever the indicator eventually comes to
+     * one of the two aforementioned states.
+     */
+    private final Queue<String> mPendingNotificationPackages = new LinkedList<>();
 
     AudioRecordingDisclosureBar(Context context) {
         mContext = context;
@@ -115,11 +134,16 @@
                 new OnActiveRecordingListener());
     }
 
+    @UiThread
     private void onStartedRecording(String packageName) {
-        if (!mAudioRecordingApps.add(packageName)) {
+        if (!mActiveAudioRecordingPackages.add(packageName)) {
             // This app is already known to perform recording
             return;
         }
+        if (!mSessionNotifiedPackages.add(packageName)) {
+            // We've already notified user about this app, no need to do it again.
+            return;
+        }
 
         switch (mState) {
             case STATE_NOT_SHOWN:
@@ -137,13 +161,14 @@
             case STATE_MINIMIZING:
                 // Currently animating or expanded. Thus add to the pending notifications, and it
                 // will be picked up once the indicator comes to the STATE_MINIMIZED.
-                mPendingNotifications.add(packageName);
+                mPendingNotificationPackages.add(packageName);
                 break;
         }
     }
 
+    @UiThread
     private void onDoneRecording(String packageName) {
-        if (!mAudioRecordingApps.remove(packageName)) {
+        if (!mActiveAudioRecordingPackages.remove(packageName)) {
             // Was not marked as an active recorder, do nothing
             return;
         }
@@ -151,11 +176,13 @@
         // If not MINIMIZED, will check whether the indicator should be hidden when the indicator
         // comes to the STATE_MINIMIZED eventually. If is in the STATE_MINIMIZED, but there are
         // other active recorders - simply ignore.
-        if (mState == STATE_MINIMIZED && mAudioRecordingApps.isEmpty()) {
+        if (mState == STATE_MINIMIZED && mActiveAudioRecordingPackages.isEmpty()) {
+            mSessionNotifiedPackages.clear();
             hide();
         }
     }
 
+    @UiThread
     private void show(String packageName) {
         // Inflate the indicator view
         mIndicatorView = LayoutInflater.from(mContext).inflate(
@@ -230,6 +257,7 @@
         mState = STATE_APPEARING;
     }
 
+    @UiThread
     private void expand(String packageName) {
         final String label = getApplicationLabel(packageName);
         mTextView.setText(mContext.getString(R.string.app_accessed_mic, label));
@@ -253,6 +281,7 @@
         mState = STATE_MAXIMIZING;
     }
 
+    @UiThread
     private void minimize() {
         final int targetOffset = mTextsContainers.getWidth();
         final AnimatorSet set = new AnimatorSet();
@@ -274,6 +303,7 @@
         mState = STATE_MINIMIZING;
     }
 
+    @UiThread
     private void hide() {
         final int targetOffset =
                 mIndicatorView.getWidth() - (int) mIconTextsContainer.getTranslationX();
@@ -294,24 +324,28 @@
         mState = STATE_DISAPPEARING;
     }
 
+    @UiThread
     private void onExpanded() {
         mState = STATE_SHOWN;
 
         mIndicatorView.postDelayed(this::minimize, MAXIMIZED_DURATION);
     }
 
+    @UiThread
     private void onMinimized() {
         mState = STATE_MINIMIZED;
 
-        if (!mPendingNotifications.isEmpty()) {
+        if (!mPendingNotificationPackages.isEmpty()) {
             // There is a new application that started recording, tell the user about it.
-            expand(mPendingNotifications.poll());
-        } else if (mAudioRecordingApps.isEmpty()) {
-            // Nobody is recording anymore, remove the indicator.
+            expand(mPendingNotificationPackages.poll());
+        } else if (mActiveAudioRecordingPackages.isEmpty()) {
+            // Nobody is recording anymore, clear state and remove the indicator.
+            mSessionNotifiedPackages.clear();
             hide();
         }
     }
 
+    @UiThread
     private void onHidden() {
         final WindowManager windowManager = (WindowManager) mContext.getSystemService(
                 Context.WINDOW_SERVICE);
@@ -326,8 +360,15 @@
         mBgRight = null;
 
         mState = STATE_NOT_SHOWN;
+
+        // Check if anybody started recording while we were in STATE_DISAPPEARING
+        if (!mPendingNotificationPackages.isEmpty()) {
+            // There is a new application that started recording, tell the user about it.
+            show(mPendingNotificationPackages.poll());
+        }
     }
 
+    @UiThread
     private void startPulsatingAnimation() {
         final View pulsatingView = mIconTextsContainer.findViewById(R.id.pulsating_circle);
         final ObjectAnimator animator =
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 2452218..248bdc8 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -41,9 +41,9 @@
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.util.leak.LeakDetector;
 
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -69,7 +69,8 @@
     // Map of Uris we listen on to their settings keys.
     private final ArrayMap<Uri, String> mListeningUris = new ArrayMap<>();
     // Map of settings keys to the listener.
-    private final HashMap<String, Set<Tunable>> mTunableLookup = new HashMap<>();
+    private final ConcurrentHashMap<String, Set<Tunable>> mTunableLookup =
+            new ConcurrentHashMap<>();
     // Set of all tunables, used for leak detection.
     private final HashSet<Tunable> mTunables = LeakDetector.ENABLED ? new HashSet<>() : null;
     private final Context mContext;
diff --git a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
index 23df991..ccb8699 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
@@ -324,7 +324,7 @@
 
         @Override
         public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
-                boolean sync) {}
+                float zoom, boolean sync) {}
 
         @Override
         public void dispatchWallpaperCommand(String action, int x, int y,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
index ea6cf33..f7daf97 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
@@ -249,7 +249,7 @@
 
         // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the
         // same answer as KeyguardUpdateMonitor. Remove when this is addressed
-        when(mSubscriptionManager.getActiveAndHiddenSubscriptionInfoList()).thenReturn(
+        when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList()).thenReturn(
                 new ArrayList<>());
 
         when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index eead120..4f4ce13 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -516,7 +516,7 @@
         List<SubscriptionInfo> list = new ArrayList<>();
         list.add(TEST_SUBSCRIPTION);
         list.add(TEST_SUBSCRIPTION_2);
-        when(mSubscriptionManager.getActiveAndHiddenSubscriptionInfoList()).thenReturn(list);
+        when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList()).thenReturn(list);
         mKeyguardUpdateMonitor.mPhoneStateListener.onActiveDataSubscriptionIdChanged(
                 TEST_SUBSCRIPTION_2.getSubscriptionId());
         mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
index 6199181..353fe62 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
@@ -50,6 +50,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Arrays;
+
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 // Need to run tests on main looper because LiveData operations such as setData, observe,
@@ -126,7 +128,7 @@
         when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(null);
         when(mMockSettingsWrapper.getDockedClockFace(anyInt())).thenReturn(null);
         // WHEN settings change event is fired
-        mContentObserver.onChange(false, SETTINGS_URI, MAIN_USER_ID);
+        mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID);
         // THEN the result is null, indicated the default clock face should be used.
         assertThat(mClockManager.getCurrentClock()).isNull();
     }
@@ -136,7 +138,7 @@
         // GIVEN that settings is set to the bubble clock face
         when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK);
         // WHEN settings change event is fired
-        mContentObserver.onChange(false, SETTINGS_URI, MAIN_USER_ID);
+        mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID);
         // THEN the plugin is the bubble clock face.
         assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS);
     }
@@ -146,7 +148,7 @@
         // GIVEN that settings is set to the bubble clock face
         when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK);
         // WHEN settings change event is fired
-        mContentObserver.onChange(false, SETTINGS_URI, MAIN_USER_ID);
+        mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID);
         // THEN the plugin is the bubble clock face.
         ArgumentCaptor<ClockPlugin> captor = ArgumentCaptor.forClass(ClockPlugin.class);
         verify(mMockListener1).onClockChanged(captor.capture());
@@ -158,7 +160,7 @@
         // GIVEN that settings is set to the bubble clock face
         when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK);
         // WHEN settings change event is fired
-        mContentObserver.onChange(false, SETTINGS_URI, MAIN_USER_ID);
+        mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID);
         // THEN the listeners receive separate instances of the Bubble clock plugin.
         ArgumentCaptor<ClockPlugin> captor1 = ArgumentCaptor.forClass(ClockPlugin.class);
         ArgumentCaptor<ClockPlugin> captor2 = ArgumentCaptor.forClass(ClockPlugin.class);
@@ -175,7 +177,7 @@
         // custom clock face.
         when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn("bad value");
         // WHEN settings change event is fired
-        mContentObserver.onChange(false, SETTINGS_URI, MAIN_USER_ID);
+        mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID);
         // THEN the result is null.
         assertThat(mClockManager.getCurrentClock()).isNull();
     }
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 78160c4..6e612d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -45,7 +45,11 @@
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.res.Resources;
+import android.hardware.display.AmbientDisplayConfiguration;
 import android.hardware.face.FaceManager;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.service.dreams.IDreamManager;
 import android.service.notification.ZenModeConfig;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -61,14 +65,12 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoveInterceptor;
 import com.android.systemui.statusbar.SuperStatusBarViewFactory;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -227,15 +229,17 @@
         mZenModeConfig.suppressedVisualEffects = 0;
         when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
 
-        TestableNotificationInterruptionStateProvider interruptionStateProvider =
-                new TestableNotificationInterruptionStateProvider(mContext,
+        TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
+                new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
+                        mock(PowerManager.class),
+                        mock(IDreamManager.class),
+                        mock(AmbientDisplayConfiguration.class),
                         mock(NotificationFilter.class),
                         mock(StatusBarStateController.class),
-                        mock(BatteryController.class));
-        interruptionStateProvider.setUpWithPresenter(
-                mock(NotificationPresenter.class),
-                mock(HeadsUpManager.class),
-                mock(NotificationInterruptionStateProvider.HeadsUpSuppressor.class));
+                        mock(BatteryController.class),
+                        mock(HeadsUpManager.class),
+                        mock(Handler.class)
+                );
         mBubbleData = new BubbleData(mContext);
         when(mFeatureFlagsOldPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
         mBubbleController = new TestableBubbleController(
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 5ef4cd2..6244644 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -41,7 +41,11 @@
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.res.Resources;
+import android.hardware.display.AmbientDisplayConfiguration;
 import android.hardware.face.FaceManager;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.service.dreams.IDreamManager;
 import android.service.notification.ZenModeConfig;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -57,12 +61,10 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.SuperStatusBarViewFactory;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
@@ -212,15 +214,17 @@
         mZenModeConfig.suppressedVisualEffects = 0;
         when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
 
-        TestableNotificationInterruptionStateProvider interruptionStateProvider =
-                new TestableNotificationInterruptionStateProvider(mContext,
+        TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
+                new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
+                        mock(PowerManager.class),
+                        mock(IDreamManager.class),
+                        mock(AmbientDisplayConfiguration.class),
                         mock(NotificationFilter.class),
                         mock(StatusBarStateController.class),
-                        mock(BatteryController.class));
-        interruptionStateProvider.setUpWithPresenter(
-                mock(NotificationPresenter.class),
-                mock(HeadsUpManager.class),
-                mock(NotificationInterruptionStateProvider.HeadsUpSuppressor.class));
+                        mock(BatteryController.class),
+                        mock(HeadsUpManager.class),
+                        mock(Handler.class)
+                );
         mBubbleData = new BubbleData(mContext);
         when(mFeatureFlagsNewPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(true);
         mBubbleController = new TestableBubbleController(
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 de1fb41..d3d90c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
@@ -23,8 +23,8 @@
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.ShadeController;
@@ -44,7 +44,7 @@
             ShadeController shadeController,
             BubbleData data,
             ConfigurationController configurationController,
-            NotificationInterruptionStateProvider interruptionStateProvider,
+            NotificationInterruptStateProvider interruptionStateProvider,
             ZenModeController zenModeController,
             NotificationLockscreenUserManager lockscreenUserManager,
             NotificationGroupManager groupManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptStateProviderImpl.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptStateProviderImpl.java
new file mode 100644
index 0000000..17dc76b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptStateProviderImpl.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bubbles;
+
+import android.content.ContentResolver;
+import android.hardware.display.AmbientDisplayConfiguration;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.service.dreams.IDreamManager;
+
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.NotificationFilter;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+public class TestableNotificationInterruptStateProviderImpl
+        extends NotificationInterruptStateProviderImpl {
+
+    TestableNotificationInterruptStateProviderImpl(
+            ContentResolver contentResolver,
+            PowerManager powerManager,
+            IDreamManager dreamManager,
+            AmbientDisplayConfiguration ambientDisplayConfiguration,
+            NotificationFilter filter,
+            StatusBarStateController statusBarStateController,
+            BatteryController batteryController,
+            HeadsUpManager headsUpManager,
+            Handler mainHandler) {
+        super(contentResolver,
+                powerManager,
+                dreamManager,
+                ambientDisplayConfiguration,
+                filter,
+                batteryController,
+                statusBarStateController,
+                headsUpManager,
+                mainHandler);
+        mUseHeadsUp = true;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptionStateProvider.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptionStateProvider.java
deleted file mode 100644
index 5d192b2..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptionStateProvider.java
+++ /dev/null
@@ -1,35 +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.systemui.bubbles;
-
-import android.content.Context;
-
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
-import com.android.systemui.statusbar.policy.BatteryController;
-
-public class TestableNotificationInterruptionStateProvider
-        extends NotificationInterruptionStateProvider {
-
-    TestableNotificationInterruptionStateProvider(Context context,
-            NotificationFilter filter, StatusBarStateController controller,
-            BatteryController batteryController) {
-        super(context, filter, controller, batteryController);
-        mUseHeadsUp = true;
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
index eceb1dd..c25d4e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
@@ -22,6 +22,8 @@
 import android.os.UserHandle
 import android.service.controls.Control
 import android.service.controls.DeviceTypes
+import android.service.controls.IControlsSubscriber
+import android.service.controls.IControlsSubscription
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -34,6 +36,8 @@
 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.Mockito
 import org.mockito.Mockito.`when`
@@ -48,6 +52,7 @@
 class ControlsBindingControllerImplTest : SysuiTestCase() {
 
     companion object {
+        fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
         fun <T> any(): T = Mockito.any<T>()
         private val TEST_COMPONENT_NAME_1 = ComponentName("TEST_PKG", "TEST_CLS_1")
         private val TEST_COMPONENT_NAME_2 = ComponentName("TEST_PKG", "TEST_CLS_2")
@@ -57,6 +62,15 @@
     @Mock
     private lateinit var mockControlsController: ControlsController
 
+    @Captor
+    private lateinit var subscriberCaptor: ArgumentCaptor<IControlsSubscriber.Stub>
+
+    @Captor
+    private lateinit var loadSubscriberCaptor: ArgumentCaptor<IControlsSubscriber.Stub>
+
+    @Captor
+    private lateinit var listStringCaptor: ArgumentCaptor<List<String>>
+
     private val user = UserHandle.of(mContext.userId)
     private val otherUser = UserHandle.of(user.identifier + 1)
 
@@ -97,6 +111,102 @@
     }
 
     @Test
+    fun testBindAndLoad_cancel() {
+        val callback = object : ControlsBindingController.LoadCallback {
+            override fun error(message: String) {}
+
+            override fun accept(t: List<Control>) {}
+        }
+        val subscription = mock(IControlsSubscription::class.java)
+
+        val canceller = controller.bindAndLoad(TEST_COMPONENT_NAME_1, callback)
+
+        verify(providers[0]).maybeBindAndLoad(capture(loadSubscriberCaptor))
+        loadSubscriberCaptor.value.onSubscribe(Binder(), subscription)
+
+        canceller.run()
+        verify(subscription).cancel()
+    }
+
+    @Test
+    fun testBindAndLoad_noCancelAfterOnComplete() {
+        val callback = object : ControlsBindingController.LoadCallback {
+            override fun error(message: String) {}
+
+            override fun accept(t: List<Control>) {}
+        }
+        val subscription = mock(IControlsSubscription::class.java)
+
+        val canceller = controller.bindAndLoad(TEST_COMPONENT_NAME_1, callback)
+
+        verify(providers[0]).maybeBindAndLoad(capture(loadSubscriberCaptor))
+        val b = Binder()
+        loadSubscriberCaptor.value.onSubscribe(b, subscription)
+
+        loadSubscriberCaptor.value.onComplete(b)
+        canceller.run()
+        verify(subscription, never()).cancel()
+    }
+
+    @Test
+    fun testLoad_onCompleteRemovesTimeout() {
+        val callback = object : ControlsBindingController.LoadCallback {
+            override fun error(message: String) {}
+
+            override fun accept(t: List<Control>) {}
+        }
+        val subscription = mock(IControlsSubscription::class.java)
+
+        val canceller = controller.bindAndLoad(TEST_COMPONENT_NAME_1, callback)
+
+        verify(providers[0]).maybeBindAndLoad(capture(subscriberCaptor))
+        val b = Binder()
+        subscriberCaptor.value.onSubscribe(b, subscription)
+
+        subscriberCaptor.value.onComplete(b)
+        verify(providers[0]).cancelLoadTimeout()
+    }
+
+    @Test
+    fun testLoad_onErrorRemovesTimeout() {
+        val callback = object : ControlsBindingController.LoadCallback {
+            override fun error(message: String) {}
+
+            override fun accept(t: List<Control>) {}
+        }
+        val subscription = mock(IControlsSubscription::class.java)
+
+        val canceller = controller.bindAndLoad(TEST_COMPONENT_NAME_1, callback)
+
+        verify(providers[0]).maybeBindAndLoad(capture(subscriberCaptor))
+        val b = Binder()
+        subscriberCaptor.value.onSubscribe(b, subscription)
+
+        subscriberCaptor.value.onError(b, "")
+        verify(providers[0]).cancelLoadTimeout()
+    }
+
+    @Test
+    fun testBindAndLoad_noCancelAfterOnError() {
+        val callback = object : ControlsBindingController.LoadCallback {
+            override fun error(message: String) {}
+
+            override fun accept(t: List<Control>) {}
+        }
+        val subscription = mock(IControlsSubscription::class.java)
+
+        val canceller = controller.bindAndLoad(TEST_COMPONENT_NAME_1, callback)
+
+        verify(providers[0]).maybeBindAndLoad(capture(loadSubscriberCaptor))
+        val b = Binder()
+        loadSubscriberCaptor.value.onSubscribe(b, subscription)
+
+        loadSubscriberCaptor.value.onError(b, "")
+        canceller.run()
+        verify(subscription, never()).cancel()
+    }
+
+    @Test
     fun testBindService() {
         controller.bindService(TEST_COMPONENT_NAME_1)
         executor.runAllReady()
@@ -115,8 +225,13 @@
 
         executor.runAllReady()
 
+        val subs = mock(IControlsSubscription::class.java)
         verify(providers[0]).maybeBindAndSubscribe(
+            capture(listStringCaptor), capture(subscriberCaptor))
+        assertEquals(listStringCaptor.value,
             listOf(controlInfo1.controlId, controlInfo2.controlId))
+
+        subscriberCaptor.value.onSubscribe(providers[0].token, subs)
     }
 
     @Test
@@ -126,7 +241,7 @@
 
         executor.runAllReady()
 
-        verify(providers[0], never()).unsubscribe()
+        verify(providers[0], never()).cancelSubscription(any())
     }
 
     @Test
@@ -137,12 +252,21 @@
             StructureInfo(TEST_COMPONENT_NAME_1, "Home", listOf(controlInfo1, controlInfo2))
 
         controller.subscribe(structure)
-
-        controller.unsubscribe()
-
         executor.runAllReady()
 
-        verify(providers[0]).unsubscribe()
+        val subs = mock(IControlsSubscription::class.java)
+        verify(providers[0]).maybeBindAndSubscribe(
+            capture(listStringCaptor), capture(subscriberCaptor))
+        assertEquals(listStringCaptor.value,
+            listOf(controlInfo1.controlId, controlInfo2.controlId))
+
+        subscriberCaptor.value.onSubscribe(providers[0].token, subs)
+        executor.runAllReady()
+
+        controller.unsubscribe()
+        executor.runAllReady()
+
+        verify(providers[0]).cancelSubscription(subs)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
index c70c56a..f9c9815 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
@@ -346,6 +346,88 @@
     }
 
     @Test
+    fun testCancelLoad() {
+        val canceller = object : Runnable {
+            var ran = false
+            override fun run() {
+                ran = true
+            }
+        }
+        `when`(bindingController.bindAndLoad(any(), any())).thenReturn(canceller)
+
+        var loaded = false
+        controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO)
+        delayableExecutor.runAllReady()
+        controller.loadForComponent(TEST_COMPONENT, Consumer {
+            loaded = true
+        })
+
+        controller.cancelLoad()
+        delayableExecutor.runAllReady()
+
+        assertFalse(loaded)
+        assertTrue(canceller.ran)
+    }
+
+    @Test
+    fun testCancelLoad_noCancelAfterSuccessfulLoad() {
+        val canceller = object : Runnable {
+            var ran = false
+            override fun run() {
+                ran = true
+            }
+        }
+        `when`(bindingController.bindAndLoad(any(), any())).thenReturn(canceller)
+
+        var loaded = false
+        controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO)
+        delayableExecutor.runAllReady()
+        controller.loadForComponent(TEST_COMPONENT, Consumer {
+            loaded = true
+        })
+
+        verify(bindingController).bindAndLoad(eq(TEST_COMPONENT),
+            capture(controlLoadCallbackCaptor))
+
+        controlLoadCallbackCaptor.value.accept(emptyList())
+
+        controller.cancelLoad()
+        delayableExecutor.runAllReady()
+
+        assertTrue(loaded)
+        assertFalse(canceller.ran)
+    }
+
+    @Test
+    fun testCancelLoad_noCancelAfterErrorLoad() {
+        val canceller = object : Runnable {
+            var ran = false
+            override fun run() {
+                ran = true
+            }
+        }
+        `when`(bindingController.bindAndLoad(any(), any())).thenReturn(canceller)
+
+        var loaded = false
+        controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO)
+        delayableExecutor.runAllReady()
+        controller.loadForComponent(TEST_COMPONENT, Consumer {
+            loaded = true
+        })
+
+        verify(bindingController).bindAndLoad(eq(TEST_COMPONENT),
+            capture(controlLoadCallbackCaptor))
+
+        controlLoadCallbackCaptor.value.error("")
+
+        controller.cancelLoad()
+        delayableExecutor.runAllReady()
+
+        assertTrue(loaded)
+        assertFalse(canceller.ran)
+    }
+
+    @Test
     fun testFavoriteInformationModifiedOnLoad() {
         controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO)
         delayableExecutor.runAllReady()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
index fd92ad0..2d3757c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2020 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
+ * Licensed under the Apache License, Version 2.0 (149the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
@@ -43,6 +43,7 @@
 import org.mockito.Captor
 import org.mockito.Mock
 import org.mockito.Mockito.`when`
+import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
@@ -83,7 +84,6 @@
                 context,
                 executor,
                 actionCallbackService,
-                subscriberService,
                 UserHandle.of(0),
                 componentName
         )
@@ -144,9 +144,22 @@
     }
 
     @Test
+    fun testMaybeBindAndLoad_timeoutCancelled() {
+        manager.maybeBindAndLoad(subscriberService)
+        executor.runAllReady()
+
+        manager.cancelLoadTimeout()
+
+        executor.advanceClockToLast()
+        executor.runAllReady()
+
+        verify(subscriberService, never()).onError(any(), anyString())
+    }
+
+    @Test
     fun testMaybeBindAndSubscribe() {
         val list = listOf("TEST_ID")
-        manager.maybeBindAndSubscribe(list)
+        manager.maybeBindAndSubscribe(list, subscriberService)
         executor.runAllReady()
 
         assertTrue(mContext.isBound(componentName))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt
new file mode 100644
index 0000000..ff5c8d4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt
@@ -0,0 +1,146 @@
+/*
+ * 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.controls.controller
+
+import android.content.ComponentName
+import android.os.Binder
+import android.service.controls.Control
+import android.service.controls.IControlsSubscription
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class StatefulControlSubscriberTest : SysuiTestCase() {
+
+    @Mock
+    private lateinit var controller: ControlsController
+
+    @Mock
+    private lateinit var subscription: IControlsSubscription
+
+    @Mock
+    private lateinit var provider: ControlsProviderLifecycleManager
+
+    @Mock
+    private lateinit var control: Control
+
+    private val executor = FakeExecutor(FakeSystemClock())
+    private val token = Binder()
+    private val badToken = Binder()
+
+    private val TEST_COMPONENT = ComponentName("TEST_PKG", "TEST_CLS_1")
+
+    private lateinit var scs: StatefulControlSubscriber
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        `when`(provider.componentName).thenReturn(TEST_COMPONENT)
+        `when`(provider.token).thenReturn(token)
+        scs = StatefulControlSubscriber(controller, provider, executor)
+    }
+
+    @Test
+    fun testOnSubscribe() {
+        scs.onSubscribe(token, subscription)
+
+        executor.runAllReady()
+        verify(provider).startSubscription(subscription)
+    }
+
+    @Test
+    fun testOnSubscribe_badToken() {
+        scs.onSubscribe(badToken, subscription)
+
+        executor.runAllReady()
+        verify(provider, never()).startSubscription(subscription)
+    }
+
+    @Test
+    fun testOnNext() {
+        scs.onSubscribe(token, subscription)
+        scs.onNext(token, control)
+
+        executor.runAllReady()
+        verify(controller).refreshStatus(TEST_COMPONENT, control)
+    }
+
+    @Test
+    fun testOnNext_multiple() {
+        scs.onSubscribe(token, subscription)
+        scs.onNext(token, control)
+        scs.onNext(token, control)
+        scs.onNext(token, control)
+
+        executor.runAllReady()
+        verify(controller, times(3)).refreshStatus(TEST_COMPONENT, control)
+    }
+
+    @Test
+    fun testOnNext_noRefreshBeforeSubscribe() {
+        scs.onNext(token, control)
+
+        executor.runAllReady()
+        verify(controller, never()).refreshStatus(TEST_COMPONENT, control)
+    }
+
+    @Test
+    fun testOnNext_noRefreshAfterCancel() {
+        scs.onSubscribe(token, subscription)
+        executor.runAllReady()
+
+        scs.cancel()
+        scs.onNext(token, control)
+
+        executor.runAllReady()
+        verify(controller, never()).refreshStatus(TEST_COMPONENT, control)
+    }
+
+    @Test
+    fun testOnNext_noRefreshAfterError() {
+        scs.onSubscribe(token, subscription)
+        scs.onError(token, "Error")
+        scs.onNext(token, control)
+
+        executor.runAllReady()
+        verify(controller, never()).refreshStatus(TEST_COMPONENT, control)
+    }
+
+    @Test
+    fun testOnNext_noRefreshAfterComplete() {
+        scs.onSubscribe(token, subscription)
+        scs.onComplete(token)
+        scs.onNext(token, control)
+
+        executor.runAllReady()
+        verify(controller, never()).refreshStatus(TEST_COMPONENT, control)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index 9117ea8..f535351 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -248,31 +248,6 @@
     }
 
     @Test
-    public void pausingAod_softBlanks_withSpuriousSensorDuringPause() throws Exception {
-        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
-        mScreen.transitionTo(INITIALIZED, DOZE_AOD);
-        mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING);
-        mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED);
-
-        reset(mDozeHost);
-        mSensor.sendSensorEvent(1);
-        verify(mDozeHost).setAodDimmingScrim(eq(1f));
-    }
-
-    @Test
-    public void screenOff_softBlanks() throws Exception {
-        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
-        mScreen.transitionTo(INITIALIZED, DOZE_AOD);
-        mScreen.transitionTo(DOZE_AOD, DOZE);
-        verify(mDozeHost).setAodDimmingScrim(eq(1f));
-
-        reset(mDozeHost);
-        mScreen.transitionTo(DOZE, DOZE_AOD);
-        mSensor.sendSensorEvent(2);
-        verify(mDozeHost).setAodDimmingScrim(eq(0f));
-    }
-
-    @Test
     public void pausingAod_unblanksAfterSensor() throws Exception {
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE_AOD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 0098012..73f3ddd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -22,7 +22,9 @@
 import static junit.framework.TestCase.assertFalse;
 
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -137,6 +139,8 @@
                         return new TestTile1(mQSTileHost);
                     } else if ("spec2".equals(spec)) {
                         return new TestTile2(mQSTileHost);
+                    } else if ("na".equals(spec)) {
+                        return new NotAvailableTile(mQSTileHost);
                     } else if (CUSTOM_TILE_SPEC.equals(spec)) {
                         return mCustomTile;
                     } else {
@@ -283,6 +287,12 @@
         assertEquals(1, specs.size());
     }
 
+    @Test
+    public void testNotAvailableTile_specNotNull() {
+        mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "na");
+        verify(mQSLogger, never()).logTileDestroyed(isNull(), anyString());
+    }
+
     private static class TestQSTileHost extends QSTileHost {
         TestQSTileHost(Context context, StatusBarIconController iconController,
                 QSFactoryImpl defaultFactory, Handler mainHandler, Looper bgLooper,
@@ -369,4 +379,16 @@
             super(host);
         }
     }
+
+    private class NotAvailableTile extends TestTile {
+
+        protected NotAvailableTile(QSHost host) {
+            super(host);
+        }
+
+        @Override
+        public boolean isAvailable() {
+            return false;
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
similarity index 63%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index 1693e7f..f9c62e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.interruption;
 
 
 import static android.app.Notification.FLAG_BUBBLE;
@@ -30,15 +30,14 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.Notification;
 import android.app.PendingIntent;
-import android.content.Context;
 import android.content.Intent;
 import android.graphics.drawable.Icon;
 import android.hardware.display.AmbientDisplayConfiguration;
+import android.os.Handler;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.service.dreams.IDreamManager;
@@ -50,7 +49,6 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.policy.BatteryController;
@@ -68,7 +66,7 @@
  */
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
-public class NotificationInterruptionStateProviderTest extends SysuiTestCase {
+public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
 
     @Mock
     PowerManager mPowerManager;
@@ -81,38 +79,36 @@
     @Mock
     StatusBarStateController mStatusBarStateController;
     @Mock
-    NotificationPresenter mPresenter;
-    @Mock
     HeadsUpManager mHeadsUpManager;
     @Mock
-    NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor;
-    @Mock
     BatteryController mBatteryController;
+    @Mock
+    Handler mMockHandler;
 
-    private NotificationInterruptionStateProvider mNotifInterruptionStateProvider;
+    private NotificationInterruptStateProviderImpl mNotifInterruptionStateProvider;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
 
         mNotifInterruptionStateProvider =
-                new TestableNotificationInterruptionStateProvider(mContext,
+                new NotificationInterruptStateProviderImpl(
+                        mContext.getContentResolver(),
                         mPowerManager,
                         mDreamManager,
                         mAmbientDisplayConfiguration,
                         mNotificationFilter,
+                        mBatteryController,
                         mStatusBarStateController,
-                        mBatteryController);
+                        mHeadsUpManager,
+                        mMockHandler);
 
-        mNotifInterruptionStateProvider.setUpWithPresenter(
-                mPresenter,
-                mHeadsUpManager,
-                mHeadsUpSuppressor);
+        mNotifInterruptionStateProvider.mUseHeadsUp = true;
     }
 
     /**
      * Sets up the state such that any requests to
-     * {@link NotificationInterruptionStateProvider#canAlertCommon(NotificationEntry)} will
+     * {@link NotificationInterruptStateProviderImpl#canAlertCommon(NotificationEntry)} will
      * pass as long its provided NotificationEntry fulfills group suppression check.
      */
     private void ensureStateForAlertCommon() {
@@ -121,17 +117,16 @@
 
     /**
      * Sets up the state such that any requests to
-     * {@link NotificationInterruptionStateProvider#canAlertAwakeCommon(NotificationEntry)} will
+     * {@link NotificationInterruptStateProviderImpl#canAlertAwakeCommon(NotificationEntry)} will
      * pass as long its provided NotificationEntry fulfills launch fullscreen check.
      */
     private void ensureStateForAlertAwakeCommon() {
-        when(mPresenter.isDeviceInVrMode()).thenReturn(false);
         when(mHeadsUpManager.isSnoozed(any())).thenReturn(false);
     }
 
     /**
      * Sets up the state such that any requests to
-     * {@link NotificationInterruptionStateProvider#shouldHeadsUp(NotificationEntry)} will
+     * {@link NotificationInterruptStateProviderImpl#shouldHeadsUp(NotificationEntry)} will
      * pass as long its provided NotificationEntry fulfills importance & DND checks.
      */
     private void ensureStateForHeadsUpWhenAwake() throws RemoteException {
@@ -141,12 +136,11 @@
         when(mStatusBarStateController.isDozing()).thenReturn(false);
         when(mDreamManager.isDreaming()).thenReturn(false);
         when(mPowerManager.isScreenOn()).thenReturn(true);
-        when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
     }
 
     /**
      * Sets up the state such that any requests to
-     * {@link NotificationInterruptionStateProvider#shouldHeadsUp(NotificationEntry)} will
+     * {@link NotificationInterruptStateProviderImpl#shouldHeadsUp(NotificationEntry)} will
      * pass as long its provided NotificationEntry fulfills importance & DND checks.
      */
     private void ensureStateForHeadsUpWhenDozing() {
@@ -158,7 +152,7 @@
 
     /**
      * Sets up the state such that any requests to
-     * {@link NotificationInterruptionStateProvider#shouldBubbleUp(NotificationEntry)} will
+     * {@link NotificationInterruptStateProviderImpl#shouldBubbleUp(NotificationEntry)} will
      * pass as long its provided NotificationEntry fulfills importance & bubble checks.
      */
     private void ensureStateForBubbleUp() {
@@ -166,75 +160,53 @@
         ensureStateForAlertAwakeCommon();
     }
 
-    /**
-     * Ensure that the disabled state is set correctly.
-     */
     @Test
-    public void testDisableNotificationAlerts() {
-        // Enabled by default
-        assertThat(mNotifInterruptionStateProvider.areNotificationAlertsDisabled()).isFalse();
-
-        // Disable alerts
-        mNotifInterruptionStateProvider.setDisableNotificationAlerts(true);
-        assertThat(mNotifInterruptionStateProvider.areNotificationAlertsDisabled()).isTrue();
-
-        // Enable alerts
-        mNotifInterruptionStateProvider.setDisableNotificationAlerts(false);
-        assertThat(mNotifInterruptionStateProvider.areNotificationAlertsDisabled()).isFalse();
-    }
-
-    /**
-     * Ensure that the disabled alert state effects whether HUNs are enabled.
-     */
-    @Test
-    public void testHunSettingsChange_enabled_butAlertsDisabled() {
-        // Set up but without a mock change observer
-        mNotifInterruptionStateProvider.setUpWithPresenter(
-                mPresenter,
-                mHeadsUpManager,
-                mHeadsUpSuppressor);
-
-        // HUNs enabled by default
-        assertThat(mNotifInterruptionStateProvider.getUseHeadsUp()).isTrue();
-
-        // Set alerts disabled
-        mNotifInterruptionStateProvider.setDisableNotificationAlerts(true);
-
-        // No more HUNs
-        assertThat(mNotifInterruptionStateProvider.getUseHeadsUp()).isFalse();
-    }
-
-    /**
-     * Alerts can happen.
-     */
-    @Test
-    public void testCanAlertCommon_true() {
-        ensureStateForAlertCommon();
+    public void testDefaultSuppressorDoesNotSuppress() {
+        // GIVEN a suppressor without any overrides
+        final NotificationInterruptSuppressor defaultSuppressor =
+                new NotificationInterruptSuppressor() {
+                    @Override
+                    public String getName() {
+                        return "defaultSuppressor";
+                    }
+                };
 
         NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
-        assertThat(mNotifInterruptionStateProvider.canAlertCommon(entry)).isTrue();
+
+        // THEN this suppressor doesn't suppress anything by default
+        assertThat(defaultSuppressor.suppressAwakeHeadsUp(entry)).isFalse();
+        assertThat(defaultSuppressor.suppressAwakeInterruptions(entry)).isFalse();
+        assertThat(defaultSuppressor.suppressInterruptions(entry)).isFalse();
     }
 
-    /**
-     * Filtered out notifications don't alert.
-     */
     @Test
-    public void testCanAlertCommon_false_filteredOut() {
-        ensureStateForAlertCommon();
-        when(mNotificationFilter.shouldFilterOut(any())).thenReturn(true);
+    public void testShouldHeadsUpAwake() throws RemoteException {
+        ensureStateForHeadsUpWhenAwake();
 
-        NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
-        assertThat(mNotifInterruptionStateProvider.canAlertCommon(entry)).isFalse();
+        NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
+        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
     }
 
-    /**
-     * Grouped notifications have different alerting behaviours, sometimes the alert for a
-     * grouped notification may be suppressed {@link android.app.Notification#GROUP_ALERT_CHILDREN}.
-     */
     @Test
-    public void testCanAlertCommon_false_suppressedForGroups() {
-        ensureStateForAlertCommon();
+    public void testShouldNotHeadsUpAwake_flteredOut() throws RemoteException {
+        // GIVEN state for "heads up when awake" is true
+        ensureStateForHeadsUpWhenAwake();
 
+        // WHEN this entry should be filtered out
+        NotificationEntry entry  = createNotification(IMPORTANCE_DEFAULT);
+        when(mNotificationFilter.shouldFilterOut(entry)).thenReturn(true);
+
+        // THEN we shouldn't heads up this entry
+        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
+    }
+
+    @Test
+    public void testShouldNotHeadsUp_suppressedForGroups() throws RemoteException {
+        // GIVEN state for "heads up when awake" is true
+        ensureStateForHeadsUpWhenAwake();
+
+        // WHEN the alert for a grouped notification is suppressed
+        // see {@link android.app.Notification#GROUP_ALERT_CHILDREN}
         NotificationEntry entry = new NotificationEntryBuilder()
                 .setPkg("a")
                 .setOpPkg("a")
@@ -247,40 +219,40 @@
                 .setImportance(IMPORTANCE_DEFAULT)
                 .build();
 
-        assertThat(mNotifInterruptionStateProvider.canAlertCommon(entry)).isFalse();
+        // THEN this entry shouldn't HUN
+        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
     }
 
-    /**
-     * HUNs while dozing can happen.
-     */
     @Test
-    public void testShouldHeadsUpWhenDozing_true() {
+    public void testShouldHeadsUpWhenDozing() {
         ensureStateForHeadsUpWhenDozing();
 
         NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
         assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
     }
 
-    /**
-     * Ambient display can show HUNs for new notifications, this may be disabled.
-     */
     @Test
-    public void testShouldHeadsUpWhenDozing_false_pulseDisabled() {
+    public void testShouldNotHeadsUpWhenDozing_pulseDisabled() {
+        // GIVEN state for "heads up when dozing" is true
         ensureStateForHeadsUpWhenDozing();
+
+        // WHEN pulsing (HUNs when dozing) is disabled
         when(mAmbientDisplayConfiguration.pulseOnNotificationEnabled(anyInt())).thenReturn(false);
 
+        // THEN this entry shouldn't HUN
         NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
         assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
     }
 
-    /**
-     * If the device is not in ambient display or sleeping then we don't HUN.
-     */
     @Test
-    public void testShouldHeadsUpWhenDozing_false_notDozing() {
+    public void testShouldNotHeadsUpWhenDozing_notDozing() {
+        // GIVEN state for "heads up when dozing" is true
         ensureStateForHeadsUpWhenDozing();
+
+        // WHEN we're not dozing (in ambient display or sleeping)
         when(mStatusBarStateController.isDozing()).thenReturn(false);
 
+        // THEN this entry shouldn't HUN
         NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
         assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
     }
@@ -290,7 +262,7 @@
      * {@link android.app.NotificationManager.Policy#SUPPRESSED_EFFECT_AMBIENT}.
      */
     @Test
-    public void testShouldHeadsUpWhenDozing_false_suppressingAmbient() {
+    public void testShouldNotHeadsUpWhenDozing_suppressingAmbient() {
         ensureStateForHeadsUpWhenDozing();
 
         NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
@@ -301,23 +273,18 @@
         assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
     }
 
-    /**
-     * Notifications that are < {@link android.app.NotificationManager#IMPORTANCE_DEFAULT} don't
-     * get to pulse.
-     */
     @Test
-    public void testShouldHeadsUpWhenDozing_false_lessImportant() {
+    public void testShouldNotHeadsUpWhenDozing_lessImportant() {
         ensureStateForHeadsUpWhenDozing();
 
+        // Notifications that are < {@link android.app.NotificationManager#IMPORTANCE_DEFAULT} don't
+        // get to pulse
         NotificationEntry entry = createNotification(IMPORTANCE_LOW);
         assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
     }
 
-    /**
-     * Heads up can happen.
-     */
     @Test
-    public void testShouldHeadsUp_true() throws RemoteException {
+    public void testShouldHeadsUp() throws RemoteException {
         ensureStateForHeadsUpWhenAwake();
 
         NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
@@ -325,38 +292,11 @@
     }
 
     /**
-     * Heads up notifications can be disabled in general.
-     */
-    @Test
-    public void testShouldHeadsUp_false_noHunsAllowed() throws RemoteException {
-        ensureStateForHeadsUpWhenAwake();
-
-        // Set alerts disabled, this should cause heads up to be false
-        mNotifInterruptionStateProvider.setDisableNotificationAlerts(true);
-        assertThat(mNotifInterruptionStateProvider.getUseHeadsUp()).isFalse();
-
-        NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
-        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
-    }
-
-    /**
-     * If the device is dozing, we don't show as heads up.
-     */
-    @Test
-    public void testShouldHeadsUp_false_dozing() throws RemoteException {
-        ensureStateForHeadsUpWhenAwake();
-        when(mStatusBarStateController.isDozing()).thenReturn(true);
-
-        NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
-        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
-    }
-
-    /**
      * If the notification is a bubble, and the user is not on AOD / lockscreen, then
      * the bubble is shown rather than the heads up.
      */
     @Test
-    public void testShouldHeadsUp_false_bubble() throws RemoteException {
+    public void testShouldNotHeadsUp_bubble() throws RemoteException {
         ensureStateForHeadsUpWhenAwake();
 
         // Bubble bit only applies to interruption when we're in the shade
@@ -369,7 +309,7 @@
      * If we're not allowed to alert in general, we shouldn't be shown as heads up.
      */
     @Test
-    public void testShouldHeadsUp_false_alertCommonFalse() throws RemoteException {
+    public void testShouldNotHeadsUp_filtered() throws RemoteException {
         ensureStateForHeadsUpWhenAwake();
         // Make canAlertCommon false by saying it's filtered out
         when(mNotificationFilter.shouldFilterOut(any())).thenReturn(true);
@@ -383,7 +323,7 @@
      * {@link android.app.NotificationManager.Policy#SUPPRESSED_EFFECT_PEEK}.
      */
     @Test
-    public void testShouldHeadsUp_false_suppressPeek() throws RemoteException {
+    public void testShouldNotHeadsUp_suppressPeek() throws RemoteException {
         ensureStateForHeadsUpWhenAwake();
 
         NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
@@ -399,7 +339,7 @@
      * to show as a heads up.
      */
     @Test
-    public void testShouldHeadsUp_false_lessImportant() throws RemoteException {
+    public void testShouldNotHeadsUp_lessImportant() throws RemoteException {
         ensureStateForHeadsUpWhenAwake();
 
         NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
@@ -410,7 +350,7 @@
      * If the device is not in use then we shouldn't be shown as heads up.
      */
     @Test
-    public void testShouldHeadsUp_false_deviceNotInUse() throws RemoteException {
+    public void testShouldNotHeadsUp_deviceNotInUse() throws RemoteException {
         ensureStateForHeadsUpWhenAwake();
         NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
 
@@ -424,61 +364,58 @@
         assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
     }
 
-    /**
-     * If something wants to suppress this heads up, then it shouldn't be shown as a heads up.
-     */
     @Test
-    public void testShouldHeadsUp_false_suppressed() throws RemoteException {
+    public void testShouldNotHeadsUp_headsUpSuppressed() throws RemoteException {
         ensureStateForHeadsUpWhenAwake();
-        when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(false);
+
+        // If a suppressor is suppressing heads up, then it shouldn't be shown as a heads up.
+        mNotifInterruptionStateProvider.addSuppressor(mSuppressAwakeHeadsUp);
 
         NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
         assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
-        verify(mHeadsUpSuppressor).canHeadsUp(any(), any());
     }
 
-    /**
-     * On screen alerts don't happen when the device is in VR Mode.
-     */
     @Test
-    public void testCanAlertAwakeCommon__false_vrMode() {
-        ensureStateForAlertAwakeCommon();
-        when(mPresenter.isDeviceInVrMode()).thenReturn(true);
+    public void testShouldNotHeadsUpAwake_awakeInterruptsSuppressed() throws RemoteException {
+        ensureStateForHeadsUpWhenAwake();
 
-        NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
-        assertThat(mNotifInterruptionStateProvider.canAlertAwakeCommon(entry)).isFalse();
+        // If a suppressor is suppressing heads up, then it shouldn't be shown as a heads up.
+        mNotifInterruptionStateProvider.addSuppressor(mSuppressAwakeInterruptions);
+
+        NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
+        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
     }
 
     /**
      * On screen alerts don't happen when the notification is snoozed.
      */
     @Test
-    public void testCanAlertAwakeCommon_false_snoozedPackage() {
-        ensureStateForAlertAwakeCommon();
-        when(mHeadsUpManager.isSnoozed(any())).thenReturn(true);
-
+    public void testShouldNotHeadsUp_snoozedPackage() {
         NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
-        assertThat(mNotifInterruptionStateProvider.canAlertAwakeCommon(entry)).isFalse();
+        ensureStateForAlertAwakeCommon();
+
+        when(mHeadsUpManager.isSnoozed(entry.getSbn().getPackageName())).thenReturn(true);
+
+        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
     }
 
-    /**
-     * On screen alerts don't happen when that package has just launched fullscreen.
-     */
+
     @Test
-    public void testCanAlertAwakeCommon_false_justLaunchedFullscreen() {
+    public void testShouldNotHeadsUp_justLaunchedFullscreen() {
         ensureStateForAlertAwakeCommon();
 
+        // On screen alerts don't happen when that package has just launched fullscreen.
         NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
         entry.notifyFullScreenIntentLaunched();
 
-        assertThat(mNotifInterruptionStateProvider.canAlertAwakeCommon(entry)).isFalse();
+        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
     }
 
     /**
      * Bubbles can happen.
      */
     @Test
-    public void testShouldBubbleUp_true() {
+    public void testShouldBubbleUp() {
         ensureStateForBubbleUp();
         assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(createBubble())).isTrue();
     }
@@ -487,7 +424,7 @@
      * If the notification doesn't have permission to bubble, it shouldn't bubble.
      */
     @Test
-    public void shouldBubbleUp_false_notAllowedToBubble() {
+    public void shouldNotBubbleUp_notAllowedToBubble() {
         ensureStateForBubbleUp();
 
         NotificationEntry entry = createBubble();
@@ -502,7 +439,7 @@
      * If the notification isn't a bubble, it should definitely not show as a bubble.
      */
     @Test
-    public void shouldBubbleUp_false_notABubble() {
+    public void shouldNotBubbleUp_notABubble() {
         ensureStateForBubbleUp();
 
         NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
@@ -517,7 +454,7 @@
      * If the notification doesn't have bubble metadata, it shouldn't bubble.
      */
     @Test
-    public void shouldBubbleUp_false_invalidMetadata() {
+    public void shouldNotBubbleUp_invalidMetadata() {
         ensureStateForBubbleUp();
 
         NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
@@ -529,24 +466,18 @@
         assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(entry)).isFalse();
     }
 
-    /**
-     * If the notification can't heads up in general, it shouldn't bubble.
-     */
     @Test
-    public void shouldBubbleUp_false_alertAwakeCommonFalse() {
+    public void shouldNotBubbleUp_suppressedInterruptions() {
         ensureStateForBubbleUp();
 
-        // Make alert common return false by pretending we're in VR mode
-        when(mPresenter.isDeviceInVrMode()).thenReturn(true);
+        // If the notification can't heads up in general, it shouldn't bubble.
+        mNotifInterruptionStateProvider.addSuppressor(mSuppressInterruptions);
 
         assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(createBubble())).isFalse();
     }
 
-    /**
-     * If the notification can't heads up in general, it shouldn't bubble.
-     */
     @Test
-    public void shouldBubbleUp_false_alertCommonFalse() {
+    public void shouldNotBubbleUp_filteredOut() {
         ensureStateForBubbleUp();
 
         // Make canAlertCommon false by saying it's filtered out
@@ -592,20 +523,45 @@
                 .build();
     }
 
-    /**
-     * Testable class overriding constructor.
-     */
-    public static class TestableNotificationInterruptionStateProvider extends
-            NotificationInterruptionStateProvider {
-
-        TestableNotificationInterruptionStateProvider(Context context,
-                PowerManager powerManager, IDreamManager dreamManager,
-                AmbientDisplayConfiguration ambientDisplayConfiguration,
-                NotificationFilter notificationFilter,
-                StatusBarStateController statusBarStateController,
-                BatteryController batteryController) {
-            super(context, powerManager, dreamManager, ambientDisplayConfiguration,
-                    notificationFilter, batteryController, statusBarStateController);
+    private final NotificationInterruptSuppressor
+            mSuppressAwakeHeadsUp =
+            new NotificationInterruptSuppressor() {
+        @Override
+        public String getName() {
+            return "suppressAwakeHeadsUp";
         }
-    }
+
+        @Override
+        public boolean suppressAwakeHeadsUp(NotificationEntry entry) {
+            return true;
+        }
+    };
+
+    private final NotificationInterruptSuppressor
+            mSuppressAwakeInterruptions =
+            new NotificationInterruptSuppressor() {
+        @Override
+        public String getName() {
+            return "suppressAwakeInterruptions";
+        }
+
+        @Override
+        public boolean suppressAwakeInterruptions(NotificationEntry entry) {
+            return true;
+        }
+    };
+
+    private final NotificationInterruptSuppressor
+            mSuppressInterruptions =
+            new NotificationInterruptSuppressor() {
+        @Override
+        public String getName() {
+            return "suppressInterruptions";
+        }
+
+        @Override
+        public boolean suppressInterruptions(NotificationEntry entry) {
+            return true;
+        }
+    };
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index 5d0349d..a21a047 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -56,12 +56,12 @@
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger;
 import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
@@ -108,7 +108,7 @@
     @Mock private NotificationEntryListener mEntryListener;
     @Mock private NotificationRowBinderImpl.BindRowCallback mBindCallback;
     @Mock private HeadsUpManager mHeadsUpManager;
-    @Mock private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+    @Mock private NotificationInterruptStateProvider mNotificationInterruptionStateProvider;
     @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
     @Mock private NotificationGutsManager mGutsManager;
     @Mock private NotificationRemoteInputManager mRemoteInputManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java
new file mode 100644
index 0000000..291c039
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.phone;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class KeyguardIndicationTextViewTest extends SysuiTestCase {
+
+    private KeyguardIndicationTextView mKeyguardIndicationTextView;
+
+    @Before
+    public void setup() {
+        mKeyguardIndicationTextView = new KeyguardIndicationTextView(mContext);
+    }
+
+    @Test
+    public void switchIndication_null_hideIndication() {
+        mKeyguardIndicationTextView.switchIndication(null /* text */);
+
+        assertThat(mKeyguardIndicationTextView.getVisibility()).isEqualTo(View.INVISIBLE);
+        assertThat(mKeyguardIndicationTextView.getText()).isEqualTo("");
+    }
+
+    @Test
+    public void switchIndication_emptyText_hideIndication() {
+        mKeyguardIndicationTextView.switchIndication("" /* text */);
+
+        assertThat(mKeyguardIndicationTextView.getVisibility()).isEqualTo(View.INVISIBLE);
+        assertThat(mKeyguardIndicationTextView.getText()).isEqualTo("");
+    }
+
+    @Test
+    public void switchIndication_newText_updateProperly() {
+        mKeyguardIndicationTextView.switchIndication("test_indication" /* text */);
+
+        assertThat(mKeyguardIndicationTextView.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mKeyguardIndicationTextView.getText()).isEqualTo("test_indication");
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
index c5b6969..cc2d1c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
@@ -37,7 +37,7 @@
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationShadeWindowBlurController;
+import com.android.systemui.statusbar.NotificationShadeDepthController;
 import com.android.systemui.statusbar.PulseExpansionHandler;
 import com.android.systemui.statusbar.SuperStatusBarViewFactory;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -81,7 +81,7 @@
     @Mock private DockManager mDockManager;
     @Mock private NotificationPanelViewController mNotificationPanelViewController;
     @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
-    @Mock private NotificationShadeWindowBlurController mNotificationShadeWindowBlurController;
+    @Mock private NotificationShadeDepthController mNotificationShadeDepthController;
     @Mock private SuperStatusBarViewFactory mStatusBarViewFactory;
 
     @Before
@@ -116,7 +116,7 @@
                 new CommandQueue(mContext),
                 mShadeController,
                 mDockManager,
-                mNotificationShadeWindowBlurController,
+                mNotificationShadeDepthController,
                 mView,
                 mNotificationPanelViewController,
                 mStatusBarViewFactory);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 1e4df27..b9c5b7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -67,10 +67,10 @@
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -183,7 +183,7 @@
                 mock(StatusBarRemoteInputCallback.class), mock(NotificationGroupManager.class),
                 mock(NotificationLockscreenUserManager.class),
                 mKeyguardStateController,
-                mock(NotificationInterruptionStateProvider.class), mock(MetricsLogger.class),
+                mock(NotificationInterruptStateProvider.class), mock(MetricsLogger.class),
                 mock(LockPatternUtils.class), mHandler, mHandler, mUiBgExecutor,
                 mActivityIntentHelper, mBubbleController, mShadeController, mFeatureFlags,
                 mNotifPipeline, mNotifCollection)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index b9d2d22..318e9b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -16,8 +16,9 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.Notification;
@@ -35,6 +36,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.logging.testing.FakeMetricsLogger;
+import com.android.systemui.ForegroundServiceNotificationListener;
 import com.android.systemui.InitController;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -48,12 +50,12 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -62,6 +64,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 
 import java.util.ArrayList;
 
@@ -72,6 +75,9 @@
 
 
     private StatusBarNotificationPresenter mStatusBarNotificationPresenter;
+    private NotificationInterruptStateProvider mNotificationInterruptStateProvider =
+            mock(NotificationInterruptStateProvider.class);
+    private NotificationInterruptSuppressor mInterruptSuppressor;
     private CommandQueue mCommandQueue;
     private FakeMetricsLogger mMetricsLogger;
     private ShadeController mShadeController = mock(ShadeController.class);
@@ -95,11 +101,11 @@
         mDependency.injectMockDependency(NotificationViewHierarchyManager.class);
         mDependency.injectMockDependency(NotificationRemoteInputManager.Callback.class);
         mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
-        mDependency.injectMockDependency(NotificationInterruptionStateProvider.class);
         mDependency.injectMockDependency(NotificationMediaManager.class);
         mDependency.injectMockDependency(VisualStabilityManager.class);
         mDependency.injectMockDependency(NotificationGutsManager.class);
         mDependency.injectMockDependency(NotificationShadeWindowController.class);
+        mDependency.injectMockDependency(ForegroundServiceNotificationListener.class);
         NotificationEntryManager entryManager =
                 mDependency.injectMockDependency(NotificationEntryManager.class);
         when(entryManager.getActiveNotificationsForCurrentUser()).thenReturn(new ArrayList<>());
@@ -107,18 +113,25 @@
         NotificationShadeWindowView notificationShadeWindowView =
                 mock(NotificationShadeWindowView.class);
         when(notificationShadeWindowView.getResources()).thenReturn(mContext.getResources());
+
         mStatusBarNotificationPresenter = new StatusBarNotificationPresenter(mContext,
                 mock(NotificationPanelViewController.class), mock(HeadsUpManagerPhone.class),
                 notificationShadeWindowView, mock(NotificationListContainerViewGroup.class),
                 mock(DozeScrimController.class), mock(ScrimController.class),
                 mock(ActivityLaunchAnimator.class), mock(DynamicPrivacyController.class),
-                mock(NotificationAlertingManager.class), mock(KeyguardStateController.class),
+                mock(KeyguardStateController.class),
                 mock(KeyguardIndicationController.class), mStatusBar,
-                mock(ShadeControllerImpl.class), mCommandQueue, mInitController);
+                mock(ShadeControllerImpl.class), mCommandQueue, mInitController,
+                mNotificationInterruptStateProvider);
+        mInitController.executePostInitTasks();
+        ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor =
+                ArgumentCaptor.forClass(NotificationInterruptSuppressor.class);
+        verify(mNotificationInterruptStateProvider).addSuppressor(suppressorCaptor.capture());
+        mInterruptSuppressor = suppressorCaptor.getValue();
     }
 
     @Test
-    public void testHeadsUp_disabledStatusBar() {
+    public void testSuppressHeadsUp_disabledStatusBar() {
         Notification n = new Notification.Builder(getContext(), "a").build();
         NotificationEntry entry = new NotificationEntryBuilder()
                 .setPkg("a")
@@ -130,12 +143,12 @@
                 false /* animate */);
         TestableLooper.get(this).processAllMessages();
 
-        assertFalse("The panel shouldn't allow heads up while disabled",
-                mStatusBarNotificationPresenter.canHeadsUp(entry, entry.getSbn()));
+        assertTrue("The panel should suppress heads up while disabled",
+                mInterruptSuppressor.suppressAwakeHeadsUp(entry));
     }
 
     @Test
-    public void testHeadsUp_disabledNotificationShade() {
+    public void testSuppressHeadsUp_disabledNotificationShade() {
         Notification n = new Notification.Builder(getContext(), "a").build();
         NotificationEntry entry = new NotificationEntryBuilder()
                 .setPkg("a")
@@ -147,8 +160,39 @@
                 false /* animate */);
         TestableLooper.get(this).processAllMessages();
 
-        assertFalse("The panel shouldn't allow heads up while notitifcation shade disabled",
-                mStatusBarNotificationPresenter.canHeadsUp(entry, entry.getSbn()));
+        assertTrue("The panel should suppress interruptions while notification shade "
+                        + "disabled",
+                mInterruptSuppressor.suppressAwakeHeadsUp(entry));
+    }
+
+    @Test
+    public void testSuppressInterruptions_vrMode() {
+        Notification n = new Notification.Builder(getContext(), "a").build();
+        NotificationEntry entry = new NotificationEntryBuilder()
+                .setPkg("a")
+                .setOpPkg("a")
+                .setTag("a")
+                .setNotification(n)
+                .build();
+        mStatusBarNotificationPresenter.mVrMode = true;
+
+        assertTrue("Vr mode should suppress interruptions",
+                mInterruptSuppressor.suppressAwakeInterruptions(entry));
+    }
+
+    @Test
+    public void testSuppressInterruptions_statusBarAlertsDisabled() {
+        Notification n = new Notification.Builder(getContext(), "a").build();
+        NotificationEntry entry = new NotificationEntryBuilder()
+                .setPkg("a")
+                .setOpPkg("a")
+                .setTag("a")
+                .setNotification(n)
+                .build();
+        when(mStatusBar.areNotificationAlertsDisabled()).thenReturn(true);
+
+        assertTrue("StatusBar alerts disabled shouldn't allow interruptions",
+                mInterruptSuppressor.suppressInterruptions(entry));
     }
 
     @Test
@@ -172,4 +216,3 @@
         }
     }
 }
-
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index e407927..679ac22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -42,7 +42,7 @@
 import android.app.StatusBarManager;
 import android.app.trust.TrustManager;
 import android.content.BroadcastReceiver;
-import android.content.Context;
+import android.content.ContentResolver;
 import android.content.IntentFilter;
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.hardware.fingerprint.FingerprintManager;
@@ -109,18 +109,18 @@
 import com.android.systemui.statusbar.StatusBarStateControllerImpl;
 import com.android.systemui.statusbar.SuperStatusBarViewFactory;
 import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.init.NotificationsController;
+import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
+import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerFake;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -130,6 +130,7 @@
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.ExtensionController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
@@ -161,7 +162,7 @@
     private StatusBar mStatusBar;
     private FakeMetricsLogger mMetricsLogger;
     private PowerManager mPowerManager;
-    private TestableNotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+    private TestableNotificationInterruptStateProviderImpl mNotificationInterruptStateProvider;
 
     @Mock private NotificationsController mNotificationsController;
     @Mock private LightBarController mLightBarController;
@@ -179,7 +180,6 @@
     @Mock private DozeScrimController mDozeScrimController;
     @Mock private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
     @Mock private BiometricUnlockController mBiometricUnlockController;
-    @Mock private NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor;
     @Mock private VisualStabilityManager mVisualStabilityManager;
     @Mock private NotificationListener mNotificationListener;
     @Mock private KeyguardViewMediator mKeyguardViewMediator;
@@ -193,9 +193,9 @@
     @Mock private NotificationEntryListener mEntryListener;
     @Mock private NotificationFilter mNotificationFilter;
     @Mock private NotificationAlertingManager mNotificationAlertingManager;
+    @Mock private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
     @Mock private NotificationLogger.ExpansionStateLogger mExpansionStateLogger;
     @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    @Mock private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
     @Mock private NotificationShadeWindowView mNotificationShadeWindowView;
     @Mock private BroadcastDispatcher mBroadcastDispatcher;
     @Mock private AssistManager mAssistManager;
@@ -263,10 +263,12 @@
         mPowerManager = new PowerManager(mContext, powerManagerService, thermalService,
                 Handler.createAsync(Looper.myLooper()));
 
-        mNotificationInterruptionStateProvider =
-                new TestableNotificationInterruptionStateProvider(mContext, mPowerManager,
+        mNotificationInterruptStateProvider =
+                new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
+                        mPowerManager,
                         mDreamManager, mAmbientDisplayConfiguration, mNotificationFilter,
-                        mStatusBarStateController, mBatteryController);
+                        mStatusBarStateController, mBatteryController, mHeadsUpManager,
+                        new Handler(TestableLooper.get(this).getLooper()));
 
         mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
         mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
@@ -299,9 +301,6 @@
             return null;
         }).when(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any());
 
-        mNotificationInterruptionStateProvider.setUpWithPresenter(mNotificationPresenter,
-                mHeadsUpManager, mHeadsUpSuppressor);
-
         when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
 
         WakefulnessLifecycle wakefulnessLifecycle = new WakefulnessLifecycle();
@@ -348,7 +347,7 @@
                 ),
                 mNotificationGutsManager,
                 notificationLogger,
-                mNotificationInterruptionStateProvider,
+                mNotificationInterruptStateProvider,
                 mNotificationViewHierarchyManager,
                 mKeyguardViewMediator,
                 mNotificationAlertingManager,
@@ -562,7 +561,6 @@
         when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
         when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
         when(mDreamManager.isDreaming()).thenReturn(false);
-        when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
 
         Notification n = new Notification.Builder(getContext(), "a")
                 .setGroup("a")
@@ -578,7 +576,7 @@
                 .setImportance(IMPORTANCE_HIGH)
                 .build();
 
-        assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
+        assertTrue(mNotificationInterruptStateProvider.shouldHeadsUp(entry));
     }
 
     @Test
@@ -587,7 +585,6 @@
         when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
         when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
         when(mDreamManager.isDreaming()).thenReturn(false);
-        when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
 
         Notification n = new Notification.Builder(getContext(), "a")
                 .setGroup("a")
@@ -603,7 +600,7 @@
                 .setImportance(IMPORTANCE_HIGH)
                 .build();
 
-        assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
+        assertFalse(mNotificationInterruptStateProvider.shouldHeadsUp(entry));
     }
 
     @Test
@@ -612,7 +609,6 @@
         when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
         when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
         when(mDreamManager.isDreaming()).thenReturn(false);
-        when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
 
         Notification n = new Notification.Builder(getContext(), "a").build();
 
@@ -625,7 +621,7 @@
                 .setSuppressedVisualEffects(SUPPRESSED_EFFECT_PEEK)
                 .build();
 
-        assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
+        assertFalse(mNotificationInterruptStateProvider.shouldHeadsUp(entry));
     }
 
     @Test
@@ -634,7 +630,6 @@
         when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
         when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
         when(mDreamManager.isDreaming()).thenReturn(false);
-        when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
 
         Notification n = new Notification.Builder(getContext(), "a").build();
 
@@ -646,7 +641,7 @@
                 .setImportance(IMPORTANCE_HIGH)
                 .build();
 
-        assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
+        assertTrue(mNotificationInterruptStateProvider.shouldHeadsUp(entry));
     }
 
     @Test
@@ -872,19 +867,21 @@
         verify(mDozeServiceHost).setDozeSuppressed(false);
     }
 
-    public static class TestableNotificationInterruptionStateProvider extends
-            NotificationInterruptionStateProvider {
+    public static class TestableNotificationInterruptStateProviderImpl extends
+            NotificationInterruptStateProviderImpl {
 
-        TestableNotificationInterruptionStateProvider(
-                Context context,
+        TestableNotificationInterruptStateProviderImpl(
+                ContentResolver contentResolver,
                 PowerManager powerManager,
                 IDreamManager dreamManager,
                 AmbientDisplayConfiguration ambientDisplayConfiguration,
                 NotificationFilter filter,
                 StatusBarStateController controller,
-                BatteryController batteryController) {
-            super(context, powerManager, dreamManager, ambientDisplayConfiguration, filter,
-                    batteryController, controller);
+                BatteryController batteryController,
+                HeadsUpManager headsUpManager,
+                Handler mainHandler) {
+            super(contentResolver, powerManager, dreamManager, ambientDisplayConfiguration, filter,
+                    batteryController, controller, headsUpManager, mainHandler);
             mUseHeadsUp = true;
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index a0d551c..cddbb9f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -207,7 +207,7 @@
     protected void setupNetworkController() {
         // For now just pretend to be the data sim, so we can test that too.
         mSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
-        when(mMockTm.isDataConnectionEnabled()).thenReturn(true);
+        when(mMockTm.isDataConnectionAllowed()).thenReturn(true);
         setDefaultSubId(mSubId);
         setSubscriptions(mSubId);
         mMobileSignalController = mNetworkController.mMobileSignalControllers.get(mSubId);
@@ -235,7 +235,7 @@
             subs.add(subscription);
         }
         when(mMockSm.getActiveSubscriptionInfoList()).thenReturn(subs);
-        when(mMockSm.getActiveAndHiddenSubscriptionInfoList()).thenReturn(subs);
+        when(mMockSm.getCompleteActiveSubscriptionInfoList()).thenReturn(subs);
         mNetworkController.doUpdateMobileControllers();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index 3eb0c44..d8b6aac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -119,7 +119,7 @@
     @Test
     public void testNoInternetIcon_withDefaultSub() {
         setupNetworkController();
-        when(mMockTm.isDataConnectionEnabled()).thenReturn(false);
+        when(mMockTm.isDataConnectionAllowed()).thenReturn(false);
         setupDefaultSignal();
         updateDataConnectionState(TelephonyManager.DATA_CONNECTED, 0);
         setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
@@ -133,7 +133,7 @@
     @Test
     public void testDataDisabledIcon_withDefaultSub() {
         setupNetworkController();
-        when(mMockTm.isDataConnectionEnabled()).thenReturn(false);
+        when(mMockTm.isDataConnectionAllowed()).thenReturn(false);
         setupDefaultSignal();
         updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0);
         setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
@@ -147,7 +147,7 @@
     @Test
     public void testNonDefaultSIM_showsFullSignal_connected() {
         setupNetworkController();
-        when(mMockTm.isDataConnectionEnabled()).thenReturn(false);
+        when(mMockTm.isDataConnectionAllowed()).thenReturn(false);
         setupDefaultSignal();
         setDefaultSubId(mSubId + 1);
         updateDataConnectionState(TelephonyManager.DATA_CONNECTED, 0);
@@ -162,7 +162,7 @@
     @Test
     public void testNonDefaultSIM_showsFullSignal_disconnected() {
         setupNetworkController();
-        when(mMockTm.isDataConnectionEnabled()).thenReturn(false);
+        when(mMockTm.isDataConnectionAllowed()).thenReturn(false);
         setupDefaultSignal();
         setDefaultSubId(mSubId + 1);
         updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0);
@@ -177,7 +177,7 @@
     @Test
     public void testDataDisabledIcon_UserNotSetup() {
         setupNetworkController();
-        when(mMockTm.isDataConnectionEnabled()).thenReturn(false);
+        when(mMockTm.isDataConnectionAllowed()).thenReturn(false);
         setupDefaultSignal();
         updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0);
         setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
@@ -192,7 +192,7 @@
     @Test
     public void testAlwaysShowDataRatIcon() {
         setupDefaultSignal();
-        when(mMockTm.isDataConnectionEnabled()).thenReturn(false);
+        when(mMockTm.isDataConnectionAllowed()).thenReturn(false);
         updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED,
                 TelephonyManager.NETWORK_TYPE_GSM);
 
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index 5b73dd5..2fbba68 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -62,26 +62,14 @@
     apex_available: ["com.android.tethering"],
 }
 
-droidstubs {
-    name: "framework-tethering-stubs-sources",
-    defaults: ["framework-module-stubs-defaults-module_libs_api"],
+stubs_defaults {
+    name: "framework-tethering-stubs-defaults",
     srcs: [
         "src/android/net/TetheredClient.java",
         "src/android/net/TetheringManager.java",
         "src/android/net/TetheringConstants.java",
     ],
-    libs: [
-        "tethering-aidl-interfaces-java",
-        "framework-all",
-    ],
-    sdk_version: "core_platform",
-}
-
-java_library {
-    name: "framework-tethering-stubs",
-    srcs: [":framework-tethering-stubs-sources"],
-    libs: ["framework-all"],
-    sdk_version: "core_platform",
+    libs: ["tethering-aidl-interfaces-java"],
 }
 
 filegroup {
@@ -101,3 +89,53 @@
     ],
     path: "src"
 }
+
+droidstubs {
+    name: "framework-tethering-stubs-srcs-publicapi",
+    defaults: [
+        "framework-module-stubs-defaults-publicapi",
+        "framework-tethering-stubs-defaults",
+    ],
+}
+
+droidstubs {
+    name: "framework-tethering-stubs-srcs-systemapi",
+    defaults: [
+        "framework-module-stubs-defaults-systemapi",
+        "framework-tethering-stubs-defaults",
+    ],
+}
+
+droidstubs {
+    name: "framework-tethering-api-module_libs_api",
+    defaults: [
+        "framework-module-api-defaults-module_libs_api",
+        "framework-tethering-stubs-defaults",
+    ],
+}
+
+droidstubs {
+    name: "framework-tethering-stubs-srcs-module_libs_api",
+    defaults: [
+        "framework-module-stubs-defaults-module_libs_api",
+        "framework-tethering-stubs-defaults",
+    ],
+}
+
+java_library {
+    name: "framework-tethering-stubs-publicapi",
+    srcs: [":framework-tethering-stubs-srcs-publicapi"],
+    sdk_version: "current",
+}
+
+java_library {
+    name: "framework-tethering-stubs-systemapi",
+    srcs: [":framework-tethering-stubs-srcs-systemapi"],
+    sdk_version: "system_current",
+}
+
+java_library {
+    name: "framework-tethering-stubs-module_libs_api",
+    srcs: [":framework-tethering-stubs-srcs-module_libs_api"],
+    sdk_version: "module_current",
+}
diff --git a/services/Android.bp b/services/Android.bp
index ef47867..c4be003 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -78,7 +78,7 @@
 
     libs: [
         "android.hidl.manager-V1.0-java",
-        "framework-tethering-stubs",
+        "framework-tethering-stubs-module_libs_api",
     ],
 
     plugins: [
diff --git a/services/api/current.txt b/services/api/current.txt
index 8c90165..9bbb3ef 100644
--- a/services/api/current.txt
+++ b/services/api/current.txt
@@ -3,9 +3,9 @@
 
   public interface RuntimePermissionsPersistence {
     method @NonNull public static com.android.permission.persistence.RuntimePermissionsPersistence createInstance();
-    method public void deleteAsUser(@NonNull android.os.UserHandle);
-    method @Nullable public com.android.permission.persistence.RuntimePermissionsState readAsUser(@NonNull android.os.UserHandle);
-    method public void writeAsUser(@NonNull com.android.permission.persistence.RuntimePermissionsState, @NonNull android.os.UserHandle);
+    method public void deleteForUser(@NonNull android.os.UserHandle);
+    method @Nullable public com.android.permission.persistence.RuntimePermissionsState readForUser(@NonNull android.os.UserHandle);
+    method public void writeForUser(@NonNull com.android.permission.persistence.RuntimePermissionsState, @NonNull android.os.UserHandle);
   }
 
   public final class RuntimePermissionsState {
@@ -17,7 +17,7 @@
     field public static final int NO_VERSION = -1; // 0xffffffff
   }
 
-  public static class RuntimePermissionsState.PermissionState {
+  public static final class RuntimePermissionsState.PermissionState {
     ctor public RuntimePermissionsState.PermissionState(@NonNull String, boolean, int);
     method public int getFlags();
     method @NonNull public String getName();
@@ -30,9 +30,9 @@
 
   public interface RolesPersistence {
     method @NonNull public static com.android.role.persistence.RolesPersistence createInstance();
-    method public void deleteAsUser(@NonNull android.os.UserHandle);
-    method @Nullable public com.android.role.persistence.RolesState readAsUser(@NonNull android.os.UserHandle);
-    method public void writeAsUser(@NonNull com.android.role.persistence.RolesState, @NonNull android.os.UserHandle);
+    method public void deleteForUser(@NonNull android.os.UserHandle);
+    method @Nullable public com.android.role.persistence.RolesState readForUser(@NonNull android.os.UserHandle);
+    method public void writeForUser(@NonNull com.android.role.persistence.RolesState, @NonNull android.os.UserHandle);
   }
 
   public final class RolesState {
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
index 7fe086d..3376f2b 100644
--- a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
+++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
@@ -23,6 +23,7 @@
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.Slog;
@@ -38,11 +39,8 @@
 
 import java.util.Collections;
 import java.util.Optional;
-import java.util.concurrent.CancellationException;
 import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
+import java.util.function.Consumer;
 
 /**
  * Maintains an autofill inline suggestion session that communicates with the IME.
@@ -69,7 +67,7 @@
 final class InlineSuggestionSession {
 
     private static final String TAG = "AfInlineSuggestionSession";
-    private static final int INLINE_REQUEST_TIMEOUT_MS = 1000;
+    private static final int INLINE_REQUEST_TIMEOUT_MS = 200;
 
     @NonNull
     private final InputMethodManagerInternal mInputMethodManagerInternal;
@@ -80,6 +78,8 @@
     private final Object mLock;
     @NonNull
     private final ImeStatusListener mImeStatusListener;
+    @NonNull
+    private final Handler mHandler;
 
     /**
      * To avoid the race condition, one should not access {@code mPendingImeResponse} without
@@ -105,10 +105,11 @@
     private boolean mImeInputViewStarted = false;
 
     InlineSuggestionSession(InputMethodManagerInternal inputMethodManagerInternal,
-            int userId, ComponentName componentName) {
+            int userId, ComponentName componentName, Handler handler) {
         mInputMethodManagerInternal = inputMethodManagerInternal;
         mUserId = userId;
         mComponentName = componentName;
+        mHandler = handler;
         mLock = new Object();
         mImeStatusListener = new ImeStatusListener() {
             @Override
@@ -137,7 +138,8 @@
         };
     }
 
-    public void onCreateInlineSuggestionsRequest(@NonNull AutofillId autofillId) {
+    public void onCreateInlineSuggestionsRequest(@NonNull AutofillId autofillId,
+            @NonNull Consumer<InlineSuggestionsRequest> requestConsumer) {
         if (sDebug) Log.d(TAG, "onCreateInlineSuggestionsRequest called for " + autofillId);
 
         synchronized (mLock) {
@@ -154,26 +156,16 @@
                     mUserId,
                     new InlineSuggestionsRequestInfo(mComponentName, autofillId, new Bundle()),
                     new InlineSuggestionsRequestCallbackImpl(mPendingImeResponse,
-                            mImeStatusListener));
+                            mImeStatusListener, requestConsumer, mHandler, mLock));
         }
     }
 
-    public Optional<InlineSuggestionsRequest> waitAndGetInlineSuggestionsRequest() {
+    public Optional<InlineSuggestionsRequest> getInlineSuggestionsRequest() {
         final CompletableFuture<ImeResponse> pendingImeResponse = getPendingImeResponse();
-        if (pendingImeResponse == null) {
+        if (pendingImeResponse == null || !pendingImeResponse.isDone()) {
             return Optional.empty();
         }
-        try {
-            return Optional.ofNullable(pendingImeResponse.get(INLINE_REQUEST_TIMEOUT_MS,
-                    TimeUnit.MILLISECONDS)).map(ImeResponse::getRequest);
-        } catch (TimeoutException e) {
-            Log.w(TAG, "Exception getting inline suggestions request in time: " + e);
-        } catch (CancellationException e) {
-            Log.w(TAG, "Inline suggestions request cancelled");
-        } catch (InterruptedException | ExecutionException e) {
-            throw new RuntimeException(e);
-        }
-        return Optional.empty();
+        return Optional.ofNullable(pendingImeResponse.getNow(null)).map(ImeResponse::getRequest);
     }
 
     public boolean hideInlineSuggestionsUi(@NonNull AutofillId autofillId) {
@@ -200,8 +192,7 @@
             if (sDebug) Log.d(TAG, "onInlineSuggestionsResponseLocked without IMS request");
             return false;
         }
-        // There is no need to wait on the CompletableFuture since it should have been completed
-        // when {@link #waitAndGetInlineSuggestionsRequest()} was called.
+        // There is no need to wait on the CompletableFuture since it should have been completed.
         ImeResponse imeResponse = completedImsResponse.getNow(null);
         if (imeResponse == null) {
             if (sDebug) Log.d(TAG, "onInlineSuggestionsResponseLocked with pending IMS response");
@@ -249,20 +240,50 @@
     private static final class InlineSuggestionsRequestCallbackImpl
             extends IInlineSuggestionsRequestCallback.Stub {
 
+        private final Object mLock;
+        @GuardedBy("mLock")
         private final CompletableFuture<ImeResponse> mResponse;
+        @GuardedBy("mLock")
+        private final Consumer<InlineSuggestionsRequest> mRequestConsumer;
         private final ImeStatusListener mImeStatusListener;
+        private final Handler mHandler;
+        private final Runnable mTimeoutCallback;
 
         private InlineSuggestionsRequestCallbackImpl(CompletableFuture<ImeResponse> response,
-                ImeStatusListener imeStatusListener) {
+                ImeStatusListener imeStatusListener,
+                Consumer<InlineSuggestionsRequest> requestConsumer,
+                Handler handler, Object lock) {
             mResponse = response;
             mImeStatusListener = imeStatusListener;
+            mRequestConsumer = requestConsumer;
+            mLock = lock;
+
+            mHandler = handler;
+            mTimeoutCallback = () -> {
+                Log.w(TAG, "Timed out waiting for IME callback InlineSuggestionsRequest.");
+                synchronized (mLock) {
+                    completeIfNotLocked(null);
+                }
+            };
+            mHandler.postDelayed(mTimeoutCallback, INLINE_REQUEST_TIMEOUT_MS);
+        }
+
+        private void completeIfNotLocked(@Nullable ImeResponse response) {
+            if (mResponse.isDone()) {
+                return;
+            }
+            mResponse.complete(response);
+            mRequestConsumer.accept(response == null ? null : response.mRequest);
+            mHandler.removeCallbacks(mTimeoutCallback);
         }
 
         @BinderThread
         @Override
         public void onInlineSuggestionsUnsupported() throws RemoteException {
             if (sDebug) Log.d(TAG, "onInlineSuggestionsUnsupported() called.");
-            mResponse.complete(null);
+            synchronized (mLock) {
+                completeIfNotLocked(null);
+            }
         }
 
         @BinderThread
@@ -281,9 +302,13 @@
                 mImeStatusListener.onInputMethodFinishInputView(imeFieldId);
             }
             if (request != null && callback != null) {
-                mResponse.complete(new ImeResponse(request, callback));
+                synchronized (mLock) {
+                    completeIfNotLocked(new ImeResponse(request, callback));
+                }
             } else {
-                mResponse.complete(null);
+                synchronized (mLock) {
+                    completeIfNotLocked(null);
+                }
             }
         }
 
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index e73f9ce..53afa6e 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -57,6 +57,7 @@
 import com.android.internal.os.IResultReceiver;
 import com.android.server.autofill.ui.InlineSuggestionFactory;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.TimeUnit;
@@ -255,8 +256,12 @@
                             mCallbacks.logAugmentedAutofillSelected(sessionId,
                                     dataset.getId());
                             try {
-                                client.autofill(sessionId, dataset.getFieldIds(),
-                                        dataset.getFieldValues());
+                                final ArrayList<AutofillId> fieldIds = dataset.getFieldIds();
+                                final int size = fieldIds.size();
+                                final boolean hideHighlight = size == 1
+                                        && fieldIds.get(0).equals(focusedId);
+                                client.autofill(sessionId, fieldIds, dataset.getFieldValues(),
+                                        hideHighlight);
                             } catch (RemoteException e) {
                                 Slog.w(TAG, "Encounter exception autofilling the values");
                             }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 538082d..5064663 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -18,6 +18,7 @@
 
 import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES;
 import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
+import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE;
 import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
 import static android.view.autofill.AutofillManager.ACTION_RESPONSE_EXPIRED;
 import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
@@ -113,7 +114,9 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
 
 /**
  * A session for a given activity.
@@ -306,7 +309,47 @@
     /**
      * Receiver of assist data from the app's {@link Activity}.
      */
-    private final IAssistDataReceiver mAssistReceiver = new IAssistDataReceiver.Stub() {
+    private final AssistDataReceiverImpl mAssistReceiver = new AssistDataReceiverImpl();
+
+    private final class AssistDataReceiverImpl extends IAssistDataReceiver.Stub {
+
+        @GuardedBy("mLock")
+        private InlineSuggestionsRequest mPendingInlineSuggestionsRequest;
+        @GuardedBy("mLock")
+        private FillRequest mPendingFillRequest;
+        @GuardedBy("mLock")
+        private CountDownLatch mCountDownLatch;
+
+        @Nullable Consumer<InlineSuggestionsRequest> newAutofillRequestLocked(
+                boolean isInlineRequest) {
+            mCountDownLatch = new CountDownLatch(isInlineRequest ? 2 : 1);
+            mPendingFillRequest = null;
+            mPendingInlineSuggestionsRequest = null;
+            return isInlineRequest ? (inlineSuggestionsRequest) -> {
+                synchronized (mLock) {
+                    mPendingInlineSuggestionsRequest = inlineSuggestionsRequest;
+                    mCountDownLatch.countDown();
+                    maybeRequestFillLocked();
+                }
+            } : null;
+        }
+
+        void maybeRequestFillLocked() {
+            if (mCountDownLatch == null || mCountDownLatch.getCount() > 0
+                    || mPendingFillRequest == null) {
+                return;
+            }
+            if (mPendingInlineSuggestionsRequest != null) {
+                mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
+                        mPendingFillRequest.getFillContexts(), mPendingFillRequest.getClientState(),
+                        mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest);
+            }
+            mRemoteFillService.onFillRequest(mPendingFillRequest);
+            mPendingInlineSuggestionsRequest = null;
+            mPendingFillRequest = null;
+            mCountDownLatch = null;
+        }
+
         @Override
         public void onHandleAssistData(Bundle resultData) throws RemoteException {
             if (mRemoteFillService == null) {
@@ -401,17 +444,17 @@
 
                 final ArrayList<FillContext> contexts =
                         mergePreviousSessionLocked(/* forSave= */ false);
-
-                final Optional<InlineSuggestionsRequest> inlineSuggestionsRequest =
-                        mInlineSuggestionSession.waitAndGetInlineSuggestionsRequest();
                 request = new FillRequest(requestId, contexts, mClientState, flags,
-                        inlineSuggestionsRequest.orElse(null));
+                        /*inlineSuggestionsRequest=*/null);
+
+                mPendingFillRequest = request;
+                mCountDownLatch.countDown();
+                maybeRequestFillLocked();
             }
 
             if (mActivityToken != null) {
                 mService.sendActivityAssistDataToContentCapture(mActivityToken, resultData);
             }
-            mRemoteFillService.onFillRequest(request);
         }
 
         @Override
@@ -604,9 +647,15 @@
     private void maybeRequestInlineSuggestionsRequestThenFillLocked(@NonNull ViewState viewState,
             int newState, int flags) {
         if (isInlineSuggestionsEnabledLocked()) {
-            mInlineSuggestionSession.onCreateInlineSuggestionsRequest(mCurrentViewId);
+            Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer =
+                    mAssistReceiver.newAutofillRequestLocked(/*isInlineRequest=*/ true);
+            if (inlineSuggestionsRequestConsumer != null) {
+                mInlineSuggestionSession.onCreateInlineSuggestionsRequest(mCurrentViewId,
+                        inlineSuggestionsRequestConsumer);
+            }
+        } else {
+            mAssistReceiver.newAutofillRequestLocked(/*isInlineRequest=*/ false);
         }
-
         requestNewFillResponseLocked(viewState, newState, flags);
     }
 
@@ -624,7 +673,7 @@
                         + ", flags=" + flags + ")");
             }
             mForAugmentedAutofillOnly = true;
-            triggerAugmentedAutofillLocked();
+            triggerAugmentedAutofillLocked(flags);
             return;
         }
 
@@ -707,7 +756,7 @@
         setClientLocked(client);
 
         mInlineSuggestionSession = new InlineSuggestionSession(inputMethodManagerInternal, userId,
-                componentName);
+                componentName, handler);
 
         mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED)
                 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags));
@@ -834,7 +883,7 @@
             }
 
             // Although "standard" autofill is disabled, it might still trigger augmented autofill
-            if (triggerAugmentedAutofillLocked() != null) {
+            if (triggerAugmentedAutofillLocked(requestFlags) != null) {
                 mForAugmentedAutofillOnly = true;
                 if (sDebug) {
                     Slog.d(TAG, "Service disabled autofill for " + mComponentName
@@ -2465,7 +2514,7 @@
                         // triggered augmented autofill
                         if (!isSameViewEntered) {
                             if (sDebug) Slog.d(TAG, "trigger augmented autofill.");
-                            triggerAugmentedAutofillLocked();
+                            triggerAugmentedAutofillLocked(flags);
                         } else {
                             if (sDebug) Slog.d(TAG, "skip augmented autofill for same view.");
                         }
@@ -2662,7 +2711,7 @@
     private boolean requestShowInlineSuggestionsLocked(@NonNull FillResponse response,
             @Nullable String filterText) {
         final Optional<InlineSuggestionsRequest> inlineSuggestionsRequest =
-                mInlineSuggestionSession.waitAndGetInlineSuggestionsRequest();
+                mInlineSuggestionSession.getInlineSuggestionsRequest();
         if (!inlineSuggestionsRequest.isPresent()) {
             Log.w(TAG, "InlineSuggestionsRequest unavailable");
             return false;
@@ -2863,8 +2912,8 @@
 
         // The default autofill service cannot fullfill the request, let's check if the augmented
         // autofill service can.
-        mAugmentedAutofillDestroyer = triggerAugmentedAutofillLocked();
-        if (mAugmentedAutofillDestroyer == null) {
+        mAugmentedAutofillDestroyer = triggerAugmentedAutofillLocked(flags);
+        if (mAugmentedAutofillDestroyer == null && ((flags & FLAG_PASSWORD_INPUT_TYPE) == 0)) {
             if (sVerbose) {
                 Slog.v(TAG, "canceling session " + id + " when service returned null and it cannot "
                         + "be augmented. AutofillableIds: " + autofillableIds);
@@ -2874,8 +2923,14 @@
             removeSelf();
         } else {
             if (sVerbose) {
-                Slog.v(TAG, "keeping session " + id + " when service returned null but "
-                        + "it can be augmented. AutofillableIds: " + autofillableIds);
+                if ((flags & FLAG_PASSWORD_INPUT_TYPE) != 0) {
+                    Slog.v(TAG, "keeping session " + id + " when service returned null and "
+                            + "augmented service is disabled for password fields. "
+                            + "AutofillableIds: " + autofillableIds);
+                } else {
+                    Slog.v(TAG, "keeping session " + id + " when service returned null but "
+                            + "it can be augmented. AutofillableIds: " + autofillableIds);
+                }
             }
             mAugmentedAutofillableIds = autofillableIds;
             try {
@@ -2889,12 +2944,20 @@
     /**
      * Tries to trigger Augmented Autofill when the standard service could not fulfill a request.
      *
+     * <p> The request may not have been sent when this method returns as it may be waiting for
+     * the inline suggestion request asynchronously.
+     *
      * @return callback to destroy the autofill UI, or {@code null} if not supported.
      */
     // TODO(b/123099468): might need to call it in other places, like when the service returns a
     // non-null response but without datasets (for example, just SaveInfo)
     @GuardedBy("mLock")
-    private Runnable triggerAugmentedAutofillLocked() {
+    private Runnable triggerAugmentedAutofillLocked(int flags) {
+        // (TODO: b/141703197) Fix later by passing info to service.
+        if ((flags & FLAG_PASSWORD_INPUT_TYPE) != 0) {
+            return null;
+        }
+
         // Check if Smart Suggestions is supported...
         final @SmartSuggestionMode int supportedModes = mService
                 .getSupportedSmartSuggestionModesLocked();
@@ -2966,6 +3029,21 @@
 
         final AutofillId focusedId = AutofillId.withoutSession(mCurrentViewId);
 
+        final Consumer<InlineSuggestionsRequest> requestAugmentedAutofill =
+                (inlineSuggestionsRequest) -> {
+                    remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName,
+                            focusedId,
+                            currentValue, inlineSuggestionsRequest,
+                            /*inlineSuggestionsCallback=*/
+                            response -> mInlineSuggestionSession.onInlineSuggestionsResponse(
+                                    mCurrentViewId, response),
+                            /*onErrorCallback=*/ () -> {
+                                synchronized (mLock) {
+                                    cancelAugmentedAutofillLocked();
+                                }
+                            }, mService.getRemoteInlineSuggestionRenderServiceLocked());
+                };
+
         // There are 3 cases when augmented autofill should ask IME for a new request:
         // 1. standard autofill provider is None
         // 2. standard autofill provider doesn't support inline (and returns null response)
@@ -2973,21 +3051,12 @@
         // doesn't want autofill
         if (mForAugmentedAutofillOnly || !isInlineSuggestionsEnabledLocked()) {
             if (sDebug) Slog.d(TAG, "Create inline request for augmented autofill");
-            mInlineSuggestionSession.onCreateInlineSuggestionsRequest(mCurrentViewId);
+            mInlineSuggestionSession.onCreateInlineSuggestionsRequest(mCurrentViewId,
+                    /*requestConsumer=*/ requestAugmentedAutofill);
+        } else {
+            requestAugmentedAutofill.accept(
+                    mInlineSuggestionSession.getInlineSuggestionsRequest().orElse(null));
         }
-
-        Optional<InlineSuggestionsRequest> inlineSuggestionsRequest =
-                mInlineSuggestionSession.waitAndGetInlineSuggestionsRequest();
-        remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, focusedId,
-                currentValue, inlineSuggestionsRequest.orElse(null),
-                response -> mInlineSuggestionSession.onInlineSuggestionsResponse(
-                        mCurrentViewId, response),
-                () -> {
-                    synchronized (mLock) {
-                        cancelAugmentedAutofillLocked();
-                    }
-                }, mService.getRemoteInlineSuggestionRenderServiceLocked());
-
         if (mAugmentedAutofillDestroyer == null) {
             mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest();
         }
@@ -3370,6 +3439,8 @@
                 final List<AutofillId> ids = new ArrayList<>(entryCount);
                 final List<AutofillValue> values = new ArrayList<>(entryCount);
                 boolean waitingDatasetAuth = false;
+                boolean hideHighlight = (entryCount == 1
+                        && dataset.getFieldIds().get(0).equals(mCurrentViewId));
                 for (int i = 0; i < entryCount; i++) {
                     if (dataset.getFieldValues().get(i) == null) {
                         continue;
@@ -3393,7 +3464,7 @@
                     }
                     if (sDebug) Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset);
 
-                    mClient.autofill(id, ids, values);
+                    mClient.autofill(id, ids, values, hideHighlight);
                     if (dataset.getId() != null) {
                         if (mSelectedDatasetIds == null) {
                             mSelectedDatasetIds = new ArrayList<>();
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index ee59d89..0ca9dd9 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -329,8 +329,14 @@
             @NonNull Runnable onErrorCallback) {
         return new IInlineSuggestionUiCallback.Stub() {
             @Override
-            public void onAutofill() throws RemoteException {
+            public void onClick() throws RemoteException {
                 onAutofillCallback.run();
+                callback.onClick();
+            }
+
+            @Override
+            public void onLongClick() throws RemoteException {
+                callback.onLongClick();
             }
 
             @Override
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 4cc65900..942d563 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -99,7 +99,7 @@
         "android.hardware.tv.cec-V1.0-java",
         "android.hardware.vibrator-java",
         "app-compat-annotations",
-        "framework-tethering-stubs",
+        "framework-tethering-stubs-module_libs_api",
         "ike-stubs",
     ],
 
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 7840b19..9b04e79 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1672,7 +1672,7 @@
                     | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
                     | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
                     | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
-            intent.putExtra("time-zone", zone.getID());
+            intent.putExtra(Intent.EXTRA_TIMEZONE, zone.getID());
             getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
         }
     }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 7287a44..ecf1f13 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -7803,12 +7803,15 @@
     private void handleNetworkTestedWithExtras(
             @NonNull ConnectivityReportEvent reportEvent, @NonNull PersistableBundle extras) {
         final NetworkAgentInfo nai = reportEvent.mNai;
+        final NetworkCapabilities networkCapabilities =
+                new NetworkCapabilities(nai.networkCapabilities);
+        clearNetworkCapabilitiesUids(networkCapabilities);
         final ConnectivityReport report =
                 new ConnectivityReport(
                         reportEvent.mNai.network,
                         reportEvent.mTimestampMillis,
                         nai.linkProperties,
-                        nai.networkCapabilities,
+                        networkCapabilities,
                         extras);
         final List<IConnectivityDiagnosticsCallback> results =
                 getMatchingPermissionedCallbacks(nai);
@@ -7824,13 +7827,16 @@
     private void handleDataStallSuspected(
             @NonNull NetworkAgentInfo nai, long timestampMillis, int detectionMethod,
             @NonNull PersistableBundle extras) {
+        final NetworkCapabilities networkCapabilities =
+                new NetworkCapabilities(nai.networkCapabilities);
+        clearNetworkCapabilitiesUids(networkCapabilities);
         final DataStallReport report =
                 new DataStallReport(
                         nai.network,
                         timestampMillis,
                         detectionMethod,
                         nai.linkProperties,
-                        nai.networkCapabilities,
+                        networkCapabilities,
                         extras);
         final List<IConnectivityDiagnosticsCallback> results =
                 getMatchingPermissionedCallbacks(nai);
@@ -7856,6 +7862,12 @@
         }
     }
 
+    private void clearNetworkCapabilitiesUids(@NonNull NetworkCapabilities nc) {
+        nc.setUids(null);
+        nc.setAdministratorUids(Collections.EMPTY_LIST);
+        nc.setOwnerUid(Process.INVALID_UID);
+    }
+
     private List<IConnectivityDiagnosticsCallback> getMatchingPermissionedCallbacks(
             @NonNull NetworkAgentInfo nai) {
         final List<IConnectivityDiagnosticsCallback> results = new ArrayList<>();
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index acd4039..d814b9c 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -200,7 +200,7 @@
     // time
     private static final int MAX_PROVIDER_SCHEDULING_JITTER_MS = 100;
 
-    private static final String FEATURE_ID = "LocationService";
+    private static final String ATTRIBUTION_TAG = "LocationService";
 
     private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
 
@@ -246,7 +246,7 @@
     private int mBatterySaverMode;
 
     private LocationManagerService(Context context) {
-        mContext = context.createFeatureContext(FEATURE_ID);
+        mContext = context.createAttributionContext(ATTRIBUTION_TAG);
         mHandler = FgThread.getHandler();
         mLocalService = new LocalService();
 
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 2cfe404..b464422 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -161,9 +161,6 @@
     private final Runnable mSaveToFile = this::saveToFile;
     private final SystemClock mSystemClock;
     private final BootThreshold mBootThreshold;
-    // The set of packages that have been synced with the ExplicitHealthCheckController
-    @GuardedBy("mLock")
-    private Set<String> mRequestedHealthCheckPackages = new ArraySet<>();
     @GuardedBy("mLock")
     private boolean mIsPackagesReady;
     // Flag to control whether explicit health checks are supported or not
@@ -627,22 +624,17 @@
      * @see #syncRequestsAsync
      */
     private void syncRequests() {
-        boolean syncRequired = false;
+        Set<String> packages = null;
         synchronized (mLock) {
             if (mIsPackagesReady) {
-                Set<String> packages = getPackagesPendingHealthChecksLocked();
-                if (!packages.equals(mRequestedHealthCheckPackages)) {
-                    syncRequired = true;
-                    mRequestedHealthCheckPackages = packages;
-                }
+                packages = getPackagesPendingHealthChecksLocked();
             } // else, we will sync requests when packages become ready
         }
 
         // Call outside lock to avoid holding lock when calling into the controller.
-        if (syncRequired) {
-            Slog.i(TAG, "Syncing health check requests for packages: "
-                    + mRequestedHealthCheckPackages);
-            mHealthCheckController.syncRequests(mRequestedHealthCheckPackages);
+        if (packages != null) {
+            Slog.i(TAG, "Syncing health check requests for packages: " + packages);
+            mHealthCheckController.syncRequests(packages);
         }
     }
 
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index 80036bb..808d322 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -99,6 +99,8 @@
     private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue";
     private static final String PROP_VIRTUAL_DEVICE = "ro.hardware.virtual_device";
 
+    private static final String DEVICE_CONFIG_DISABLE_FLAG = "disable_rescue_party";
+
     private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT
             | ApplicationInfo.FLAG_SYSTEM;
 
@@ -114,6 +116,14 @@
             return false;
         }
 
+        // We're disabled if the DeviceConfig disable flag is set to true.
+        // This is in case that an emergency rollback of the feature is needed.
+        if (DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_CONFIGURATION, DEVICE_CONFIG_DISABLE_FLAG, false)) {
+            Slog.v(TAG, "Disabled because of DeviceConfig flag");
+            return true;
+        }
+
         // We're disabled on all engineering devices
         if (Build.IS_ENG) {
             Slog.v(TAG, "Disabled because of eng build");
diff --git a/services/core/java/com/android/server/SensorNotificationService.java b/services/core/java/com/android/server/SensorNotificationService.java
index 9082dca..db3db0c 100644
--- a/services/core/java/com/android/server/SensorNotificationService.java
+++ b/services/core/java/com/android/server/SensorNotificationService.java
@@ -48,7 +48,7 @@
 
     private static final long MILLIS_2010_1_1 = 1262358000000l;
 
-    private static final String FEATURE_ID = "SensorNotificationService";
+    private static final String ATTRIBUTION_TAG = "SensorNotificationService";
 
     private Context mContext;
     private SensorManager mSensorManager;
@@ -59,7 +59,7 @@
     private long mLocalGeomagneticFieldUpdateTime = -LOCATION_MIN_TIME;
 
     public SensorNotificationService(Context context) {
-        super(context.createFeatureContext(FEATURE_ID));
+        super(context.createAttributionContext(ATTRIBUTION_TAG));
         mContext = getContext();
     }
 
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index b43ae36..cfb79aa 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -100,7 +100,7 @@
         @Nullable public final ComponentName component;
         @UserIdInt public final int userId;
 
-        private ServiceInfo(ResolveInfo resolveInfo, int currentUserId) {
+        ServiceInfo(ResolveInfo resolveInfo, int currentUserId) {
             Preconditions.checkArgument(resolveInfo.serviceInfo.getComponentName() != null);
 
             Bundle metadata = resolveInfo.serviceInfo.metaData;
@@ -316,6 +316,7 @@
             }
 
             mContext.unbindService(this);
+            onServiceDisconnected(mServiceInfo.component);
             mServiceInfo = ServiceInfo.NONE;
         }
 
@@ -339,15 +340,13 @@
     @Override
     public final void onServiceConnected(ComponentName component, IBinder binder) {
         Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+        Preconditions.checkState(mBinder == null);
 
         if (D) {
             Log.i(TAG, getLogPrefix() + " connected to " + component.toShortString());
         }
 
         mBinder = binder;
-
-        // we always run the on bind callback even if we know that the binder is dead already so
-        // that there are always balance pairs of bind/unbind callbacks
         if (mOnBind != null) {
             try {
                 mOnBind.run(binder);
@@ -357,19 +356,16 @@
                 Log.e(TAG, getLogPrefix() + " exception running on " + mServiceInfo, e);
             }
         }
-
-        try {
-            // setting the binder to null lets us skip queued transactions
-            binder.linkToDeath(() -> mBinder = null, 0);
-        } catch (RemoteException e) {
-            mBinder = null;
-        }
     }
 
     @Override
     public final void onServiceDisconnected(ComponentName component) {
         Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
 
+        if (mBinder == null) {
+            return;
+        }
+
         if (D) {
             Log.i(TAG, getLogPrefix() + " disconnected from " + component.toShortString());
         }
@@ -391,18 +387,18 @@
         onBestServiceChanged(true);
     }
 
-    private void onUserSwitched(@UserIdInt int userId) {
+    void onUserSwitched(@UserIdInt int userId) {
         mCurrentUserId = userId;
         onBestServiceChanged(false);
     }
 
-    private void onUserUnlocked(@UserIdInt int userId) {
+    void onUserUnlocked(@UserIdInt int userId) {
         if (userId == mCurrentUserId) {
             onBestServiceChanged(false);
         }
     }
 
-    private void onPackageChanged(String packageName) {
+    void onPackageChanged(String packageName) {
         // force a rebind if the changed package was the currently connected package
         String currentPackageName =
                 mServiceInfo.component != null ? mServiceInfo.component.getPackageName() : null;
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 7c833fa..12a1a95 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -75,7 +75,9 @@
 import java.time.LocalDateTime;
 import java.time.LocalTime;
 import java.time.ZoneId;
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -293,7 +295,7 @@
         public void onChange(boolean selfChange, Uri uri) {
             synchronized (mLock) {
                 // setup wizard is done now so we can unblock
-                if (setupWizardCompleteForCurrentUser()) {
+                if (setupWizardCompleteForCurrentUser() && !selfChange) {
                     mSetupWizardComplete = true;
                     getContext().getContentResolver()
                             .unregisterContentObserver(mSetupWizardObserver);
@@ -348,6 +350,9 @@
         IntentFilter batteryFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
         context.registerReceiver(mBatteryReceiver, batteryFilter);
 
+        context.registerReceiver(mSettingsRestored,
+                new IntentFilter(Intent.ACTION_SETTING_RESTORED), null, mHandler);
+
         mLocalPowerManager =
                 LocalServices.getService(PowerManagerInternal.class);
         initPowerSave();
@@ -395,6 +400,22 @@
         mHandler.post(() -> updateSystemProperties());
     }
 
+    private final BroadcastReceiver mSettingsRestored = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            List<String> settings = Arrays.asList(
+                    Secure.UI_NIGHT_MODE, Secure.DARK_THEME_CUSTOM_START_TIME,
+                    Secure.DARK_THEME_CUSTOM_END_TIME);
+            if (settings.contains(intent.getExtras().getCharSequence(Intent.EXTRA_SETTING_NAME))) {
+                synchronized (mLock) {
+                    updateNightModeFromSettingsLocked(context, context.getResources(),
+                            UserHandle.getCallingUserId());
+                    updateConfigurationLocked();
+                }
+            }
+        }
+    };
+
     private void initPowerSave() {
         mPowerSave =
                 mLocalPowerManager.getLowPowerState(ServiceType.NIGHT_MODE)
@@ -1297,9 +1318,9 @@
             if (Sandman.shouldStartDockApp(getContext(), homeIntent)) {
                 try {
                     int result = ActivityTaskManager.getService().startActivityWithConfig(
-                            null, getContext().getBasePackageName(), getContext().getFeatureId(),
-                            homeIntent, null, null, null, 0, 0, mConfiguration, null,
-                            UserHandle.USER_CURRENT);
+                            null, getContext().getBasePackageName(),
+                            getContext().getAttributionTag(), homeIntent, null, null, null, 0, 0,
+                            mConfiguration, null, UserHandle.USER_CURRENT);
                     if (ActivityManager.isStartResultSuccessful(result)) {
                         dockAppStarted = true;
                     } else if (result != ActivityManager.START_INTENT_NOT_RESOLVED) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 59f64ac..8f5fbf7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2931,25 +2931,35 @@
         final PlatformCompat platformCompat = (PlatformCompat)
                 ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE);
         String toggleValue = getNextArgRequired();
-        if (toggleValue.equals("reset-all")) {
-            final String packageName = getNextArgRequired();
-            pw.println("Reset all changes for " + packageName + " to default value.");
-            platformCompat.clearOverrides(packageName);
-            return 0;
-        }
-        long changeId;
-        String changeIdString = getNextArgRequired();
-        try {
-            changeId = Long.parseLong(changeIdString);
-        } catch (NumberFormatException e) {
-            changeId = platformCompat.lookupChangeId(changeIdString);
-        }
-        if (changeId == -1) {
-            pw.println("Unknown or invalid change: '" + changeIdString + "'.");
-            return -1;
+        boolean toggleAll = false;
+        int targetSdkVersion = -1;
+        long changeId = -1;
+
+        if (toggleValue.endsWith("-all")) {
+            toggleValue = toggleValue.substring(0, toggleValue.lastIndexOf("-all"));
+            toggleAll = true;
+            if (!toggleValue.equals("reset")) {
+                try {
+                    targetSdkVersion = Integer.parseInt(getNextArgRequired());
+                } catch (NumberFormatException e) {
+                    pw.println("Invalid targetSdkVersion!");
+                    return -1;
+                }
+            }
+        } else {
+            String changeIdString = getNextArgRequired();
+            try {
+                changeId = Long.parseLong(changeIdString);
+            } catch (NumberFormatException e) {
+                changeId = platformCompat.lookupChangeId(changeIdString);
+            }
+            if (changeId == -1) {
+                pw.println("Unknown or invalid change: '" + changeIdString + "'.");
+                return -1;
+            }
         }
         String packageName = getNextArgRequired();
-        if (!platformCompat.isKnownChangeId(changeId)) {
+        if (!toggleAll && !platformCompat.isKnownChangeId(changeId)) {
             pw.println("Warning! Change " + changeId + " is not known yet. Enabling/disabling it"
                     + " could have no effect.");
         }
@@ -2958,22 +2968,49 @@
         try {
             switch (toggleValue) {
                 case "enable":
-                    enabled.add(changeId);
-                    CompatibilityChangeConfig overrides =
-                            new CompatibilityChangeConfig(
-                                    new Compatibility.ChangeConfig(enabled, disabled));
-                    platformCompat.setOverrides(overrides, packageName);
-                    pw.println("Enabled change " + changeId + " for " + packageName + ".");
+                    if (toggleAll) {
+                        int numChanges = platformCompat.enableTargetSdkChanges(packageName,
+                                                                               targetSdkVersion);
+                        if (numChanges == 0) {
+                            pw.println("No changes were enabled.");
+                            return -1;
+                        }
+                        pw.println("Enabled " + numChanges + " changes gated by targetSdkVersion "
+                                + targetSdkVersion + " for " + packageName + ".");
+                    } else {
+                        enabled.add(changeId);
+                        CompatibilityChangeConfig overrides =
+                                new CompatibilityChangeConfig(
+                                        new Compatibility.ChangeConfig(enabled, disabled));
+                        platformCompat.setOverrides(overrides, packageName);
+                        pw.println("Enabled change " + changeId + " for " + packageName + ".");
+                    }
                     return 0;
                 case "disable":
-                    disabled.add(changeId);
-                    overrides =
-                            new CompatibilityChangeConfig(
-                                    new Compatibility.ChangeConfig(enabled, disabled));
-                    platformCompat.setOverrides(overrides, packageName);
-                    pw.println("Disabled change " + changeId + " for " + packageName + ".");
+                    if (toggleAll) {
+                        int numChanges = platformCompat.disableTargetSdkChanges(packageName,
+                                                                                targetSdkVersion);
+                        if (numChanges == 0) {
+                            pw.println("No changes were disabled.");
+                            return -1;
+                        }
+                        pw.println("Disabled " + numChanges + " changes gated by targetSdkVersion "
+                                + targetSdkVersion + " for " + packageName + ".");
+                    } else {
+                        disabled.add(changeId);
+                        CompatibilityChangeConfig overrides =
+                                new CompatibilityChangeConfig(
+                                        new Compatibility.ChangeConfig(enabled, disabled));
+                        platformCompat.setOverrides(overrides, packageName);
+                        pw.println("Disabled change " + changeId + " for " + packageName + ".");
+                    }
                     return 0;
                 case "reset":
+                    if (toggleAll) {
+                        platformCompat.clearOverrides(packageName);
+                        pw.println("Reset all changes for " + packageName + " to default value.");
+                        return 0;
+                    }
                     if (platformCompat.clearOverride(changeId, packageName)) {
                         pw.println("Reset change " + changeId + " for " + packageName
                                 + " to default value.");
@@ -3304,6 +3341,8 @@
             pw.println("         enable|disable|reset <CHANGE_ID|CHANGE_NAME> <PACKAGE_NAME>");
             pw.println("            Toggles a change either by id or by name for <PACKAGE_NAME>.");
             pw.println("            It kills <PACKAGE_NAME> (to allow the toggle to take effect).");
+            pw.println("         enable-all|disable-all <targetSdkVersion> <PACKAGE_NAME");
+            pw.println("            Toggles all changes that are gated by <targetSdkVersion>.");
             pw.println("         reset-all <PACKAGE_NAME>");
             pw.println("            Removes all existing overrides for all changes for ");
             pw.println("            <PACKAGE_NAME> (back to default behaviour).");
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 3a6065e..86d9028 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -121,6 +121,7 @@
     static final int COMPACT_PROCESS_MSG = 1;
     static final int COMPACT_SYSTEM_MSG = 2;
     static final int SET_FROZEN_PROCESS_MSG = 3;
+    static final int REPORT_UNFREEZE_MSG = 4;
 
     //TODO:change this static definition into a configurable flag.
     static final int FREEZE_TIMEOUT_MS = 500;
@@ -613,30 +614,6 @@
                 FREEZE_TIMEOUT_MS);
     }
 
-    private final class UnfreezeStats {
-        final int mPid;
-        final String mName;
-        final long mFrozenDuration;
-
-        UnfreezeStats(int pid, String name, long frozenDuration) {
-            mPid = pid;
-            mName = name;
-            mFrozenDuration = frozenDuration;
-        }
-
-        public int getPid() {
-            return mPid;
-        }
-
-        public String getName() {
-            return mName;
-        }
-
-        public long getFrozenDuration() {
-            return mFrozenDuration;
-        }
-    }
-
     @GuardedBy("mAm")
     void unfreezeAppLocked(ProcessRecord app) {
         mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app);
@@ -667,12 +644,11 @@
                 Slog.d(TAG_AM, "sync unfroze " + app.pid + " " + app.processName);
             }
 
-            UnfreezeStats stats = new UnfreezeStats(app.pid, app.processName,
-                    app.freezeUnfreezeTime - freezeTime);
-
             mFreezeHandler.sendMessage(
-                    mFreezeHandler.obtainMessage(SET_FROZEN_PROCESS_MSG, REPORT_UNFREEZE, 0,
-                        stats));
+                    mFreezeHandler.obtainMessage(REPORT_UNFREEZE_MSG,
+                        app.pid,
+                        (int) Math.min(app.freezeUnfreezeTime - freezeTime, Integer.MAX_VALUE),
+                        app.processName));
         }
     }
 
@@ -945,14 +921,19 @@
 
         @Override
         public void handleMessage(Message msg) {
-            if (msg.what != SET_FROZEN_PROCESS_MSG) {
-                return;
-            }
+            switch (msg.what) {
+                case SET_FROZEN_PROCESS_MSG:
+                    freezeProcess((ProcessRecord) msg.obj);
+                    break;
+                case REPORT_UNFREEZE_MSG:
+                    int pid = msg.arg1;
+                    int frozenDuration = msg.arg2;
+                    String processName = (String) msg.obj;
 
-            if (msg.arg1 == DO_FREEZE) {
-                freezeProcess((ProcessRecord) msg.obj);
-            } else if (msg.arg1 == REPORT_UNFREEZE) {
-                reportUnfreeze((UnfreezeStats) msg.obj);
+                    reportUnfreeze(pid, frozenDuration, processName);
+                    break;
+                default:
+                    return;
             }
         }
 
@@ -1015,18 +996,18 @@
             }
         }
 
-        private void reportUnfreeze(UnfreezeStats stats) {
+        private void reportUnfreeze(int pid, int frozenDuration, String processName) {
 
-            EventLog.writeEvent(EventLogTags.AM_UNFREEZE, stats.getPid(), stats.getName());
+            EventLog.writeEvent(EventLogTags.AM_UNFREEZE, pid, processName);
 
             // See above for why we're not taking mPhenotypeFlagLock here
             if (mRandom.nextFloat() < mFreezerStatsdSampleRate) {
                 FrameworkStatsLog.write(
                         FrameworkStatsLog.APP_FREEZE_CHANGED,
                         FrameworkStatsLog.APP_FREEZE_CHANGED__ACTION__UNFREEZE_APP,
-                        stats.getPid(),
-                        stats.getName(),
-                        stats.getFrozenDuration());
+                        pid,
+                        processName,
+                        frozenDuration);
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 6b16513..0e9970e 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -61,6 +61,7 @@
 import android.app.IApplicationThread;
 import android.app.IUidObserver;
 import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
 import android.compat.annotation.EnabledAfter;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -70,6 +71,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.ProcessInfo;
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.net.LocalSocket;
@@ -345,6 +347,14 @@
     private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id.
 
     /**
+     * Enable sampled memory bug detection in the app.
+     * @see <a href="https://source.android.com/devices/tech/debug/gwp-asan">GWP-ASan</a>.
+     */
+    @ChangeId
+    @Disabled
+    private static final long GWP_ASAN = 135634846; // This is a bug id.
+
+    /**
      * Apps have no access to the private data directories of any other app, even if the other
      * app has made them world-readable.
      */
@@ -1634,6 +1644,28 @@
         return gidArray;
     }
 
+    private int decideGwpAsanLevel(ProcessRecord app) {
+        // Look at the process attribute first.
+        if (app.processInfo != null && app.processInfo.enableGwpAsan != null) {
+            return app.processInfo.enableGwpAsan ? Zygote.GWP_ASAN_LEVEL_ALWAYS
+                                                 : Zygote.GWP_ASAN_LEVEL_NEVER;
+        }
+        // Then at the applicaton attribute.
+        if (app.info.isGwpAsanEnabled() != null) {
+            return app.info.isGwpAsanEnabled() ? Zygote.GWP_ASAN_LEVEL_ALWAYS
+                                               : Zygote.GWP_ASAN_LEVEL_NEVER;
+        }
+        // If the app does not specify enableGwpAsan, the default behavior is lottery among the
+        // system apps, and disabled for user apps, unless overwritten by the compat feature.
+        if (mPlatformCompat.isChangeEnabled(GWP_ASAN, app.info)) {
+            return Zygote.GWP_ASAN_LEVEL_ALWAYS;
+        }
+        if ((app.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+            return Zygote.GWP_ASAN_LEVEL_LOTTERY;
+        }
+        return Zygote.GWP_ASAN_LEVEL_NEVER;
+    }
+
     /**
      * @return {@code true} if process start is successful, false otherwise.
      */
@@ -1803,6 +1835,8 @@
                 runtimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
             }
 
+            runtimeFlags |= decideGwpAsanLevel(app);
+
             String invokeWith = null;
             if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
                 // Debuggable apps may include a wrapper script with their library directory.
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index c029811..a78caac 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -384,6 +384,9 @@
                     pw.println(processInfo.deniedPermissions.valueAt(i));
                 }
             }
+            if (processInfo.enableGwpAsan != null) {
+                pw.print(prefix); pw.println("  enableGwpAsan=" + processInfo.enableGwpAsan);
+            }
         }
         pw.print(prefix); pw.print("mRequiredAbi="); pw.print(mRequiredAbi);
                 pw.print(" instructionSet="); pw.println(instructionSet);
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 0a8e70c..bac7565 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -80,6 +80,7 @@
     @VisibleForTesting
     static final String[] sDeviceConfigScopes = new String[] {
         DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
+        DeviceConfig.NAMESPACE_CONFIGURATION,
         DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT,
         DeviceConfig.NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS,
         DeviceConfig.NAMESPACE_MEDIA_NATIVE,
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 7774633..310664e 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -19,8 +19,8 @@
 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
-import static android.app.AppOpsManager.CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE;
-import static android.app.AppOpsManager.FILTER_BY_FEATURE_ID;
+import static android.app.AppOpsManager.CALL_BACK_ON_SWITCHED_OP;
+import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG;
 import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
 import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
 import static android.app.AppOpsManager.FILTER_BY_UID;
@@ -40,6 +40,7 @@
 import static android.app.AppOpsManager.OP_PLAY_AUDIO;
 import static android.app.AppOpsManager.OP_RECORD_AUDIO;
 import static android.app.AppOpsManager.OpEventProxyInfo;
+import static android.app.AppOpsManager.RestrictionBypass;
 import static android.app.AppOpsManager.SAMPLING_STRATEGY_RARELY_USED;
 import static android.app.AppOpsManager.SAMPLING_STRATEGY_UNIFORM;
 import static android.app.AppOpsManager.UID_STATE_BACKGROUND;
@@ -55,6 +56,7 @@
 import static android.app.AppOpsManager.extractUidStateFromKey;
 import static android.app.AppOpsManager.makeKey;
 import static android.app.AppOpsManager.modeToName;
+import static android.app.AppOpsManager.opAllowSystemBypassRestriction;
 import static android.app.AppOpsManager.opToName;
 import static android.app.AppOpsManager.opToPublicName;
 import static android.app.AppOpsManager.resolveFirstUnrestrictedUidState;
@@ -73,33 +75,29 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
-import android.app.ActivityThread;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.AppOpsManager.HistoricalOps;
 import android.app.AppOpsManager.Mode;
 import android.app.AppOpsManager.OpEntry;
-import android.app.AppOpsManager.OpFeatureEntry;
+import android.app.AppOpsManager.AttributedOpEntry;
 import android.app.AppOpsManager.OpFlags;
 import android.app.AppOpsManagerInternal;
 import android.app.AppOpsManagerInternal.CheckOpsDelegate;
 import android.app.AsyncNotedAppOp;
 import android.app.RuntimeAppOpAccessMessage;
 import android.app.SyncNotedAppOp;
-import android.compat.Compatibility;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PermissionInfo;
 import android.content.pm.UserInfo;
-import android.content.pm.parsing.component.ParsedFeature;
+import android.content.pm.parsing.component.ParsedAttribution;
 import android.database.ContentObserver;
 import android.hardware.camera2.CameraDevice.CAMERA_AUDIO_RESTRICTION;
 import android.net.Uri;
@@ -373,14 +371,14 @@
         }
 
         OpEventProxyInfo acquire(@IntRange(from = 0) int uid, @Nullable String packageName,
-                @Nullable String featureId) {
+                @Nullable String attributionTag) {
             OpEventProxyInfo recycled = acquire();
             if (recycled != null) {
-                recycled.reinit(uid, packageName, featureId);
+                recycled.reinit(uid, packageName, attributionTag);
                 return recycled;
             }
 
-            return new OpEventProxyInfo(uid, packageName, featureId);
+            return new OpEventProxyInfo(uid, packageName, attributionTag);
         }
     }
 
@@ -666,15 +664,19 @@
     final static class Ops extends SparseArray<Op> {
         final String packageName;
         final UidState uidState;
-        final boolean isPrivileged;
 
-        /** Lazily populated cache of featureIds of this package */
-        final @NonNull ArraySet<String> knownFeatureIds = new ArraySet<>();
+        /**
+         * The restriction properties of the package. If {@code null} it could not have been read
+         * yet and has to be refreshed.
+         */
+        @Nullable RestrictionBypass bypass;
 
-        Ops(String _packageName, UidState _uidState, boolean _isPrivileged) {
+        /** Lazily populated cache of attributionTags of this package */
+        final @NonNull ArraySet<String> knownAttributionTags = new ArraySet<>();
+
+        Ops(String _packageName, UidState _uidState) {
             packageName = _packageName;
             uidState = _uidState;
-            isPrivileged = _isPrivileged;
         }
     }
 
@@ -774,8 +776,8 @@
         }
     }
 
-    private final class FeatureOp {
-        public final @Nullable String featureId;
+    private final class AttributedOp {
+        public final @Nullable String tag;
         public final @NonNull Op parent;
 
         /**
@@ -802,8 +804,8 @@
         @GuardedBy("AppOpsService.this")
         private @Nullable ArrayMap<IBinder, InProgressStartOpEvent> mInProgressEvents;
 
-        FeatureOp(@Nullable String featureId, @NonNull Op parent) {
-            this.featureId = featureId;
+        AttributedOp(@Nullable String tag, @NonNull Op parent) {
+            this.tag = tag;
             this.parent = parent;
         }
 
@@ -812,18 +814,18 @@
          *
          * @param proxyUid The uid of the proxy
          * @param proxyPackageName The package name of the proxy
-         * @param proxyFeatureId the featureId in the proxies package
+         * @param proxyAttributionTag the attributionTag in the proxies package
          * @param uidState UID state of the app noteOp/startOp was called for
          * @param flags OpFlags of the call
          */
         public void accessed(int proxyUid, @Nullable String proxyPackageName,
-                @Nullable String proxyFeatureId, @AppOpsManager.UidState int uidState,
+                @Nullable String proxyAttributionTag, @AppOpsManager.UidState int uidState,
                 @OpFlags int flags) {
             accessed(System.currentTimeMillis(), -1, proxyUid, proxyPackageName,
-                    proxyFeatureId, uidState, flags);
+                    proxyAttributionTag, uidState, flags);
 
             mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName,
-                    featureId, uidState, flags);
+                    tag, uidState, flags);
         }
 
         /**
@@ -833,12 +835,12 @@
          * @param duration The duration of the event
          * @param proxyUid The uid of the proxy
          * @param proxyPackageName The package name of the proxy
-         * @param proxyFeatureId the featureId in the proxies package
+         * @param proxyAttributionTag the attributionTag in the proxies package
          * @param uidState UID state of the app noteOp/startOp was called for
          * @param flags OpFlags of the call
          */
         public void accessed(long noteTime, long duration, int proxyUid,
-                @Nullable String proxyPackageName, @Nullable String proxyFeatureId,
+                @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
                 @AppOpsManager.UidState int uidState, @OpFlags int flags) {
             long key = makeKey(uidState, flags);
 
@@ -849,7 +851,7 @@
             OpEventProxyInfo proxyInfo = null;
             if (proxyUid != Process.INVALID_UID) {
                 proxyInfo = mOpEventProxyInfoPool.acquire(proxyUid, proxyPackageName,
-                        proxyFeatureId);
+                        proxyAttributionTag);
             }
 
             NoteOpEvent existingEvent = mAccessEvents.get(key);
@@ -870,7 +872,7 @@
             rejected(System.currentTimeMillis(), uidState, flags);
 
             mHistoricalRegistry.incrementOpRejected(parent.op, parent.uid, parent.packageName,
-                    featureId, uidState, flags);
+                    tag, uidState, flags);
         }
 
         /**
@@ -936,7 +938,7 @@
 
             // startOp events don't support proxy, hence use flags==SELF
             mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName,
-                    featureId, uidState, OP_FLAG_SELF);
+                    tag, uidState, OP_FLAG_SELF);
         }
 
         /**
@@ -976,7 +978,7 @@
                 mAccessEvents.put(makeKey(event.getUidState(), OP_FLAG_SELF), finishedEvent);
 
                 mHistoricalRegistry.increaseOpAccessDuration(parent.op, parent.uid,
-                        parent.packageName, featureId, event.getUidState(),
+                        parent.packageName, tag, event.getUidState(),
                         AppOpsManager.OP_FLAG_SELF, finishedEvent.getDuration());
 
                 mInProgressStartOpEventPool.release(event);
@@ -984,7 +986,7 @@
                 if (mInProgressEvents.isEmpty()) {
                     mInProgressEvents = null;
 
-                    // TODO moltmann: Also callback for single feature activity changes
+                    // TODO moltmann: Also callback for single attribution tag activity changes
                     if (triggerCallbackIfNeeded && !parent.isRunning()) {
                         scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
                                 parent.packageName, false);
@@ -1075,14 +1077,14 @@
         }
 
         /**
-         * Add all data from the {@code featureToAdd} to this op.
+         * Add all data from the {@code opToAdd} to this op.
          *
          * <p>If there is an event for the same key in both the later event is retained.
          * <p>{@code opToAdd} should not be used after this method is called.
          *
          * @param opToAdd The op to add
          */
-        public void add(@NonNull FeatureOp opToAdd) {
+        public void add(@NonNull AttributedOp opToAdd) {
             if (opToAdd.mInProgressEvents != null) {
                 Slog.w(TAG, "Ignoring " + opToAdd.mInProgressEvents.size() + " running app-ops");
 
@@ -1126,7 +1128,7 @@
             return clone;
         }
 
-        @NonNull OpFeatureEntry createFeatureEntryLocked() {
+        @NonNull AttributedOpEntry createAttributedOpEntryLocked() {
             LongSparseArray<NoteOpEvent> accessEvents = deepClone(mAccessEvents);
 
             // Add in progress events as access events
@@ -1150,7 +1152,7 @@
 
             LongSparseArray<NoteOpEvent> rejectEvents = deepClone(mRejectEvents);
 
-            return new OpFeatureEntry(parent.op, isRunning(), accessEvents, rejectEvents);
+            return new AttributedOpEntry(parent.op, isRunning(), accessEvents, rejectEvents);
         }
     }
 
@@ -1162,8 +1164,8 @@
 
         private @Mode int mode;
 
-        /** featureId -> FeatureOp */
-        final ArrayMap<String, FeatureOp> mFeatures = new ArrayMap<>(1);
+        /** attributionTag -> AttributedOp */
+        final ArrayMap<String, AttributedOp> mAttributions = new ArrayMap<>(1);
 
         Op(UidState uidState, String packageName, int op, int uid) {
             this.op = op;
@@ -1181,58 +1183,59 @@
             return uidState.evalMode(op, mode);
         }
 
-        void removeFeaturesWithNoTime() {
-            for (int i = mFeatures.size() - 1; i >= 0; i--) {
-                if (!mFeatures.valueAt(i).hasAnyTime()) {
-                    mFeatures.removeAt(i);
+        void removeAttributionsWithNoTime() {
+            for (int i = mAttributions.size() - 1; i >= 0; i--) {
+                if (!mAttributions.valueAt(i).hasAnyTime()) {
+                    mAttributions.removeAt(i);
                 }
             }
         }
 
-        private @NonNull FeatureOp getOrCreateFeature(@NonNull Op parent,
-                @Nullable String featureId) {
-            FeatureOp featureOp;
+        private @NonNull AttributedOp getOrCreateAttribution(@NonNull Op parent,
+                @Nullable String attributionTag) {
+            AttributedOp attributedOp;
 
-            featureOp = mFeatures.get(featureId);
-            if (featureOp == null) {
-                featureOp = new FeatureOp(featureId, parent);
-                mFeatures.put(featureId, featureOp);
+            attributedOp = mAttributions.get(attributionTag);
+            if (attributedOp == null) {
+                attributedOp = new AttributedOp(attributionTag, parent);
+                mAttributions.put(attributionTag, attributedOp);
             }
 
-            return featureOp;
+            return attributedOp;
         }
 
         @NonNull OpEntry createEntryLocked() {
-            final int numFeatures = mFeatures.size();
+            final int numAttributions = mAttributions.size();
 
-            final ArrayMap<String, OpFeatureEntry> featureEntries = new ArrayMap<>(numFeatures);
-            for (int i = 0; i < numFeatures; i++) {
-                featureEntries.put(mFeatures.keyAt(i),
-                        mFeatures.valueAt(i).createFeatureEntryLocked());
+            final ArrayMap<String, AppOpsManager.AttributedOpEntry> attributionEntries =
+                    new ArrayMap<>(numAttributions);
+            for (int i = 0; i < numAttributions; i++) {
+                attributionEntries.put(mAttributions.keyAt(i),
+                        mAttributions.valueAt(i).createAttributedOpEntryLocked());
             }
 
-            return new OpEntry(op, mode, featureEntries);
+            return new OpEntry(op, mode, attributionEntries);
         }
 
-        @NonNull OpEntry createSingleFeatureEntryLocked(@Nullable String featureId) {
-            final int numFeatures = mFeatures.size();
+        @NonNull OpEntry createSingleAttributionEntryLocked(@Nullable String attributionTag) {
+            final int numAttributions = mAttributions.size();
 
-            final ArrayMap<String, OpFeatureEntry> featureEntries = new ArrayMap<>(1);
-            for (int i = 0; i < numFeatures; i++) {
-                if (Objects.equals(mFeatures.keyAt(i), featureId)) {
-                    featureEntries.put(mFeatures.keyAt(i),
-                            mFeatures.valueAt(i).createFeatureEntryLocked());
+            final ArrayMap<String, AttributedOpEntry> attributionEntries = new ArrayMap<>(1);
+            for (int i = 0; i < numAttributions; i++) {
+                if (Objects.equals(mAttributions.keyAt(i), attributionTag)) {
+                    attributionEntries.put(mAttributions.keyAt(i),
+                            mAttributions.valueAt(i).createAttributedOpEntryLocked());
                     break;
                 }
             }
 
-            return new OpEntry(op, mode, featureEntries);
+            return new OpEntry(op, mode, attributionEntries);
         }
 
         boolean isRunning() {
-            final int numFeatures = mFeatures.size();
-            for (int i = 0; i < numFeatures; i++) {
-                if (mFeatures.valueAt(i).isRunning()) {
+            final int numAttributions = mAttributions.size();
+            for (int i = 0; i < numAttributions; i++) {
+                if (mAttributions.valueAt(i).isRunning()) {
                     return true;
                 }
             }
@@ -1405,10 +1408,11 @@
     }
 
     /**
-     * Call {@link FeatureOp#onClientDeath featureOp.onClientDeath(clientId)}.
+     * Call {@link AttributedOp#onClientDeath attributedOp.onClientDeath(clientId)}.
      */
-    private static void onClientDeath(@NonNull FeatureOp featureOp, @NonNull IBinder clientId) {
-        featureOp.onClientDeath(clientId);
+    private static void onClientDeath(@NonNull AttributedOp attributedOp,
+            @NonNull IBinder clientId) {
+        attributedOp.onClientDeath(clientId);
     }
 
 
@@ -1490,20 +1494,21 @@
                     return;
                 }
 
-                ArrayMap<String, String> dstFeatureIds = new ArrayMap<>();
-                ArraySet<String> featureIds = new ArraySet<>();
-                featureIds.add(null);
-                if (pkg.getFeatures() != null) {
-                    int numFeatures = pkg.getFeatures().size();
-                    for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
-                        ParsedFeature feature = pkg.getFeatures().get(featureNum);
-                        featureIds.add(feature.id);
+                ArrayMap<String, String> dstAttributionTags = new ArrayMap<>();
+                ArraySet<String> attributionTags = new ArraySet<>();
+                attributionTags.add(null);
+                if (pkg.getAttributions() != null) {
+                    int numAttributions = pkg.getAttributions().size();
+                    for (int attributionNum = 0; attributionNum < numAttributions;
+                            attributionNum++) {
+                        ParsedAttribution attribution = pkg.getAttributions().get(attributionNum);
+                        attributionTags.add(attribution.tag);
 
-                        int numInheritFrom = feature.inheritFrom.size();
+                        int numInheritFrom = attribution.inheritFrom.size();
                         for (int inheritFromNum = 0; inheritFromNum < numInheritFrom;
                                 inheritFromNum++) {
-                            dstFeatureIds.put(feature.inheritFrom.get(inheritFromNum),
-                                    feature.id);
+                            dstAttributionTags.put(attribution.inheritFrom.get(inheritFromNum),
+                                    attribution.tag);
                         }
                     }
                 }
@@ -1519,25 +1524,32 @@
                         return;
                     }
 
-                    ops.knownFeatureIds.clear();
+                    // Reset cached package properties to re-initialize when needed
+                    ops.bypass = null;
+                    ops.knownAttributionTags.clear();
+
+                    // Merge data collected for removed attributions into their successor
+                    // attributions
                     int numOps = ops.size();
                     for (int opNum = 0; opNum < numOps; opNum++) {
                         Op op = ops.valueAt(opNum);
 
-                        int numFeatures = op.mFeatures.size();
-                        for (int featureNum = numFeatures - 1; featureNum >= 0; featureNum--) {
-                            String featureId = op.mFeatures.keyAt(featureNum);
+                        int numAttributions = op.mAttributions.size();
+                        for (int attributionNum = numAttributions - 1; attributionNum >= 0;
+                                attributionNum--) {
+                            String attributionTag = op.mAttributions.keyAt(attributionNum);
 
-                            if (featureIds.contains(featureId)) {
-                                // feature still exist after upgrade
+                            if (attributionTags.contains(attributionTag)) {
+                                // attribution still exist after upgrade
                                 continue;
                             }
 
-                            String newFeatureId = dstFeatureIds.get(featureId);
+                            String newAttributionTag = dstAttributionTags.get(attributionTag);
 
-                            FeatureOp newFeatureOp = op.getOrCreateFeature(op, newFeatureId);
-                            newFeatureOp.add(op.mFeatures.valueAt(featureNum));
-                            op.mFeatures.removeAt(featureNum);
+                            AttributedOp newAttributedOp = op.getOrCreateAttribution(op,
+                                    newAttributionTag);
+                            newAttributedOp.add(op.mAttributions.valueAt(attributionNum));
+                            op.mAttributions.removeAt(attributionNum);
 
                             scheduleFastWriteLocked();
                         }
@@ -1737,12 +1749,13 @@
                 for (int opNum = 0; opNum < numOps; opNum++) {
                     final Op op = ops.valueAt(opNum);
 
-                    final int numFeatures = op.mFeatures.size();
-                    for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
-                        FeatureOp featureOp = op.mFeatures.valueAt(featureNum);
+                    final int numAttributions = op.mAttributions.size();
+                    for (int attributionNum = 0; attributionNum < numAttributions;
+                            attributionNum++) {
+                        AttributedOp attributedOp = op.mAttributions.valueAt(attributionNum);
 
-                        while (featureOp.mInProgressEvents != null) {
-                            featureOp.finished(featureOp.mInProgressEvents.keyAt(0));
+                        while (attributedOp.mInProgressEvents != null) {
+                            attributedOp.finished(attributedOp.mInProgressEvents.keyAt(0));
                         }
                     }
                 }
@@ -1822,11 +1835,13 @@
                         for (int opNum = 0; opNum < numOps; opNum++) {
                             Op op = ops.valueAt(opNum);
 
-                            int numFeatures = op.mFeatures.size();
-                            for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
-                                FeatureOp featureOp = op.mFeatures.valueAt(featureNum);
+                            int numAttributions = op.mAttributions.size();
+                            for (int attributionNum = 0; attributionNum < numAttributions;
+                                    attributionNum++) {
+                                AttributedOp attributedOp = op.mAttributions.valueAt(
+                                        attributionNum);
 
-                                featureOp.onUidStateChanged(newState);
+                                attributedOp.onUidStateChanged(newState);
                             }
                         }
                     }
@@ -1953,8 +1968,7 @@
             return Collections.emptyList();
         }
         synchronized (this) {
-            Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, null, false /* isPrivileged */,
-                    false /* edit */);
+            Ops pkgOps = getOpsLocked(uid, resolvedPackageName, null, null, false /* edit */);
             if (pkgOps == null) {
                 return null;
             }
@@ -1973,9 +1987,9 @@
     /**
      * Verify that historical appop request arguments are valid.
      */
-    private void ensureHistoricalOpRequestIsValid(int uid, String packageName, String featureId,
-            List<String> opNames, int filter, long beginTimeMillis, long endTimeMillis,
-            int flags) {
+    private void ensureHistoricalOpRequestIsValid(int uid, String packageName,
+            String attributionTag, List<String> opNames, int filter, long beginTimeMillis,
+            long endTimeMillis, int flags) {
         if ((filter & FILTER_BY_UID) != 0) {
             Preconditions.checkArgument(uid != Process.INVALID_UID);
         } else {
@@ -1988,8 +2002,8 @@
             Preconditions.checkArgument(packageName == null);
         }
 
-        if ((filter & FILTER_BY_FEATURE_ID) == 0) {
-            Preconditions.checkArgument(featureId == null);
+        if ((filter & FILTER_BY_ATTRIBUTION_TAG) == 0) {
+            Preconditions.checkArgument(attributionTag == null);
         }
 
         if ((filter & FILTER_BY_OP_NAMES) != 0) {
@@ -1999,17 +2013,18 @@
         }
 
         Preconditions.checkFlagsArgument(filter,
-                FILTER_BY_UID | FILTER_BY_PACKAGE_NAME | FILTER_BY_FEATURE_ID | FILTER_BY_OP_NAMES);
+                FILTER_BY_UID | FILTER_BY_PACKAGE_NAME | FILTER_BY_ATTRIBUTION_TAG
+                        | FILTER_BY_OP_NAMES);
         Preconditions.checkArgumentNonnegative(beginTimeMillis);
         Preconditions.checkArgument(endTimeMillis > beginTimeMillis);
         Preconditions.checkFlagsArgument(flags, OP_FLAGS_ALL);
     }
 
     @Override
-    public void getHistoricalOps(int uid, String packageName, String featureId,
+    public void getHistoricalOps(int uid, String packageName, String attributionTag,
             List<String> opNames, int filter, long beginTimeMillis, long endTimeMillis,
             int flags, RemoteCallback callback) {
-        ensureHistoricalOpRequestIsValid(uid, packageName, featureId, opNames, filter,
+        ensureHistoricalOpRequestIsValid(uid, packageName, attributionTag, opNames, filter,
                 beginTimeMillis, endTimeMillis, flags);
         Objects.requireNonNull(callback, "callback cannot be null");
 
@@ -2030,15 +2045,15 @@
 
         // Must not hold the appops lock
         mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOps,
-                mHistoricalRegistry, uid, packageName, featureId, opNamesArray, filter,
+                mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, filter,
                 beginTimeMillis, endTimeMillis, flags, callback).recycleOnUse());
     }
 
     @Override
-    public void getHistoricalOpsFromDiskRaw(int uid, String packageName, String featureId,
+    public void getHistoricalOpsFromDiskRaw(int uid, String packageName, String attributionTag,
             List<String> opNames, int filter, long beginTimeMillis, long endTimeMillis,
             int flags, RemoteCallback callback) {
-        ensureHistoricalOpRequestIsValid(uid, packageName, featureId, opNames, filter,
+        ensureHistoricalOpRequestIsValid(uid, packageName, attributionTag, opNames, filter,
                 beginTimeMillis, endTimeMillis, flags);
         Objects.requireNonNull(callback, "callback cannot be null");
 
@@ -2050,7 +2065,7 @@
 
         // Must not hold the appops lock
         mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOpsFromDiskRaw,
-                mHistoricalRegistry, uid, packageName, featureId, opNamesArray,
+                mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray,
                 filter, beginTimeMillis, endTimeMillis, flags, callback).recycleOnUse());
     }
 
@@ -2084,11 +2099,10 @@
     }
 
     private void pruneOpLocked(Op op, int uid, String packageName) {
-        op.removeFeaturesWithNoTime();
+        op.removeAttributionsWithNoTime();
 
-        if (op.mFeatures.size() == 0) {
-            Ops ops = getOpsRawLocked(uid, packageName, null, false /* isPrivileged */,
-                    false /* edit */);
+        if (op.mAttributions.isEmpty()) {
+            Ops ops = getOpsLocked(uid, packageName, null, null, false /* edit */);
             if (ops != null) {
                 ops.remove(op.op);
                 if (ops.size() <= 0) {
@@ -2390,9 +2404,9 @@
         ArraySet<ModeCallback> repCbs = null;
         code = AppOpsManager.opToSwitch(code);
 
-        boolean isPrivileged;
+        RestrictionBypass bypass;
         try {
-            isPrivileged = verifyAndGetIsPrivileged(uid, packageName, null);
+            bypass = verifyAndGetBypass(uid, packageName, null);
         } catch (SecurityException e) {
             Slog.e(TAG, "Cannot setMode", e);
             return;
@@ -2400,7 +2414,7 @@
 
         synchronized (this) {
             UidState uidState = getUidStateLocked(uid, false);
-            Op op = getOpLocked(code, uid, packageName, null, isPrivileged, true);
+            Op op = getOpLocked(code, uid, packageName, null, bypass, true);
             if (op != null) {
                 if (op.mode != mode) {
                     op.mode = mode;
@@ -2606,8 +2620,8 @@
                             callbacks = addCallbacks(callbacks, curOp.op, uid, packageName,
                                     mPackageModeWatchers.get(packageName));
 
-                            curOp.removeFeaturesWithNoTime();
-                            if (curOp.mFeatures.size() == 0) {
+                            curOp.removeAttributionsWithNoTime();
+                            if (curOp.mAttributions.isEmpty()) {
                                 pkgOps.removeAt(j);
                             }
                         }
@@ -2674,10 +2688,8 @@
         synchronized (this) {
             int switchOp = (op != AppOpsManager.OP_NONE) ? AppOpsManager.opToSwitch(op) : op;
 
-            // See CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE
             int notifiedOps;
-            if (Compatibility.isChangeEnabled(
-                    CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE)) {
+            if ((flags & CALL_BACK_ON_SWITCHED_OP) == 0) {
                 if (op == OP_NONE) {
                     notifiedOps = ALL_OPS;
                 } else {
@@ -2798,10 +2810,9 @@
      */
     private @Mode int checkOperationUnchecked(int code, int uid, @NonNull String packageName,
                 boolean raw) {
-        boolean isPrivileged;
-
+        RestrictionBypass bypass;
         try {
-            isPrivileged = verifyAndGetIsPrivileged(uid, packageName, null);
+            bypass = verifyAndGetBypass(uid, packageName, null);
         } catch (SecurityException e) {
             Slog.e(TAG, "checkOperation", e);
             return AppOpsManager.opToDefaultMode(code);
@@ -2811,7 +2822,7 @@
             return AppOpsManager.MODE_IGNORED;
         }
         synchronized (this) {
-            if (isOpRestrictedLocked(uid, code, packageName, null, isPrivileged)) {
+            if (isOpRestrictedLocked(uid, code, packageName, bypass)) {
                 return AppOpsManager.MODE_IGNORED;
             }
             code = AppOpsManager.opToSwitch(code);
@@ -2821,7 +2832,7 @@
                 final int rawMode = uidState.opModes.get(code);
                 return raw ? rawMode : uidState.evalMode(code, rawMode);
             }
-            Op op = getOpLocked(code, uid, packageName, null, false, false);
+            Op op = getOpLocked(code, uid, packageName, null, bypass, false);
             if (op == null) {
                 return AppOpsManager.opToDefaultMode(code);
             }
@@ -2884,7 +2895,7 @@
     public int checkPackage(int uid, String packageName) {
         Objects.requireNonNull(packageName);
         try {
-            verifyAndGetIsPrivileged(uid, packageName, null);
+            verifyAndGetBypass(uid, packageName, null);
 
             return AppOpsManager.MODE_ALLOWED;
         } catch (SecurityException ignored) {
@@ -2894,8 +2905,8 @@
 
     @Override
     public int noteProxyOperation(int code, int proxiedUid, String proxiedPackageName,
-            String proxiedFeatureId, int proxyUid, String proxyPackageName,
-            String proxyFeatureId, boolean shouldCollectAsyncNotedOp, String message) {
+            String proxiedAttributionTag, int proxyUid, String proxyPackageName,
+            String proxyAttributionTag, boolean shouldCollectAsyncNotedOp, String message) {
         verifyIncomingUid(proxyUid);
         verifyIncomingOp(code);
 
@@ -2911,7 +2922,7 @@
         final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
                 : AppOpsManager.OP_FLAG_UNTRUSTED_PROXY;
         final int proxyMode = noteOperationUnchecked(code, proxyUid, resolveProxyPackageName,
-                proxyFeatureId, Process.INVALID_UID, null, null, proxyFlags,
+                proxyAttributionTag, Process.INVALID_UID, null, null, proxyFlags,
                 !isProxyTrusted, "proxy " + message);
         if (proxyMode != AppOpsManager.MODE_ALLOWED || Binder.getCallingUid() == proxiedUid) {
             return proxyMode;
@@ -2924,27 +2935,27 @@
         final int proxiedFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXIED
                 : AppOpsManager.OP_FLAG_UNTRUSTED_PROXIED;
         return noteOperationUnchecked(code, proxiedUid, resolveProxiedPackageName,
-                proxiedFeatureId, proxyUid, resolveProxyPackageName, proxyFeatureId,
+                proxiedAttributionTag, proxyUid, resolveProxyPackageName, proxyAttributionTag,
                 proxiedFlags, shouldCollectAsyncNotedOp, message);
     }
 
     @Override
-    public int noteOperation(int code, int uid, String packageName, String featureId,
+    public int noteOperation(int code, int uid, String packageName, String attributionTag,
             boolean shouldCollectAsyncNotedOp, String message) {
         final CheckOpsDelegate delegate;
         synchronized (this) {
             delegate = mCheckOpsDelegate;
         }
         if (delegate == null) {
-            return noteOperationImpl(code, uid, packageName, featureId, shouldCollectAsyncNotedOp,
-                    message);
+            return noteOperationImpl(code, uid, packageName, attributionTag,
+                    shouldCollectAsyncNotedOp, message);
         }
-        return delegate.noteOperation(code, uid, packageName, featureId, shouldCollectAsyncNotedOp,
-                message, AppOpsService.this::noteOperationImpl);
+        return delegate.noteOperation(code, uid, packageName, attributionTag,
+                shouldCollectAsyncNotedOp, message, AppOpsService.this::noteOperationImpl);
     }
 
     private int noteOperationImpl(int code, int uid, @Nullable String packageName,
-            @Nullable String featureId, boolean shouldCollectAsyncNotedOp,
+            @Nullable String attributionTag, boolean shouldCollectAsyncNotedOp,
             @Nullable String message) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
@@ -2952,25 +2963,25 @@
         if (resolvedPackageName == null) {
             return AppOpsManager.MODE_IGNORED;
         }
-        return noteOperationUnchecked(code, uid, resolvedPackageName, featureId,
+        return noteOperationUnchecked(code, uid, resolvedPackageName, attributionTag,
                 Process.INVALID_UID, null, null, AppOpsManager.OP_FLAG_SELF,
                 shouldCollectAsyncNotedOp, message);
     }
 
     private int noteOperationUnchecked(int code, int uid, @NonNull String packageName,
-            @Nullable String featureId, int proxyUid, String proxyPackageName,
-            @Nullable String proxyFeatureId, @OpFlags int flags, boolean shouldCollectAsyncNotedOp,
-            @Nullable String message) {
-        boolean isPrivileged;
+            @Nullable String attributionTag, int proxyUid, String proxyPackageName,
+            @Nullable String proxyAttributionTag, @OpFlags int flags,
+            boolean shouldCollectAsyncNotedOp, @Nullable String message) {
+        RestrictionBypass bypass;
         try {
-            isPrivileged = verifyAndGetIsPrivileged(uid, packageName, featureId);
+            bypass = verifyAndGetBypass(uid, packageName, attributionTag);
         } catch (SecurityException e) {
             Slog.e(TAG, "noteOperation", e);
             return AppOpsManager.MODE_ERRORED;
         }
 
         synchronized (this) {
-            final Ops ops = getOpsRawLocked(uid, packageName, featureId, isPrivileged,
+            final Ops ops = getOpsLocked(uid, packageName, attributionTag, bypass,
                     true /* edit */);
             if (ops == null) {
                 scheduleOpNotedIfNeededLocked(code, uid, packageName,
@@ -2980,17 +2991,17 @@
                 return AppOpsManager.MODE_ERRORED;
             }
             final Op op = getOpLocked(ops, code, uid, true);
-            final FeatureOp featureOp = op.getOrCreateFeature(op, featureId);
-            if (isOpRestrictedLocked(uid, code, packageName, featureId, isPrivileged)) {
+            final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
+            if (isOpRestrictedLocked(uid, code, packageName, bypass)) {
                 scheduleOpNotedIfNeededLocked(code, uid, packageName,
                         AppOpsManager.MODE_IGNORED);
                 return AppOpsManager.MODE_IGNORED;
             }
             final UidState uidState = ops.uidState;
-            if (featureOp.isRunning()) {
+            if (attributedOp.isRunning()) {
                 Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName + " code "
                         + code + " startTime of in progress event="
-                        + featureOp.mInProgressEvents.valueAt(0).getStartTime());
+                        + attributedOp.mInProgressEvents.valueAt(0).getStartTime());
             }
 
             final int switchCode = AppOpsManager.opToSwitch(code);
@@ -3002,7 +3013,7 @@
                     if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + packageName);
-                    featureOp.rejected(uidState.state, flags);
+                    attributedOp.rejected(uidState.state, flags);
                     scheduleOpNotedIfNeededLocked(code, uid, packageName, uidMode);
                     return uidMode;
                 }
@@ -3014,26 +3025,31 @@
                     if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + mode + " for code "
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + packageName);
-                    featureOp.rejected(uidState.state, flags);
+                    attributedOp.rejected(uidState.state, flags);
                     scheduleOpNotedIfNeededLocked(code, uid, packageName, mode);
                     return mode;
                 }
             }
-            if (DEBUG) Slog.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
-                    + " package " + packageName + (featureId == null ? "" : "." + featureId));
-            featureOp.accessed(proxyUid, proxyPackageName, proxyFeatureId, uidState.state, flags);
+            if (DEBUG) {
+                Slog.d(TAG,
+                        "noteOperation: allowing code " + code + " uid " + uid + " package "
+                                + packageName + (attributionTag == null ? ""
+                                : "." + attributionTag));
+            }
+            attributedOp.accessed(proxyUid, proxyPackageName, proxyAttributionTag, uidState.state,
+                    flags);
             scheduleOpNotedIfNeededLocked(code, uid, packageName,
                     AppOpsManager.MODE_ALLOWED);
 
             if (shouldCollectAsyncNotedOp) {
-                collectAsyncNotedOp(uid, packageName, code, featureId, message);
+                collectAsyncNotedOp(uid, packageName, code, attributionTag, message);
             }
 
             return AppOpsManager.MODE_ALLOWED;
         }
     }
 
-    // TODO moltmann: Allow watching for feature ops
+    // TODO moltmann: Allow watching for attribution ops
     @Override
     public void startWatchingActive(int[] ops, IAppOpsActiveCallback callback) {
         int watchedUid = -1;
@@ -3131,11 +3147,11 @@
      * @param uid The uid the op was noted for
      * @param packageName The package the op was noted for
      * @param opCode The code of the op noted
-     * @param featureId The id of the feature to op was noted for
+     * @param attributionTag attribution tag the op was noted for
      * @param message The message for the op noting
      */
     private void collectAsyncNotedOp(int uid, @NonNull String packageName, int opCode,
-            @Nullable String featureId, @NonNull String message) {
+            @Nullable String attributionTag, @NonNull String message) {
         Objects.requireNonNull(message);
 
         int callingUid = Binder.getCallingUid();
@@ -3147,10 +3163,10 @@
 
                 RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key);
                 AsyncNotedAppOp asyncNotedOp = new AsyncNotedAppOp(opCode, callingUid,
-                        featureId, message, System.currentTimeMillis());
+                        attributionTag, message, System.currentTimeMillis());
                 final boolean[] wasNoteForwarded = {false};
 
-                reportRuntimeAppOpAccessMessageAsyncLocked(uid, packageName, opCode, featureId,
+                reportRuntimeAppOpAccessMessageAsyncLocked(uid, packageName, opCode, attributionTag,
                         message);
 
                 if (callbacks != null) {
@@ -3161,7 +3177,7 @@
                         } catch (RemoteException e) {
                             Slog.e(TAG,
                                     "Could not forward noteOp of " + opCode + " to " + packageName
-                                            + "/" + uid + "(" + featureId + ")", e);
+                                            + "/" + uid + "(" + attributionTag + ")", e);
                         }
                     });
                 }
@@ -3205,7 +3221,7 @@
         int uid = Binder.getCallingUid();
         Pair<String, Integer> key = getAsyncNotedOpsKey(packageName, uid);
 
-        verifyAndGetIsPrivileged(uid, packageName, null);
+        verifyAndGetBypass(uid, packageName, null);
 
         synchronized (this) {
             RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key);
@@ -3235,7 +3251,7 @@
         int uid = Binder.getCallingUid();
         Pair<String, Integer> key = getAsyncNotedOpsKey(packageName, uid);
 
-        verifyAndGetIsPrivileged(uid, packageName, null);
+        verifyAndGetBypass(uid, packageName, null);
 
         synchronized (this) {
             RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key);
@@ -3254,7 +3270,7 @@
 
         int uid = Binder.getCallingUid();
 
-        verifyAndGetIsPrivileged(uid, packageName, null);
+        verifyAndGetBypass(uid, packageName, null);
 
         synchronized (this) {
             return mUnforwardedAsyncNotedOps.remove(getAsyncNotedOpsKey(packageName, uid));
@@ -3263,7 +3279,7 @@
 
     @Override
     public int startOperation(IBinder clientId, int code, int uid, String packageName,
-            String featureId, boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
+            String attributionTag, boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
             String message) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
@@ -3272,16 +3288,16 @@
             return  AppOpsManager.MODE_IGNORED;
         }
 
-        boolean isPrivileged;
+        RestrictionBypass bypass;
         try {
-            isPrivileged = verifyAndGetIsPrivileged(uid, packageName, featureId);
+            bypass = verifyAndGetBypass(uid, packageName, attributionTag);
         } catch (SecurityException e) {
             Slog.e(TAG, "startOperation", e);
             return AppOpsManager.MODE_ERRORED;
         }
 
         synchronized (this) {
-            final Ops ops = getOpsRawLocked(uid, resolvedPackageName, featureId, isPrivileged,
+            final Ops ops = getOpsLocked(uid, resolvedPackageName, attributionTag, bypass,
                     true /* edit */);
             if (ops == null) {
                 if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid
@@ -3289,10 +3305,10 @@
                 return AppOpsManager.MODE_ERRORED;
             }
             final Op op = getOpLocked(ops, code, uid, true);
-            if (isOpRestrictedLocked(uid, code, resolvedPackageName, featureId, isPrivileged)) {
+            if (isOpRestrictedLocked(uid, code, resolvedPackageName, bypass)) {
                 return AppOpsManager.MODE_IGNORED;
             }
-            final FeatureOp featureOp = op.getOrCreateFeature(op, featureId);
+            final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
             final int switchCode = AppOpsManager.opToSwitch(code);
             final UidState uidState = ops.uidState;
             // If there is a non-default per UID policy (we set UID op mode only if
@@ -3305,7 +3321,7 @@
                     if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + resolvedPackageName);
-                    featureOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF);
+                    attributedOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF);
                     return uidMode;
                 }
             } else {
@@ -3317,21 +3333,21 @@
                     if (DEBUG) Slog.d(TAG, "startOperation: reject #" + mode + " for code "
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + resolvedPackageName);
-                    featureOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF);
+                    attributedOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF);
                     return mode;
                 }
             }
             if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid
                     + " package " + resolvedPackageName);
             try {
-                featureOp.started(clientId, uidState.state);
+                attributedOp.started(clientId, uidState.state);
             } catch (RemoteException e) {
                 throw new RuntimeException(e);
             }
         }
 
         if (shouldCollectAsyncNotedOp) {
-            collectAsyncNotedOp(uid, packageName, code, featureId, message);
+            collectAsyncNotedOp(uid, packageName, code, attributionTag, message);
         }
 
         return AppOpsManager.MODE_ALLOWED;
@@ -3339,7 +3355,7 @@
 
     @Override
     public void finishOperation(IBinder clientId, int code, int uid, String packageName,
-            String featureId) {
+            String attributionTag) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
         String resolvedPackageName = resolvePackageName(uid, packageName);
@@ -3347,26 +3363,26 @@
             return;
         }
 
-        boolean isPrivileged;
+        RestrictionBypass bypass;
         try {
-            isPrivileged = verifyAndGetIsPrivileged(uid, packageName, featureId);
+            bypass = verifyAndGetBypass(uid, packageName, attributionTag);
         } catch (SecurityException e) {
             Slog.e(TAG, "Cannot finishOperation", e);
             return;
         }
 
         synchronized (this) {
-            Op op = getOpLocked(code, uid, resolvedPackageName, featureId, isPrivileged, true);
+            Op op = getOpLocked(code, uid, resolvedPackageName, attributionTag, bypass, true);
             if (op == null) {
                 return;
             }
-            final FeatureOp featureOp = op.mFeatures.get(featureId);
-            if (featureOp == null) {
+            final AttributedOp attributedOp = op.mAttributions.get(attributionTag);
+            if (attributedOp == null) {
                 return;
             }
 
             try {
-                featureOp.finished(clientId);
+                attributedOp.finished(clientId);
             } catch (IllegalStateException e) {
                 Slog.e(TAG, "Operation not started: uid=" + uid + " pkg="
                         + packageName + " op=" + AppOpsManager.opToName(code), e);
@@ -3617,50 +3633,66 @@
     }
 
     /**
-     * Verify that package belongs to uid and return whether the package is privileged.
+     * Create a restriction description matching the properties of the package.
+     *
+     * @param context A context to use
+     * @param pkg The package to create the restriction description for
+     *
+     * @return The restriction matching the package
+     */
+    private RestrictionBypass getBypassforPackage(@NonNull AndroidPackage pkg) {
+        return new RestrictionBypass(pkg.isPrivileged(), mContext.checkPermission(
+                android.Manifest.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS, -1, pkg.getUid())
+                == PackageManager.PERMISSION_GRANTED);
+    }
+
+    /**
+     * Verify that package belongs to uid and return the {@link RestrictionBypass bypass
+     * description} for the package.
      *
      * @param uid The uid the package belongs to
      * @param packageName The package the might belong to the uid
-     * @param featureId The feature in the package or {@code null} if no need to verify
+     * @param attributionTag attribution tag or {@code null} if no need to verify
      *
      * @return {@code true} iff the package is privileged
      */
-    private boolean verifyAndGetIsPrivileged(int uid, String packageName,
-            @Nullable String featureId) {
+    private @Nullable RestrictionBypass verifyAndGetBypass(int uid, String packageName,
+            @Nullable String attributionTag) {
         if (uid == Process.ROOT_UID) {
             // For backwards compatibility, don't check package name for root UID.
-            return false;
+            return null;
         }
 
-        // Do not check if uid/packageName/featureId is already known
+        // Do not check if uid/packageName/attributionTag is already known
         synchronized (this) {
             UidState uidState = mUidStates.get(uid);
             if (uidState != null && uidState.pkgOps != null) {
                 Ops ops = uidState.pkgOps.get(packageName);
 
-                if (ops != null && (featureId == null || ops.knownFeatureIds.contains(featureId))) {
-                    return ops.isPrivileged;
+                if (ops != null && (attributionTag == null || ops.knownAttributionTags.contains(
+                        attributionTag)) && ops.bypass != null) {
+                    return ops.bypass;
                 }
             }
         }
 
-        boolean isPrivileged = false;
+        RestrictionBypass bypass = null;
         final long ident = Binder.clearCallingIdentity();
         try {
             int pkgUid;
             AndroidPackage pkg = LocalServices.getService(PackageManagerInternal.class).getPackage(
                     packageName);
-            boolean isFeatureIdValid = false;
+            boolean isAttributionTagValid = false;
 
             if (pkg != null) {
-                if (featureId == null) {
-                    isFeatureIdValid = true;
+                if (attributionTag == null) {
+                    isAttributionTagValid = true;
                 } else {
-                    if (pkg.getFeatures() != null) {
-                        int numFeatures = pkg.getFeatures().size();
-                        for (int i = 0; i < numFeatures; i++) {
-                            if (pkg.getFeatures().get(i).id.equals(featureId)) {
-                                isFeatureIdValid = true;
+                    if (pkg.getAttributions() != null) {
+                        int numAttributions = pkg.getAttributions().size();
+                        for (int i = 0; i < numAttributions; i++) {
+                            if (pkg.getAttributions().get(i).tag.equals(attributionTag)) {
+                                isAttributionTagValid = true;
                             }
                         }
                     }
@@ -3668,14 +3700,14 @@
 
                 pkgUid = UserHandle.getUid(
                         UserHandle.getUserId(uid), UserHandle.getAppId(pkg.getUid()));
-                isPrivileged = pkg.isPrivileged();
+                bypass = getBypassforPackage(pkg);
             } else {
-                // Allow any feature id for resolvable uids
-                isFeatureIdValid = true;
+                // Allow any attribution tag for resolvable uids
+                isAttributionTagValid = true;
 
                 pkgUid = resolveUid(packageName);
                 if (pkgUid >= 0) {
-                    isPrivileged = false;
+                    bypass = RestrictionBypass.UNRESTRICTED;
                 }
             }
             if (pkgUid != uid) {
@@ -3683,16 +3715,16 @@
                         + " but it is really " + pkgUid);
             }
 
-            if (!isFeatureIdValid) {
+            if (!isAttributionTagValid) {
                 // TODO moltmann: Switch from logging to enforcement
-                Slog.e(TAG, "featureId " + featureId + " not declared in manifest of "
+                Slog.e(TAG, "attributionTag " + attributionTag + " not declared in manifest of "
                         + packageName);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
 
-        return isPrivileged;
+        return bypass;
     }
 
     /**
@@ -3700,14 +3732,14 @@
      *
      * @param uid The uid the package belongs to
      * @param packageName The name of the package
-     * @param featureId The feature in the package
-     * @param isPrivileged If the package is privilidged (ignored if {@code edit} is false)
+     * @param attributionTag attribution tag
+     * @param bypass When to bypass certain op restrictions (can be null if edit == false)
      * @param edit If an ops does not exist, create the ops?
 
-     * @return
+     * @return The ops
      */
-    private Ops getOpsRawLocked(int uid, String packageName, @Nullable String featureId,
-            boolean isPrivileged, boolean edit) {
+    private Ops getOpsLocked(int uid, String packageName, @Nullable String attributionTag,
+            @Nullable RestrictionBypass bypass, boolean edit) {
         UidState uidState = getUidStateLocked(uid, edit);
         if (uidState == null) {
             return null;
@@ -3725,53 +3757,18 @@
             if (!edit) {
                 return null;
             }
-            ops = new Ops(packageName, uidState, isPrivileged);
-            uidState.pkgOps.put(packageName, ops);
-        }
-        if (edit && featureId != null) {
-            ops.knownFeatureIds.add(featureId);
-        }
-        return ops;
-    }
-
-    /**
-     * Get the state of all ops for a package.
-     *
-     * <p>Usually callers should use {@link #getOpLocked} and not call this directly.
-     *
-     * @param uid The uid the of the package
-     * @param packageName The package name for which to get the state for
-     * @param featureId The feature in the package
-     * @param edit Iff {@code true} create the {@link Ops} object if not yet created
-     * @param isPrivileged Whether the package is privileged or not
-     *
-     * @return The {@link Ops state} of all ops for the package
-     */
-    private @Nullable Ops getOpsRawNoVerifyLocked(int uid, @NonNull String packageName,
-            @Nullable String featureId, boolean edit, boolean isPrivileged) {
-        UidState uidState = getUidStateLocked(uid, edit);
-        if (uidState == null) {
-            return null;
-        }
-
-        if (uidState.pkgOps == null) {
-            if (!edit) {
-                return null;
-            }
-            uidState.pkgOps = new ArrayMap<>();
-        }
-
-        Ops ops = uidState.pkgOps.get(packageName);
-        if (ops == null) {
-            if (!edit) {
-                return null;
-            }
-            ops = new Ops(packageName, uidState, isPrivileged);
+            ops = new Ops(packageName, uidState);
             uidState.pkgOps.put(packageName, ops);
         }
 
-        if (edit && featureId != null) {
-            ops.knownFeatureIds.add(featureId);
+        if (edit) {
+            if (bypass != null) {
+                ops.bypass = bypass;
+            }
+
+            if (attributionTag != null) {
+                ops.knownAttributionTags.add(attributionTag);
+            }
         }
 
         return ops;
@@ -3799,16 +3796,15 @@
      * @param code The code of the op
      * @param uid The uid the of the package
      * @param packageName The package name for which to get the state for
-     * @param featureId The feature in the package
-     * @param isPrivileged Whether the package is privileged or not (only used if {@code edit
-     *                     == true})
+     * @param attributionTag The attribution tag
+     * @param bypass When to bypass certain op restrictions (can be null if edit == false)
      * @param edit Iff {@code true} create the {@link Op} object if not yet created
      *
      * @return The {@link Op state} of the op
      */
     private @Nullable Op getOpLocked(int code, int uid, @NonNull String packageName,
-            @Nullable String featureId, boolean isPrivileged, boolean edit) {
-        Ops ops = getOpsRawNoVerifyLocked(uid, packageName, featureId, edit, isPrivileged);
+            @Nullable String attributionTag, @Nullable RestrictionBypass bypass, boolean edit) {
+        Ops ops = getOpsLocked(uid, packageName, attributionTag, bypass, edit);
         if (ops == null) {
             return null;
         }
@@ -3839,7 +3835,7 @@
     }
 
     private boolean isOpRestrictedLocked(int uid, int code, String packageName,
-            @Nullable String featureId, boolean isPrivileged) {
+            @Nullable RestrictionBypass appBypass) {
         int userHandle = UserHandle.getUserId(uid);
         final int restrictionSetCount = mOpUserRestrictions.size();
 
@@ -3848,12 +3844,15 @@
             // package is exempt from the restriction.
             ClientRestrictionState restrictionState = mOpUserRestrictions.valueAt(i);
             if (restrictionState.hasRestriction(code, packageName, userHandle)) {
-                if (AppOpsManager.opAllowSystemBypassRestriction(code)) {
+                RestrictionBypass opBypass = opAllowSystemBypassRestriction(code);
+                if (opBypass != null) {
                     // If we are the system, bypass user restrictions for certain codes
                     synchronized (this) {
-                        Ops ops = getOpsRawLocked(uid, packageName, featureId, isPrivileged,
-                                true /* edit */);
-                        if ((ops != null) && ops.isPrivileged) {
+                        if (opBypass.isPrivileged && appBypass != null && appBypass.isPrivileged) {
+                            return false;
+                        }
+                        if (opBypass.isRecordAudioRestrictionExcept && appBypass != null
+                                && appBypass.isRecordAudioRestrictionExcept) {
                             return false;
                         }
                     }
@@ -4043,28 +4042,6 @@
             throws NumberFormatException, XmlPullParserException, IOException {
         int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
         final UidState uidState = getUidStateLocked(uid, true);
-        String isPrivilegedString = parser.getAttributeValue(null, "p");
-        boolean isPrivileged = false;
-        if (isPrivilegedString == null) {
-            try {
-                IPackageManager packageManager = ActivityThread.getPackageManager();
-                if (packageManager != null) {
-                    ApplicationInfo appInfo = ActivityThread.getPackageManager()
-                            .getApplicationInfo(pkgName, 0, UserHandle.getUserId(uid));
-                    if (appInfo != null) {
-                        isPrivileged = (appInfo.privateFlags
-                                & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
-                    }
-                } else {
-                    // Could not load data, don't add to cache so it will be loaded later.
-                    return;
-                }
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Could not contact PackageManager", e);
-            }
-        } else {
-            isPrivileged = Boolean.parseBoolean(isPrivilegedString);
-        }
         int outerDepth = parser.getDepth();
         int type;
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -4074,7 +4051,7 @@
             }
             String tagName = parser.getName();
             if (tagName.equals("op")) {
-                readOp(parser, uidState, pkgName, isPrivileged);
+                readOp(parser, uidState, pkgName);
             } else {
                 Slog.w(TAG, "Unknown element under <pkg>: "
                         + parser.getName());
@@ -4084,9 +4061,9 @@
         uidState.evalForegroundOps(mOpModeWatchers);
     }
 
-    private void readFeatureOp(XmlPullParser parser, @NonNull Op parent,
-            @Nullable String feature) throws NumberFormatException, IOException {
-        final FeatureOp featureOp = parent.getOrCreateFeature(parent, feature);
+    private void readAttributionOp(XmlPullParser parser, @NonNull Op parent,
+            @Nullable String attribution) throws NumberFormatException, IOException {
+        final AttributedOp attributedOp = parent.getOrCreateAttribution(parent, attribution);
 
         final long key = XmlUtils.readLongAttribute(parser, "n");
         final int uidState = extractUidStateFromKey(key);
@@ -4097,19 +4074,19 @@
         final long accessDuration = XmlUtils.readLongAttribute(parser, "d", -1);
         final String proxyPkg = XmlUtils.readStringAttribute(parser, "pp");
         final int proxyUid = XmlUtils.readIntAttribute(parser, "pu", Process.INVALID_UID);
-        final String proxyFeatureId = XmlUtils.readStringAttribute(parser, "pc");
+        final String proxyAttributionTag = XmlUtils.readStringAttribute(parser, "pc");
 
         if (accessTime > 0) {
-            featureOp.accessed(accessTime, accessDuration, proxyUid, proxyPkg, proxyFeatureId,
-                    uidState, opFlags);
+            attributedOp.accessed(accessTime, accessDuration, proxyUid, proxyPkg,
+                    proxyAttributionTag, uidState, opFlags);
         }
         if (rejectTime > 0) {
-            featureOp.rejected(rejectTime, uidState, opFlags);
+            attributedOp.rejected(rejectTime, uidState, opFlags);
         }
     }
 
-    private void readOp(XmlPullParser parser, @NonNull UidState uidState,
-        @NonNull String pkgName, boolean isPrivileged) throws NumberFormatException,
+    private void readOp(XmlPullParser parser, @NonNull UidState uidState, @NonNull String pkgName)
+            throws NumberFormatException,
         XmlPullParserException, IOException {
         int opCode = Integer.parseInt(parser.getAttributeValue(null, "n"));
         if (isIgnoredAppOp(opCode)) {
@@ -4130,7 +4107,7 @@
             }
             String tagName = parser.getName();
             if (tagName.equals("st")) {
-                readFeatureOp(parser, op, XmlUtils.readStringAttribute(parser, "id"));
+                readAttributionOp(parser, op, XmlUtils.readStringAttribute(parser, "id"));
             } else {
                 Slog.w(TAG, "Unknown element under <op>: "
                         + parser.getName());
@@ -4143,7 +4120,7 @@
         }
         Ops ops = uidState.pkgOps.get(pkgName);
         if (ops == null) {
-            ops = new Ops(pkgName, uidState, isPrivileged);
+            ops = new Ops(pkgName, uidState);
             uidState.pkgOps.put(pkgName, ops);
         }
         ops.put(op.op, op);
@@ -4235,17 +4212,6 @@
                         }
                         out.startTag(null, "uid");
                         out.attribute(null, "n", Integer.toString(pkg.getUid()));
-                        synchronized (this) {
-                            Ops ops = getOpsRawLocked(pkg.getUid(), pkg.getPackageName(), null,
-                                    false /* isPrivileged */, false /* edit */);
-                            // Should always be present as the list of PackageOps is generated
-                            // from Ops.
-                            if (ops != null) {
-                                out.attribute(null, "p", Boolean.toString(ops.isPrivileged));
-                            } else {
-                                out.attribute(null, "p", Boolean.toString(false));
-                            }
-                        }
                         List<AppOpsManager.OpEntry> ops = pkg.getOps();
                         for (int j=0; j<ops.size(); j++) {
                             AppOpsManager.OpEntry op = ops.get(j);
@@ -4255,11 +4221,11 @@
                                 out.attribute(null, "m", Integer.toString(op.getMode()));
                             }
 
-                            for (String featureId : op.getFeatures().keySet()) {
-                                final OpFeatureEntry feature = op.getFeatures().get(
-                                        featureId);
+                            for (String attributionTag : op.getAttributedOpEntries().keySet()) {
+                                final AttributedOpEntry attribution =
+                                        op.getAttributedOpEntries().get(attributionTag);
 
-                                final ArraySet<Long> keys = feature.collectKeys();
+                                final ArraySet<Long> keys = attribution.collectKeys();
 
                                 final int keyCount = keys.size();
                                 for (int k = 0; k < keyCount; k++) {
@@ -4268,14 +4234,14 @@
                                     final int uidState = AppOpsManager.extractUidStateFromKey(key);
                                     final int flags = AppOpsManager.extractFlagsFromKey(key);
 
-                                    final long accessTime = feature.getLastAccessTime(uidState,
+                                    final long accessTime = attribution.getLastAccessTime(uidState,
                                             uidState, flags);
-                                    final long rejectTime = feature.getLastRejectTime(uidState,
+                                    final long rejectTime = attribution.getLastRejectTime(uidState,
                                             uidState, flags);
-                                    final long accessDuration = feature.getLastDuration(uidState,
-                                            uidState, flags);
+                                    final long accessDuration = attribution.getLastDuration(
+                                            uidState, uidState, flags);
                                     // Proxy information for rejections is not backed up
-                                    final OpEventProxyInfo proxy = feature.getLastProxyInfo(
+                                    final OpEventProxyInfo proxy = attribution.getLastProxyInfo(
                                             uidState, uidState, flags);
 
                                     if (accessTime <= 0 && rejectTime <= 0 && accessDuration <= 0
@@ -4284,17 +4250,17 @@
                                     }
 
                                     String proxyPkg = null;
-                                    String proxyFeatureId = null;
+                                    String proxyAttributionTag = null;
                                     int proxyUid = Process.INVALID_UID;
                                     if (proxy != null) {
                                         proxyPkg = proxy.getPackageName();
-                                        proxyFeatureId = proxy.getFeatureId();
+                                        proxyAttributionTag = proxy.getAttributionTag();
                                         proxyUid = proxy.getUid();
                                     }
 
                                     out.startTag(null, "st");
-                                    if (featureId != null) {
-                                        out.attribute(null, "id", featureId);
+                                    if (attributionTag != null) {
+                                        out.attribute(null, "id", attributionTag);
                                     }
                                     out.attribute(null, "n", Long.toString(key));
                                     if (accessTime > 0) {
@@ -4309,8 +4275,8 @@
                                     if (proxyPkg != null) {
                                         out.attribute(null, "pp", proxyPkg);
                                     }
-                                    if (proxyFeatureId != null) {
-                                        out.attribute(null, "pc", proxyFeatureId);
+                                    if (proxyAttributionTag != null) {
+                                        out.attribute(null, "pc", proxyAttributionTag);
                                     }
                                     if (proxyUid >= 0) {
                                         out.attribute(null, "pu", Integer.toString(proxyUid));
@@ -4344,7 +4310,7 @@
 
         int userId = UserHandle.USER_SYSTEM;
         String packageName;
-        String featureId;
+        String attributionTag;
         String opStr;
         String modeStr;
         int op;
@@ -4446,8 +4412,8 @@
                     userId = UserHandle.parseUserArg(getNextArgRequired());
                 } else if ("--uid".equals(argument)) {
                     targetsUid = true;
-                } else if ("--feature".equals(argument)) {
-                    featureId = getNextArgRequired();
+                } else if ("--attribution".equals(argument)) {
+                    attributionTag = getNextArgRequired();
                 } else {
                     if (packageName == null) {
                         packageName = argument;
@@ -4542,13 +4508,16 @@
         pw.println("AppOps service (appops) commands:");
         pw.println("  help");
         pw.println("    Print this help text.");
-        pw.println("  start [--user <USER_ID>] [--feature <FEATURE_ID>] <PACKAGE | UID> <OP> ");
+        pw.println("  start [--user <USER_ID>] [--attribution <ATTRIBUTION_TAG>] <PACKAGE | UID> "
+                + "<OP> ");
         pw.println("    Starts a given operation for a particular application.");
-        pw.println("  stop [--user <USER_ID>] [--feature <FEATURE_ID>] <PACKAGE | UID> <OP> ");
+        pw.println("  stop [--user <USER_ID>] [--attribution <ATTRIBUTION_TAG>] <PACKAGE | UID> "
+                + "<OP> ");
         pw.println("    Stops a given operation for a particular application.");
         pw.println("  set [--user <USER_ID>] <[--uid] PACKAGE | UID> <OP> <MODE>");
         pw.println("    Set the mode for a particular application and operation.");
-        pw.println("  get [--user <USER_ID>] [--feature <FEATURE_ID>] <PACKAGE | UID> [<OP>]");
+        pw.println("  get [--user <USER_ID>] [--attribution <ATTRIBUTION_TAG>] <PACKAGE | UID> "
+                + "[<OP>]");
         pw.println("    Return the mode for a particular application and optional operation.");
         pw.println("  query-op [--user <USER_ID>] <OP> [<MODE>]");
         pw.println("    Print all packages that currently have the given op in the given mode.");
@@ -4562,8 +4531,8 @@
         pw.println("    <PACKAGE> an Android package name or its UID if prefixed by --uid");
         pw.println("    <OP>      an AppOps operation.");
         pw.println("    <MODE>    one of allow, ignore, deny, or default");
-        pw.println("    <USER_ID> the user id under which the package is installed. If --user is not");
-        pw.println("              specified, the current user is assumed.");
+        pw.println("    <USER_ID> the user id under which the package is installed. If --user is");
+        pw.println("              not specified, the current user is assumed.");
     }
 
     static int onShellCommand(Shell shell, String cmd) {
@@ -4652,7 +4621,7 @@
                             pw.print(AppOpsManager.opToName(ent.getOp()));
                             pw.print(": ");
                             pw.print(AppOpsManager.modeToName(ent.getMode()));
-                            if (shell.featureId == null) {
+                            if (shell.attributionTag == null) {
                                 if (ent.getLastAccessTime(OP_FLAGS_ALL) != -1) {
                                     pw.print("; time=");
                                     TimeUtils.formatDuration(
@@ -4672,29 +4641,30 @@
                                     TimeUtils.formatDuration(ent.getLastDuration(OP_FLAGS_ALL), pw);
                                 }
                             } else {
-                                final OpFeatureEntry featureEnt = ent.getFeatures().get(
-                                        shell.featureId);
-                                if (featureEnt != null) {
-                                    if (featureEnt.getLastAccessTime(OP_FLAGS_ALL) != -1) {
+                                final AppOpsManager.AttributedOpEntry attributionEnt =
+                                        ent.getAttributedOpEntries().get(shell.attributionTag);
+                                if (attributionEnt != null) {
+                                    if (attributionEnt.getLastAccessTime(OP_FLAGS_ALL) != -1) {
                                         pw.print("; time=");
-                                        TimeUtils.formatDuration(now - featureEnt.getLastAccessTime(
-                                                OP_FLAGS_ALL), pw);
+                                        TimeUtils.formatDuration(
+                                                now - attributionEnt.getLastAccessTime(
+                                                        OP_FLAGS_ALL), pw);
                                         pw.print(" ago");
                                     }
-                                    if (featureEnt.getLastRejectTime(OP_FLAGS_ALL) != -1) {
+                                    if (attributionEnt.getLastRejectTime(OP_FLAGS_ALL) != -1) {
                                         pw.print("; rejectTime=");
                                         TimeUtils.formatDuration(
-                                                now - featureEnt.getLastRejectTime(OP_FLAGS_ALL),
-                                                pw);
+                                                now - attributionEnt.getLastRejectTime(
+                                                        OP_FLAGS_ALL), pw);
                                         pw.print(" ago");
                                     }
-                                    if (featureEnt.isRunning()) {
+                                    if (attributionEnt.isRunning()) {
                                         pw.print(" (running)");
-                                    } else if (featureEnt.getLastDuration(OP_FLAGS_ALL)
+                                    } else if (attributionEnt.getLastDuration(OP_FLAGS_ALL)
                                             != -1) {
                                         pw.print("; duration=");
                                         TimeUtils.formatDuration(
-                                                featureEnt.getLastDuration(OP_FLAGS_ALL), pw);
+                                                attributionEnt.getLastDuration(OP_FLAGS_ALL), pw);
                                     }
                                 }
                             }
@@ -4802,7 +4772,7 @@
 
                     if (shell.packageName != null) {
                         shell.mInterface.startOperation(shell.mToken, shell.op, shell.packageUid,
-                                shell.packageName, shell.featureId, true, true,
+                                shell.packageName, shell.attributionTag, true, true,
                                 "appops start shell command");
                     } else {
                         return -1;
@@ -4816,8 +4786,8 @@
                     }
 
                     if (shell.packageName != null) {
-                        shell.mInterface.finishOperation(shell.mToken,
-                                shell.op, shell.packageUid, shell.packageName, shell.featureId);
+                        shell.mInterface.finishOperation(shell.mToken, shell.op, shell.packageUid,
+                                shell.packageName, shell.attributionTag);
                     } else {
                         return -1;
                     }
@@ -4842,35 +4812,35 @@
         pw.println("    Limit output to data associated with the given app op mode.");
         pw.println("  --package [PACKAGE]");
         pw.println("    Limit output to data associated with the given package name.");
-        pw.println("  --featureId [featureId]");
-        pw.println("    Limit output to data associated with the given feature id.");
+        pw.println("  --attributionTag [attributionTag]");
+        pw.println("    Limit output to data associated with the given attribution tag.");
         pw.println("  --watchers");
         pw.println("    Only output the watcher sections.");
     }
 
-    private void dumpStatesLocked(@NonNull PrintWriter pw, @Nullable String filterFeatureId,
+    private void dumpStatesLocked(@NonNull PrintWriter pw, @Nullable String filterAttributionTag,
             @HistoricalOpsRequestFilter int filter, long nowElapsed, @NonNull Op op, long now,
             @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix) {
-        final int numFeatures = op.mFeatures.size();
-        for (int i = 0; i < numFeatures; i++) {
-            if ((filter & FILTER_BY_FEATURE_ID) != 0 && !Objects.equals(op.mFeatures.keyAt(i),
-                    filterFeatureId)) {
+        final int numAttributions = op.mAttributions.size();
+        for (int i = 0; i < numAttributions; i++) {
+            if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0 && !Objects.equals(
+                    op.mAttributions.keyAt(i), filterAttributionTag)) {
                 continue;
             }
 
-            pw.print(prefix + op.mFeatures.keyAt(i) + "=[\n");
-            dumpStatesLocked(pw, nowElapsed, op, op.mFeatures.keyAt(i), now, sdf, date,
+            pw.print(prefix + op.mAttributions.keyAt(i) + "=[\n");
+            dumpStatesLocked(pw, nowElapsed, op, op.mAttributions.keyAt(i), now, sdf, date,
                     prefix + "  ");
             pw.print(prefix + "]\n");
         }
     }
 
     private void dumpStatesLocked(@NonNull PrintWriter pw, long nowElapsed, @NonNull Op op,
-            @Nullable String featureId, long now, @NonNull SimpleDateFormat sdf,
+            @Nullable String attributionTag, long now, @NonNull SimpleDateFormat sdf,
             @NonNull Date date, @NonNull String prefix) {
 
-        final OpFeatureEntry entry = op.createSingleFeatureEntryLocked(
-                featureId).getFeatures().get(featureId);
+        final AttributedOpEntry entry = op.createSingleAttributionEntryLocked(
+                attributionTag).getAttributedOpEntries().get(attributionTag);
 
         final ArraySet<Long> keys = entry.collectKeys();
 
@@ -4887,11 +4857,11 @@
             final OpEventProxyInfo proxy = entry.getLastProxyInfo(uidState, uidState, flags);
 
             String proxyPkg = null;
-            String proxyFeatureId = null;
+            String proxyAttributionTag = null;
             int proxyUid = Process.INVALID_UID;
             if (proxy != null) {
                 proxyPkg = proxy.getPackageName();
-                proxyFeatureId = proxy.getFeatureId();
+                proxyAttributionTag = proxy.getAttributionTag();
                 proxyUid = proxy.getUid();
             }
 
@@ -4915,8 +4885,8 @@
                     pw.print(proxyUid);
                     pw.print(", pkg=");
                     pw.print(proxyPkg);
-                    pw.print(", feature=");
-                    pw.print(proxyFeatureId);
+                    pw.print(", attributionTag=");
+                    pw.print(proxyAttributionTag);
                     pw.print("]");
                 }
                 pw.println();
@@ -4937,21 +4907,21 @@
                     pw.print(proxyUid);
                     pw.print(", pkg=");
                     pw.print(proxyPkg);
-                    pw.print(", feature=");
-                    pw.print(proxyFeatureId);
+                    pw.print(", attributionTag=");
+                    pw.print(proxyAttributionTag);
                     pw.print("]");
                 }
                 pw.println();
             }
         }
 
-        final FeatureOp featureOp = op.mFeatures.get(featureId);
-        if (featureOp.isRunning()) {
+        final AttributedOp attributedOp = op.mAttributions.get(attributionTag);
+        if (attributedOp.isRunning()) {
             long earliestElapsedTime = Long.MAX_VALUE;
             long maxNumStarts = 0;
-            int numInProgressEvents = featureOp.mInProgressEvents.size();
+            int numInProgressEvents = attributedOp.mInProgressEvents.size();
             for (int i = 0; i < numInProgressEvents; i++) {
-                InProgressStartOpEvent event = featureOp.mInProgressEvents.valueAt(i);
+                InProgressStartOpEvent event = attributedOp.mInProgressEvents.valueAt(i);
 
                 earliestElapsedTime = Math.min(earliestElapsedTime, event.getStartElapsedTime());
                 maxNumStarts = Math.max(maxNumStarts, event.numUnfinishedStarts);
@@ -4974,7 +4944,7 @@
 
         int dumpOp = OP_NONE;
         String dumpPackage = null;
-        String dumpFeatureId = null;
+        String dumpAttributionTag = null;
         int dumpUid = Process.INVALID_UID;
         int dumpMode = -1;
         boolean dumpWatchers = false;
@@ -5021,14 +4991,14 @@
                     }
                     dumpUid = UserHandle.getAppId(dumpUid);
                     dumpFilter |= FILTER_BY_UID;
-                } else if ("--featureId".equals(arg)) {
+                } else if ("--attributionTag".equals(arg)) {
                     i++;
                     if (i >= args.length) {
-                        pw.println("No argument for --featureId option");
+                        pw.println("No argument for --attributionTag option");
                         return;
                     }
-                    dumpFeatureId = args[i];
-                    dumpFilter |= FILTER_BY_FEATURE_ID;
+                    dumpAttributionTag = args[i];
+                    dumpFilter |= FILTER_BY_ATTRIBUTION_TAG;
                 } else if ("--mode".equals(arg)) {
                     i++;
                     if (i >= args.length) {
@@ -5367,8 +5337,8 @@
                             pw.print("="); pw.print(AppOpsManager.modeToName(mode));
                         }
                         pw.println("): ");
-                        dumpStatesLocked(pw, dumpFeatureId, dumpFilter, nowElapsed, op, now, sdf,
-                                date, "        ");
+                        dumpStatesLocked(pw, dumpAttributionTag, dumpFilter, nowElapsed, op, now,
+                                sdf, date, "        ");
                     }
                 }
             }
@@ -5467,7 +5437,7 @@
 
         // Must not hold the appops lock
         if (dumpHistory && !dumpWatchers) {
-            mHistoricalRegistry.dump("  ", pw, dumpUid, dumpPackage, dumpFeatureId, dumpOp,
+            mHistoricalRegistry.dump("  ", pw, dumpUid, dumpPackage, dumpAttributionTag, dumpOp,
                     dumpFilter);
         }
     }
@@ -5572,9 +5542,9 @@
         if (resolvedPackageName == null) {
             return false;
         }
-        // TODO moltmann: Allow to check for feature op activeness
+        // TODO moltmann: Allow to check for attribution op activeness
         synchronized (AppOpsService.this) {
-            Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, null, false, false);
+            Ops pkgOps = getOpsLocked(uid, resolvedPackageName, null, null, false);
             if (pkgOps == null) {
                 return false;
             }
@@ -5633,7 +5603,7 @@
      * Report runtime access to AppOp together with message (including stack trace)
      *
      * @param packageName The package which reported the op
-     * @param notedAppOp contains code of op and featureId provided by developer
+     * @param notedAppOp contains code of op and attributionTag provided by developer
      * @param message Message describing AppOp access (can be stack trace)
      *
      * @return Config for future sampling to reduce amount of reporting
@@ -5655,7 +5625,7 @@
 
             reportRuntimeAppOpAccessMessageInternalLocked(uid, packageName,
                     AppOpsManager.strOpToOp(notedAppOp.getOp()),
-                    notedAppOp.getFeatureId(), message);
+                    notedAppOp.getAttributionTag(), message);
 
             return new MessageSamplingConfig(mSampledAppOpCode, mAcceptableLeftDistance,
                     Instant.now().plus(1, ChronoUnit.HOURS).toEpochMilli());
@@ -5668,17 +5638,18 @@
      * @param uid Uid of the package which reported the op
      * @param packageName The package which reported the op
      * @param opCode Code of AppOp
-     * @param featureId FeautreId of AppOp reported
+     * @param attributionTag FeautreId of AppOp reported
      * @param message Message describing AppOp access (can be stack trace)
      */
     private void reportRuntimeAppOpAccessMessageAsyncLocked(int uid,
-            @NonNull String packageName, int opCode, @Nullable String featureId,
+            @NonNull String packageName, int opCode, @Nullable String attributionTag,
             @NonNull String message) {
         switchPackageIfRarelyUsedLocked(packageName);
         if (!Objects.equals(mSampledPackage, packageName)) {
             return;
         }
-        reportRuntimeAppOpAccessMessageInternalLocked(uid, packageName, opCode, featureId, message);
+        reportRuntimeAppOpAccessMessageInternalLocked(uid, packageName, opCode, attributionTag,
+                message);
     }
 
     /**
@@ -5686,7 +5657,7 @@
      * reporting uniformly at random across all received messages.
      */
     private void reportRuntimeAppOpAccessMessageInternalLocked(int uid,
-            @NonNull String packageName, int opCode, @Nullable String featureId,
+            @NonNull String packageName, int opCode, @Nullable String attributionTag,
             @NonNull String message) {
         int newLeftDistance = AppOpsManager.leftCircularDistance(opCode,
                 mSampledAppOpCode, _NUM_OP);
@@ -5703,7 +5674,7 @@
         mMessagesCollectedCount += 1.0f;
         if (ThreadLocalRandom.current().nextFloat() <= 1.0f / mMessagesCollectedCount) {
             mCollectedRuntimePermissionMessage = new RuntimeAppOpAccessMessage(uid, opCode,
-                    packageName, featureId, message, mSamplingStrategy);
+                    packageName, attributionTag, message, mSamplingStrategy);
         }
         return;
     }
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index cd450d4..ed45069 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -15,7 +15,7 @@
  */
 package com.android.server.appop;
 
-import static android.app.AppOpsManager.FILTER_BY_FEATURE_ID;
+import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG;
 import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
 import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
 import static android.app.AppOpsManager.FILTER_BY_UID;
@@ -23,7 +23,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
-import android.app.AppOpsManager.HistoricalFeatureOps;
 import android.app.AppOpsManager.HistoricalMode;
 import android.app.AppOpsManager.HistoricalOp;
 import android.app.AppOpsManager.HistoricalOps;
@@ -282,7 +281,7 @@
     }
 
     void dump(String prefix, PrintWriter pw, int filterUid, @Nullable String filterPackage,
-            @Nullable String filterFeatureId, int filterOp,
+            @Nullable String filterAttributionTag, int filterOp,
             @HistoricalOpsRequestFilter int filter) {
         if (!isApiEnabled()) {
             return;
@@ -298,7 +297,7 @@
                 pw.println(AppOpsManager.historicalModeToString(mMode));
 
                 final StringDumpVisitor visitor = new StringDumpVisitor(prefix + "  ",
-                        pw, filterUid, filterPackage, filterFeatureId, filterOp, filter);
+                        pw, filterUid, filterPackage, filterAttributionTag, filterOp, filter);
                 final long nowMillis = System.currentTimeMillis();
 
                 // Dump in memory state first
@@ -338,7 +337,7 @@
     }
 
     void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName,
-            @Nullable String featureId, @Nullable String[] opNames,
+            @Nullable String attributionTag, @Nullable String[] opNames,
             @HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis,
             @OpFlags int flags, @NonNull RemoteCallback callback) {
         if (!isApiEnabled()) {
@@ -354,7 +353,7 @@
                     return;
                 }
                 final HistoricalOps result = new HistoricalOps(beginTimeMillis, endTimeMillis);
-                mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, featureId,
+                mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, attributionTag,
                         opNames, filter, beginTimeMillis, endTimeMillis, flags);
                 final Bundle payload = new Bundle();
                 payload.putParcelable(AppOpsManager.KEY_HISTORICAL_OPS, result);
@@ -363,7 +362,7 @@
         }
     }
 
-    void getHistoricalOps(int uid, @NonNull String packageName, @Nullable String featureId,
+    void getHistoricalOps(int uid, @NonNull String packageName, @Nullable String attributionTag,
             @Nullable String[] opNames, @HistoricalOpsRequestFilter int filter,
             long beginTimeMillis, long endTimeMillis, @OpFlags int flags,
             @NonNull RemoteCallback callback) {
@@ -401,7 +400,7 @@
                         || inMemoryAdjEndTimeMillis <= currentOps.getBeginTimeMillis())) {
                     // Some of the current batch falls into the query, so extract that.
                     final HistoricalOps currentOpsCopy = new HistoricalOps(currentOps);
-                    currentOpsCopy.filter(uid, packageName, featureId, opNames, filter,
+                    currentOpsCopy.filter(uid, packageName, attributionTag, opNames, filter,
                             inMemoryAdjBeginTimeMillis, inMemoryAdjEndTimeMillis);
                     result.merge(currentOpsCopy);
                 }
@@ -421,7 +420,7 @@
                         - onDiskAndInMemoryOffsetMillis, 0);
                 final long onDiskAdjEndTimeMillis = Math.max(inMemoryAdjEndTimeMillis
                         - onDiskAndInMemoryOffsetMillis, 0);
-                mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, featureId,
+                mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, attributionTag,
                         opNames, filter, onDiskAdjBeginTimeMillis, onDiskAdjEndTimeMillis, flags);
             }
 
@@ -436,7 +435,7 @@
     }
 
     void incrementOpAccessedCount(int op, int uid, @NonNull String packageName,
-            @Nullable String featureId, @UidState int uidState, @OpFlags int flags) {
+            @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags) {
         synchronized (mInMemoryLock) {
             if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
                 if (!isPersistenceInitializedMLocked()) {
@@ -445,13 +444,13 @@
                 }
                 getUpdatedPendingHistoricalOpsMLocked(
                         System.currentTimeMillis()).increaseAccessCount(op, uid, packageName,
-                        featureId, uidState, flags, 1);
+                        attributionTag, uidState, flags, 1);
             }
         }
     }
 
     void incrementOpRejected(int op, int uid, @NonNull String packageName,
-            @Nullable String featureId, @UidState int uidState, @OpFlags int flags) {
+            @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags) {
         synchronized (mInMemoryLock) {
             if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
                 if (!isPersistenceInitializedMLocked()) {
@@ -460,13 +459,13 @@
                 }
                 getUpdatedPendingHistoricalOpsMLocked(
                         System.currentTimeMillis()).increaseRejectCount(op, uid, packageName,
-                        featureId, uidState, flags, 1);
+                        attributionTag, uidState, flags, 1);
             }
         }
     }
 
     void increaseOpAccessDuration(int op, int uid, @NonNull String packageName,
-            @Nullable String featureId, @UidState int uidState, @OpFlags int flags,
+            @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
             long increment) {
         synchronized (mInMemoryLock) {
             if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
@@ -476,7 +475,7 @@
                 }
                 getUpdatedPendingHistoricalOpsMLocked(
                         System.currentTimeMillis()).increaseAccessDuration(op, uid, packageName,
-                        featureId, uidState, flags, increment);
+                        attributionTag, uidState, flags, increment);
             }
         }
     }
@@ -728,7 +727,7 @@
         private static final String TAG_OPS = "ops";
         private static final String TAG_UID = "uid";
         private static final String TAG_PACKAGE = "pkg";
-        private static final String TAG_FEATURE = "ftr";
+        private static final String TAG_ATTRIBUTION = "ftr";
         private static final String TAG_OP = "op";
         private static final String TAG_STATE = "st";
 
@@ -807,9 +806,9 @@
 
         @Nullable List<HistoricalOps> readHistoryRawDLocked() {
             return collectHistoricalOpsBaseDLocked(Process.INVALID_UID /*filterUid*/,
-                    null /*filterPackageName*/, null /*filterFeatureId*/, null /*filterOpNames*/,
-                    0 /*filter*/, 0 /*filterBeginTimeMills*/, Long.MAX_VALUE /*filterEndTimeMills*/,
-                    AppOpsManager.OP_FLAGS_ALL);
+                    null /*filterPackageName*/, null /*filterAttributionTag*/,
+                    null /*filterOpNames*/, 0 /*filter*/, 0 /*filterBeginTimeMills*/,
+                    Long.MAX_VALUE /*filterEndTimeMills*/, AppOpsManager.OP_FLAGS_ALL);
         }
 
         @Nullable List<HistoricalOps> readHistoryDLocked() {
@@ -861,13 +860,13 @@
             return 0;
         }
 
-        private void collectHistoricalOpsDLocked(@NonNull HistoricalOps currentOps,
-                int filterUid, @Nullable String filterPackageName, @Nullable String filterFeatureId,
+        private void collectHistoricalOpsDLocked(@NonNull HistoricalOps currentOps, int filterUid,
+                @Nullable String filterPackageName, @Nullable String filterAttributionTag,
                 @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
                 long filterBeingMillis, long filterEndMillis, @OpFlags int filterFlags) {
             final List<HistoricalOps> readOps = collectHistoricalOpsBaseDLocked(filterUid,
-                    filterPackageName, filterFeatureId, filterOpNames, filter, filterBeingMillis,
-                    filterEndMillis, filterFlags);
+                    filterPackageName, filterAttributionTag, filterOpNames, filter,
+                    filterBeingMillis, filterEndMillis, filterFlags);
             if (readOps != null) {
                 final int readCount = readOps.size();
                 for (int i = 0; i < readCount; i++) {
@@ -877,8 +876,8 @@
              }
         }
 
-        private @Nullable LinkedList<HistoricalOps> collectHistoricalOpsBaseDLocked(
-                int filterUid, @Nullable String filterPackageName, @Nullable String filterFeatureId,
+        private @Nullable LinkedList<HistoricalOps> collectHistoricalOpsBaseDLocked(int filterUid,
+                @Nullable String filterPackageName, @Nullable String filterAttributionTag,
                 @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
                 long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags) {
             File baseDir = null;
@@ -892,7 +891,7 @@
                 final Set<String> historyFiles = getHistoricalFileNames(baseDir);
                 final long[] globalContentOffsetMillis = {0};
                 final LinkedList<HistoricalOps> ops = collectHistoricalOpsRecursiveDLocked(
-                        baseDir, filterUid, filterPackageName, filterFeatureId, filterOpNames,
+                        baseDir, filterUid, filterPackageName, filterAttributionTag, filterOpNames,
                         filter, filterBeginTimeMillis, filterEndTimeMillis, filterFlags,
                         globalContentOffsetMillis, null /*outOps*/, 0 /*depth*/, historyFiles);
                 if (DEBUG) {
@@ -909,7 +908,7 @@
 
         private @Nullable LinkedList<HistoricalOps> collectHistoricalOpsRecursiveDLocked(
                 @NonNull File baseDir, int filterUid, @Nullable String filterPackageName,
-                @Nullable String filterFeatureId, @Nullable String[] filterOpNames,
+                @Nullable String filterAttributionTag, @Nullable String[] filterOpNames,
                 @HistoricalOpsRequestFilter int filter, long filterBeginTimeMillis,
                 long filterEndTimeMillis, @OpFlags int filterFlags,
                 @NonNull long[] globalContentOffsetMillis,
@@ -927,7 +926,7 @@
             // Read historical data at this level
             final List<HistoricalOps> readOps = readHistoricalOpsLocked(baseDir,
                     previousIntervalEndMillis, currentIntervalEndMillis, filterUid,
-                    filterPackageName, filterFeatureId, filterOpNames, filter,
+                    filterPackageName, filterAttributionTag, filterOpNames, filter,
                     filterBeginTimeMillis, filterEndTimeMillis, filterFlags,
                     globalContentOffsetMillis, depth, historyFiles);
             // Empty is a special signal to stop diving
@@ -937,7 +936,7 @@
 
             // Collect older historical data from subsequent levels
             outOps = collectHistoricalOpsRecursiveDLocked(baseDir, filterUid, filterPackageName,
-                    filterFeatureId, filterOpNames, filter, filterBeginTimeMillis,
+                    filterAttributionTag, filterOpNames, filter, filterBeginTimeMillis,
                     filterEndTimeMillis, filterFlags, globalContentOffsetMillis, outOps, depth + 1,
                     historyFiles);
 
@@ -1006,7 +1005,7 @@
             final List<HistoricalOps> existingOps = readHistoricalOpsLocked(oldBaseDir,
                     previousIntervalEndMillis, currentIntervalEndMillis,
                     Process.INVALID_UID /*filterUid*/, null /*filterPackageName*/,
-                    null /*filterFeatureId*/, null /*filterOpNames*/, 0 /*filter*/,
+                    null /*filterAttributionTag*/, null /*filterOpNames*/, 0 /*filter*/,
                     Long.MIN_VALUE /*filterBeginTimeMillis*/,
                     Long.MAX_VALUE /*filterEndTimeMillis*/, AppOpsManager.OP_FLAGS_ALL, null, depth,
                     null /*historyFiles*/);
@@ -1120,7 +1119,7 @@
 
         private @Nullable List<HistoricalOps> readHistoricalOpsLocked(File baseDir,
                 long intervalBeginMillis, long intervalEndMillis, int filterUid,
-                @Nullable String filterPackageName, @Nullable String filterFeatureId,
+                @Nullable String filterPackageName, @Nullable String filterAttributionTag,
                 @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
                 long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags,
                 @Nullable long[] cumulativeOverflowMillis, int depth,
@@ -1147,15 +1146,16 @@
                     return null;
                 }
             }
-            return readHistoricalOpsLocked(file, filterUid, filterPackageName, filterFeatureId,
+            return readHistoricalOpsLocked(file, filterUid, filterPackageName, filterAttributionTag,
                     filterOpNames, filter, filterBeginTimeMillis, filterEndTimeMillis, filterFlags,
                     cumulativeOverflowMillis);
         }
 
-        private @Nullable List<HistoricalOps> readHistoricalOpsLocked(@NonNull File file,
-                int filterUid, @Nullable String filterPackageName, @Nullable String filterFeatureId,
-                @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
-                long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags,
+        private @Nullable  List<HistoricalOps> readHistoricalOpsLocked(@NonNull File file,
+                int filterUid, @Nullable String filterPackageName,
+                @Nullable String filterAttributionTag, @Nullable String[] filterOpNames,
+                @HistoricalOpsRequestFilter int filter, long filterBeginTimeMillis,
+                long filterEndTimeMillis, @OpFlags int filterFlags,
                 @Nullable long[] cumulativeOverflowMillis)
                 throws IOException, XmlPullParserException {
             if (DEBUG) {
@@ -1180,7 +1180,7 @@
                 while (XmlUtils.nextElementWithin(parser, depth)) {
                     if (TAG_OPS.equals(parser.getName())) {
                         final HistoricalOps ops = readeHistoricalOpsDLocked(parser, filterUid,
-                                filterPackageName, filterFeatureId, filterOpNames, filter,
+                                filterPackageName, filterAttributionTag, filterOpNames, filter,
                                 filterBeginTimeMillis, filterEndTimeMillis, filterFlags,
                                 cumulativeOverflowMillis);
                         if (ops == null) {
@@ -1215,7 +1215,7 @@
 
         private @Nullable HistoricalOps readeHistoricalOpsDLocked(
                 @NonNull XmlPullParser parser, int filterUid, @Nullable String filterPackageName,
-                @Nullable String filterFeatureId, @Nullable String[] filterOpNames,
+                @Nullable String filterAttributionTag, @Nullable String[] filterOpNames,
                 @HistoricalOpsRequestFilter int filter, long filterBeginTimeMillis,
                 long filterEndTimeMillis, @OpFlags int filterFlags,
                 @Nullable long[] cumulativeOverflowMillis)
@@ -1245,8 +1245,8 @@
             while (XmlUtils.nextElementWithin(parser, depth)) {
                 if (TAG_UID.equals(parser.getName())) {
                     final HistoricalOps returnedOps = readHistoricalUidOpsDLocked(ops, parser,
-                            filterUid, filterPackageName, filterFeatureId, filterOpNames, filter,
-                            filterFlags, filterScale);
+                            filterUid, filterPackageName, filterAttributionTag, filterOpNames,
+                            filter, filterFlags, filterScale);
                     if (ops == null) {
                         ops = returnedOps;
                     }
@@ -1260,7 +1260,7 @@
 
         private @Nullable HistoricalOps readHistoricalUidOpsDLocked(
                 @Nullable HistoricalOps ops, @NonNull XmlPullParser parser, int filterUid,
-                @Nullable String filterPackageName, @Nullable String filterFeatureId,
+                @Nullable String filterPackageName, @Nullable String filterAttributionTag,
                 @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
                 @OpFlags int filterFlags, double filterScale)
                 throws IOException, XmlPullParserException {
@@ -1272,8 +1272,8 @@
             final int depth = parser.getDepth();
             while (XmlUtils.nextElementWithin(parser, depth)) {
                 if (TAG_PACKAGE.equals(parser.getName())) {
-                    final HistoricalOps returnedOps = readHistoricalPackageOpsDLocked(ops,
-                            uid, parser, filterPackageName, filterFeatureId, filterOpNames, filter,
+                    final HistoricalOps returnedOps = readHistoricalPackageOpsDLocked(ops, uid,
+                            parser, filterPackageName, filterAttributionTag, filterOpNames, filter,
                             filterFlags, filterScale);
                     if (ops == null) {
                         ops = returnedOps;
@@ -1285,7 +1285,7 @@
 
         private @Nullable HistoricalOps readHistoricalPackageOpsDLocked(
                 @Nullable HistoricalOps ops, int uid, @NonNull XmlPullParser parser,
-                @Nullable String filterPackageName, @Nullable String filterFeatureId,
+                @Nullable String filterPackageName, @Nullable String filterAttributionTag,
                 @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
                 @OpFlags int filterFlags, double filterScale)
                 throws IOException, XmlPullParserException {
@@ -1296,9 +1296,9 @@
             }
             final int depth = parser.getDepth();
             while (XmlUtils.nextElementWithin(parser, depth)) {
-                if (TAG_FEATURE.equals(parser.getName())) {
-                    final HistoricalOps returnedOps = readHistoricalFeatureOpsDLocked(ops, uid,
-                            packageName, parser, filterFeatureId, filterOpNames, filter,
+                if (TAG_ATTRIBUTION.equals(parser.getName())) {
+                    final HistoricalOps returnedOps = readHistoricalAttributionOpsDLocked(ops, uid,
+                            packageName, parser, filterAttributionTag, filterOpNames, filter,
                             filterFlags, filterScale);
                     if (ops == null) {
                         ops = returnedOps;
@@ -1308,15 +1308,15 @@
             return ops;
         }
 
-        private @Nullable HistoricalOps readHistoricalFeatureOpsDLocked(@Nullable HistoricalOps ops,
-                int uid, String packageName, @NonNull XmlPullParser parser,
-                @Nullable String filterFeatureId, @Nullable String[] filterOpNames,
-                @HistoricalOpsRequestFilter int filter, @OpFlags int filterFlags,
-                double filterScale)
+        private @Nullable HistoricalOps readHistoricalAttributionOpsDLocked(
+                @Nullable HistoricalOps ops, int uid, String packageName,
+                @NonNull XmlPullParser parser, @Nullable String filterAttributionTag,
+                @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
+                @OpFlags int filterFlags, double filterScale)
                 throws IOException, XmlPullParserException {
-            final String featureId = XmlUtils.readStringAttribute(parser, ATTR_NAME);
-            if ((filter & FILTER_BY_FEATURE_ID) != 0 && !Objects.equals(filterFeatureId,
-                    featureId)) {
+            final String attributionTag = XmlUtils.readStringAttribute(parser, ATTR_NAME);
+            if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0 && !Objects.equals(filterAttributionTag,
+                    attributionTag)) {
                 XmlUtils.skipCurrentTag(parser);
                 return null;
             }
@@ -1324,7 +1324,8 @@
             while (XmlUtils.nextElementWithin(parser, depth)) {
                 if (TAG_OP.equals(parser.getName())) {
                     final HistoricalOps returnedOps = readHistoricalOpDLocked(ops, uid, packageName,
-                            featureId, parser, filterOpNames, filter, filterFlags, filterScale);
+                            attributionTag, parser, filterOpNames, filter, filterFlags,
+                            filterScale);
                     if (ops == null) {
                         ops = returnedOps;
                     }
@@ -1334,7 +1335,7 @@
         }
 
         private @Nullable HistoricalOps readHistoricalOpDLocked(@Nullable HistoricalOps ops,
-                int uid, @NonNull String packageName, @Nullable String featureId,
+                int uid, @NonNull String packageName, @Nullable String attributionTag,
                 @NonNull XmlPullParser parser, @Nullable String[] filterOpNames,
                 @HistoricalOpsRequestFilter int filter, @OpFlags int filterFlags,
                 double filterScale)
@@ -1349,7 +1350,7 @@
             while (XmlUtils.nextElementWithin(parser, depth)) {
                 if (TAG_STATE.equals(parser.getName())) {
                     final HistoricalOps returnedOps = readStateDLocked(ops, uid,
-                            packageName, featureId, op, parser, filterFlags, filterScale);
+                            packageName, attributionTag, op, parser, filterFlags, filterScale);
                     if (ops == null) {
                         ops = returnedOps;
                     }
@@ -1359,7 +1360,7 @@
         }
 
         private @Nullable HistoricalOps readStateDLocked(@Nullable HistoricalOps ops,
-                int uid, @NonNull String packageName, @Nullable String featureId, int op,
+                int uid, @NonNull String packageName, @Nullable String attributionTag, int op,
                 @NonNull XmlPullParser parser, @OpFlags int filterFlags, double filterScale)
                 throws IOException {
             final long key = XmlUtils.readLongAttribute(parser, ATTR_NAME);
@@ -1377,7 +1378,7 @@
                 if (ops == null) {
                     ops = new HistoricalOps(0, 0);
                 }
-                ops.increaseAccessCount(op, uid, packageName, featureId, uidState, flags,
+                ops.increaseAccessCount(op, uid, packageName, attributionTag, uidState, flags,
                         accessCount);
             }
             long rejectCount = XmlUtils.readLongAttribute(parser, ATTR_REJECT_COUNT, 0);
@@ -1389,7 +1390,7 @@
                 if (ops == null) {
                     ops = new HistoricalOps(0, 0);
                 }
-                ops.increaseRejectCount(op, uid, packageName, featureId, uidState, flags,
+                ops.increaseRejectCount(op, uid, packageName, attributionTag, uidState, flags,
                         rejectCount);
             }
             long accessDuration =  XmlUtils.readLongAttribute(parser, ATTR_ACCESS_DURATION, 0);
@@ -1401,7 +1402,7 @@
                 if (ops == null) {
                     ops = new HistoricalOps(0, 0);
                 }
-                ops.increaseAccessDuration(op, uid, packageName, featureId, uidState, flags,
+                ops.increaseAccessDuration(op, uid, packageName, attributionTag, uidState, flags,
                         accessDuration);
             }
             return ops;
@@ -1467,24 +1468,25 @@
                 @NonNull XmlSerializer serializer) throws IOException {
             serializer.startTag(null, TAG_PACKAGE);
             serializer.attribute(null, ATTR_NAME, packageOps.getPackageName());
-            final int numFeatures = packageOps.getFeatureCount();
-            for (int i = 0; i < numFeatures; i++) {
-                final HistoricalFeatureOps op = packageOps.getFeatureOpsAt(i);
-                writeHistoricalFeatureOpsDLocked(op, serializer);
+            final int numAttributions = packageOps.getAttributedOpsCount();
+            for (int i = 0; i < numAttributions; i++) {
+                final AppOpsManager.AttributedHistoricalOps op = packageOps.getAttributedOpsAt(i);
+                writeHistoricalAttributionOpsDLocked(op, serializer);
             }
             serializer.endTag(null, TAG_PACKAGE);
         }
 
-        private void writeHistoricalFeatureOpsDLocked(@NonNull HistoricalFeatureOps featureOps,
+        private void writeHistoricalAttributionOpsDLocked(
+                @NonNull AppOpsManager.AttributedHistoricalOps attributionOps,
                 @NonNull XmlSerializer serializer) throws IOException {
-            serializer.startTag(null, TAG_FEATURE);
-            XmlUtils.writeStringAttribute(serializer, ATTR_NAME, featureOps.getFeatureId());
-            final int opCount = featureOps.getOpCount();
+            serializer.startTag(null, TAG_ATTRIBUTION);
+            XmlUtils.writeStringAttribute(serializer, ATTR_NAME, attributionOps.getTag());
+            final int opCount = attributionOps.getOpCount();
             for (int i = 0; i < opCount; i++) {
-                final HistoricalOp op = featureOps.getOpAt(i);
+                final HistoricalOp op = attributionOps.getOpAt(i);
                 writeHistoricalOpDLocked(op, serializer);
             }
-            serializer.endTag(null, TAG_FEATURE);
+            serializer.endTag(null, TAG_ATTRIBUTION);
         }
 
         private void writeHistoricalOpDLocked(@NonNull HistoricalOp op,
@@ -1718,29 +1720,29 @@
         private final @NonNull String mOpsPrefix;
         private final @NonNull String mUidPrefix;
         private final @NonNull String mPackagePrefix;
-        private final @NonNull String mFeaturePrefix;
+        private final @NonNull String mAttributionPrefix;
         private final @NonNull String mEntryPrefix;
         private final @NonNull String mUidStatePrefix;
         private final @NonNull PrintWriter mWriter;
         private final int mFilterUid;
         private final String mFilterPackage;
-        private final String mFilterFeatureId;
+        private final String mFilterAttributionTag;
         private final int mFilterOp;
         private final @HistoricalOpsRequestFilter int mFilter;
 
         StringDumpVisitor(@NonNull String prefix, @NonNull PrintWriter writer, int filterUid,
-                @Nullable String filterPackage, @Nullable String filterFeatureId, int filterOp,
+                @Nullable String filterPackage, @Nullable String filterAttributionTag, int filterOp,
                 @HistoricalOpsRequestFilter int filter) {
             mOpsPrefix = prefix + "  ";
             mUidPrefix = mOpsPrefix + "  ";
             mPackagePrefix = mUidPrefix + "  ";
-            mFeaturePrefix = mPackagePrefix + "  ";
-            mEntryPrefix = mFeaturePrefix + "  ";
+            mAttributionPrefix = mPackagePrefix + "  ";
+            mEntryPrefix = mAttributionPrefix + "  ";
             mUidStatePrefix = mEntryPrefix + "  ";
             mWriter = writer;
             mFilterUid = filterUid;
             mFilterPackage = filterPackage;
-            mFilterFeatureId = filterFeatureId;
+            mFilterAttributionTag = filterAttributionTag;
             mFilterOp = filterOp;
             mFilter = filter;
         }
@@ -1791,14 +1793,14 @@
         }
 
         @Override
-        public void visitHistoricalFeatureOps(HistoricalFeatureOps ops) {
-            if ((mFilter & FILTER_BY_FEATURE_ID) != 0 && !Objects.equals(mFilterPackage,
-                    ops.getFeatureId())) {
+        public void visitHistoricalAttributionOps(AppOpsManager.AttributedHistoricalOps ops) {
+            if ((mFilter & FILTER_BY_ATTRIBUTION_TAG) != 0 && !Objects.equals(mFilterPackage,
+                    ops.getTag())) {
                 return;
             }
-            mWriter.print(mFeaturePrefix);
-            mWriter.print("Feature ");
-            mWriter.print(ops.getFeatureId());
+            mWriter.print(mAttributionPrefix);
+            mWriter.print("Attribution ");
+            mWriter.print(ops.getTag());
             mWriter.println(":");
         }
 
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index 7bdeb59..2e9818d 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -151,6 +151,15 @@
         return true;
     }
 
+    /**
+     * Checks whether a change has an override for a package.
+     * @param packageName name of the package
+     * @return true if there is such override
+     */
+    boolean hasOverride(String packageName) {
+        return mPackageOverrides != null && mPackageOverrides.containsKey(packageName);
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder("ChangeId(")
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 61bede9..aeaa1fe 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -247,11 +247,13 @@
             CompatChange c = mChanges.get(changeId);
             try {
                 if (c != null) {
-                    OverrideAllowedState allowedState =
-                            mOverrideValidator.getOverrideAllowedState(changeId, packageName);
-                    allowedState.enforce(changeId, packageName);
-                    overrideExists = true;
-                    c.removePackageOverride(packageName);
+                    overrideExists = c.hasOverride(packageName);
+                    if (overrideExists) {
+                        OverrideAllowedState allowedState =
+                                mOverrideValidator.getOverrideAllowedState(changeId, packageName);
+                        allowedState.enforce(changeId, packageName);
+                        c.removePackageOverride(packageName);
+                    }
                 }
             } catch (RemoteException e) {
                 // Should never occur, since validator is in the same process.
@@ -298,12 +300,14 @@
             for (int i = 0; i < mChanges.size(); ++i) {
                 try {
                     CompatChange change = mChanges.valueAt(i);
-                    OverrideAllowedState allowedState =
-                            mOverrideValidator.getOverrideAllowedState(change.getId(),
-                                                                       packageName);
-                    allowedState.enforce(change.getId(), packageName);
-                    if (change != null) {
-                        mChanges.valueAt(i).removePackageOverride(packageName);
+                    if (change.hasOverride(packageName)) {
+                        OverrideAllowedState allowedState =
+                                mOverrideValidator.getOverrideAllowedState(change.getId(),
+                                        packageName);
+                        allowedState.enforce(change.getId(), packageName);
+                        if (change != null) {
+                            mChanges.valueAt(i).removePackageOverride(packageName);
+                        }
                     }
                 } catch (RemoteException e) {
                     // Should never occur, since validator is in the same process.
@@ -314,6 +318,63 @@
         }
     }
 
+    private long[] getAllowedChangesAfterTargetSdkForPackage(String packageName,
+                                                             int targetSdkVersion)
+                    throws RemoteException {
+        LongArray allowed = new LongArray();
+        synchronized (mChanges) {
+            for (int i = 0; i < mChanges.size(); ++i) {
+                try {
+                    CompatChange change = mChanges.valueAt(i);
+                    if (change.getEnableAfterTargetSdk() != targetSdkVersion) {
+                        continue;
+                    }
+                    OverrideAllowedState allowedState =
+                            mOverrideValidator.getOverrideAllowedState(change.getId(),
+                                                                       packageName);
+                    if (allowedState.state == OverrideAllowedState.ALLOWED) {
+                        allowed.add(change.getId());
+                    }
+                } catch (RemoteException e) {
+                    // Should never occur, since validator is in the same process.
+                    throw new RuntimeException("Unable to call override validator!", e);
+                }
+            }
+        }
+        return allowed.toArray();
+    }
+
+    /**
+     * Enables all changes with enabledAfterTargetSdk == {@param targetSdkVersion} for
+     * {@param packageName}.
+     *
+     * @return The number of changes that were toggled.
+     */
+    int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion)
+            throws RemoteException {
+        long[] changes = getAllowedChangesAfterTargetSdkForPackage(packageName, targetSdkVersion);
+        for (long changeId : changes) {
+            addOverride(changeId, packageName, true);
+        }
+        return changes.length;
+    }
+
+
+    /**
+     * Disables all changes with enabledAfterTargetSdk == {@param targetSdkVersion} for
+     * {@param packageName}.
+     *
+     * @return The number of changes that were toggled.
+     */
+    int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion)
+            throws RemoteException {
+        long[] changes = getAllowedChangesAfterTargetSdkForPackage(packageName, targetSdkVersion);
+        for (long changeId : changes) {
+            addOverride(changeId, packageName, false);
+        }
+        return changes.length;
+    }
+
     boolean registerListener(long changeId, CompatChange.ChangeListener listener) {
         boolean alreadyKnown = true;
         synchronized (mChanges) {
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 821653a..8519b00 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -166,6 +166,26 @@
     }
 
     @Override
+    public int enableTargetSdkChanges(String packageName, int targetSdkVersion)
+            throws RemoteException, SecurityException {
+        checkCompatChangeOverridePermission();
+        int numChanges = mCompatConfig.enableTargetSdkChangesForPackage(packageName,
+                                                                        targetSdkVersion);
+        killPackage(packageName);
+        return numChanges;
+    }
+
+    @Override
+    public int disableTargetSdkChanges(String packageName, int targetSdkVersion)
+            throws RemoteException, SecurityException {
+        checkCompatChangeOverridePermission();
+        int numChanges = mCompatConfig.disableTargetSdkChangesForPackage(packageName,
+                                                                         targetSdkVersion);
+        killPackage(packageName);
+        return numChanges;
+    }
+
+    @Override
     public void clearOverrides(String packageName) throws RemoteException, SecurityException {
         checkCompatChangeOverridePermission();
         mCompatConfig.removePackageOverrides(packageName);
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 7c3cab1..20ffd9f 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -2563,7 +2563,7 @@
         public void exitIfOuterInterfaceIs(String interfaze) {
             if (interfaze.equals(mOuterInterface)) {
                 Log.i(TAG, "Legacy VPN is going down with " + interfaze);
-                exit();
+                exitVpnRunner();
             }
         }
 
@@ -2572,6 +2572,10 @@
         public void exitVpnRunner() {
             // We assume that everything is reset after stopping the daemons.
             interrupt();
+
+            // Always disconnect. This may be called again in cleanupVpnStateLocked() if
+            // exitVpnRunner() was called from exit(), but it will be a no-op.
+            agentDisconnect();
             try {
                 mContext.unregisterReceiver(mBroadcastReceiver);
             } catch (IllegalArgumentException e) {}
@@ -2794,7 +2798,7 @@
             } catch (Exception e) {
                 Log.i(TAG, "Aborting", e);
                 updateState(DetailedState.FAILED, e.getMessage());
-                exit();
+                exitVpnRunner();
             }
         }
 
diff --git a/services/core/java/com/android/server/location/CountryDetectorBase.java b/services/core/java/com/android/server/location/CountryDetectorBase.java
index b158388..682b104 100644
--- a/services/core/java/com/android/server/location/CountryDetectorBase.java
+++ b/services/core/java/com/android/server/location/CountryDetectorBase.java
@@ -31,7 +31,7 @@
  * @hide
  */
 public abstract class CountryDetectorBase {
-    private static final String FEATURE_ID = "CountryDetector";
+    private static final String ATTRIBUTION_TAG = "CountryDetector";
 
     protected final Handler mHandler;
     protected final Context mContext;
@@ -39,7 +39,7 @@
     protected Country mDetectedCountry;
 
     public CountryDetectorBase(Context context) {
-        mContext = context.createFeatureContext(FEATURE_ID);
+        mContext = context.createAttributionContext(ATTRIBUTION_TAG);
         mHandler = new Handler();
     }
 
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index b456737..2aa53cc 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -60,6 +60,8 @@
     private Connection mActiveConnection;
     private boolean mConnectionReady;
 
+    private RouteDiscoveryPreference mPendingDiscoveryPreference = null;
+
     MediaRoute2ProviderServiceProxy(@NonNull Context context, @NonNull ComponentName componentName,
             int userId) {
         super(componentName);
@@ -99,6 +101,8 @@
         if (mConnectionReady) {
             mActiveConnection.updateDiscoveryPreference(discoveryPreference);
             updateBinding();
+        } else {
+            mPendingDiscoveryPreference = discoveryPreference;
         }
     }
 
@@ -271,6 +275,10 @@
     private void onConnectionReady(Connection connection) {
         if (mActiveConnection == connection) {
             mConnectionReady = true;
+            if (mPendingDiscoveryPreference != null) {
+                updateDiscoveryPreference(mPendingDiscoveryPreference);
+                mPendingDiscoveryPreference = null;
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index ee5a4fe..4af31b0 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -306,9 +306,8 @@
     /** Data layer operation counters for splicing into other structures. */
     private NetworkStats mUidOperations = new NetworkStats(0L, 10);
 
-    /** Must be set in factory by calling #setHandler. */
-    private Handler mHandler;
-    private Handler.Callback mHandlerCallback;
+    @NonNull
+    private final Handler mHandler;
 
     private volatile boolean mSystemReady;
     private long mPersistThreshold = 2 * MB_IN_BYTES;
@@ -324,6 +323,9 @@
 
     private final static int DUMP_STATS_SESSION_COUNT = 20;
 
+    @NonNull
+    private final Dependencies mDeps;
+
     private static @NonNull File getDefaultSystemDir() {
         return new File(Environment.getDataDirectory(), "system");
     }
@@ -339,9 +341,24 @@
                 Clock.systemUTC());
     }
 
-    private static final class NetworkStatsHandler extends Handler {
-        NetworkStatsHandler(Looper looper, Handler.Callback callback) {
-            super(looper, callback);
+    private final class NetworkStatsHandler extends Handler {
+        NetworkStatsHandler(@NonNull Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_PERFORM_POLL: {
+                    performPoll(FLAG_PERSIST_ALL);
+                    break;
+                }
+                case MSG_PERFORM_POLL_REGISTER_ALERT: {
+                    performPoll(FLAG_PERSIST_NETWORK);
+                    registerGlobalAlert();
+                    break;
+                }
+            }
         }
     }
 
@@ -355,14 +372,10 @@
         NetworkStatsService service = new NetworkStatsService(context, networkManager, alarmManager,
                 wakeLock, getDefaultClock(), context.getSystemService(TelephonyManager.class),
                 new DefaultNetworkStatsSettings(context), new NetworkStatsFactory(),
-                new NetworkStatsObservers(), getDefaultSystemDir(), getDefaultBaseDir());
+                new NetworkStatsObservers(), getDefaultSystemDir(), getDefaultBaseDir(),
+                new Dependencies());
         service.registerLocalService();
 
-        HandlerThread handlerThread = new HandlerThread(TAG);
-        Handler.Callback callback = new HandlerCallback(service);
-        handlerThread.start();
-        Handler handler = new NetworkStatsHandler(handlerThread.getLooper(), callback);
-        service.setHandler(handler, callback);
         return service;
     }
 
@@ -373,7 +386,7 @@
             AlarmManager alarmManager, PowerManager.WakeLock wakeLock, Clock clock,
             TelephonyManager teleManager, NetworkStatsSettings settings,
             NetworkStatsFactory factory, NetworkStatsObservers statsObservers, File systemDir,
-            File baseDir) {
+            File baseDir, @NonNull Dependencies deps) {
         mContext = Objects.requireNonNull(context, "missing Context");
         mNetworkManager = Objects.requireNonNull(networkManager,
             "missing INetworkManagementService");
@@ -387,6 +400,26 @@
         mSystemDir = Objects.requireNonNull(systemDir, "missing systemDir");
         mBaseDir = Objects.requireNonNull(baseDir, "missing baseDir");
         mUseBpfTrafficStats = new File("/sys/fs/bpf/map_netd_app_uid_stats_map").exists();
+        mDeps = Objects.requireNonNull(deps, "missing Dependencies");
+
+        final HandlerThread handlerThread = mDeps.makeHandlerThread();
+        handlerThread.start();
+        mHandler = new NetworkStatsHandler(handlerThread.getLooper());
+    }
+
+    /**
+     * Dependencies of NetworkStatsService, for injection in tests.
+     */
+    // TODO: Move more stuff into dependencies object.
+    @VisibleForTesting
+    public static class Dependencies {
+        /**
+         * Create a HandlerThread to use in NetworkStatsService.
+         */
+        @NonNull
+        public HandlerThread makeHandlerThread() {
+            return new HandlerThread(TAG);
+        }
     }
 
     private void registerLocalService() {
@@ -394,12 +427,6 @@
                 new NetworkStatsManagerInternalImpl());
     }
 
-    @VisibleForTesting
-    void setHandler(Handler handler, Handler.Callback callback) {
-        mHandler = handler;
-        mHandlerCallback = callback;
-    }
-
     public void systemReady() {
         synchronized (mStatsLock) {
             mSystemReady = true;
@@ -1920,33 +1947,6 @@
 
     }
 
-    @VisibleForTesting
-    static class HandlerCallback implements Handler.Callback {
-        private final NetworkStatsService mService;
-
-        HandlerCallback(NetworkStatsService service) {
-            this.mService = service;
-        }
-
-        @Override
-        public boolean handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_PERFORM_POLL: {
-                    mService.performPoll(FLAG_PERSIST_ALL);
-                    return true;
-                }
-                case MSG_PERFORM_POLL_REGISTER_ALERT: {
-                    mService.performPoll(FLAG_PERSIST_NETWORK);
-                    mService.registerGlobalAlert();
-                    return true;
-                }
-                default: {
-                    return false;
-                }
-            }
-        }
-    }
-
     private void assertSystemReady() {
         if (!mSystemReady) {
             throw new IllegalStateException("System not ready");
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index ec9b37d..83da381 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -45,6 +45,7 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ResolveInfo;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -183,7 +184,8 @@
             String callingFeatureId,
             Intent intent,
             @UserIdInt int userId,
-            IBinder callingActivity) throws RemoteException {
+            IBinder callingActivity,
+            Bundle options) throws RemoteException {
         Objects.requireNonNull(callingPackage);
         Objects.requireNonNull(intent);
         Objects.requireNonNull(intent.getComponent(), "The intent must have a Component set");
@@ -226,7 +228,7 @@
                         launchIntent,
                         callingActivity,
                         /* startFlags= */ 0,
-                        /* options= */ null,
+                        options,
                         userId);
         logStartActivityByIntent(callingPackage);
     }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 33ef2d4..cdc3736 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -805,26 +805,30 @@
     public SessionInfo getSessionInfo(int sessionId) {
         synchronized (mSessions) {
             final PackageInstallerSession session = mSessions.get(sessionId);
-            return session != null ? session.generateInfo() : null;
+
+            return session != null
+                    ? session.generateInfoForCaller(true /*withIcon*/, Binder.getCallingUid())
+                    : null;
         }
     }
 
     @Override
     public ParceledListSlice<SessionInfo> getStagedSessions() {
-        return mStagingManager.getSessions();
+        return mStagingManager.getSessions(Binder.getCallingUid());
     }
 
     @Override
     public ParceledListSlice<SessionInfo> getAllSessions(int userId) {
+        final int callingUid = Binder.getCallingUid();
         mPermissionManager.enforceCrossUserPermission(
-                Binder.getCallingUid(), userId, true, false, "getAllSessions");
+                callingUid, userId, true, false, "getAllSessions");
 
         final List<SessionInfo> result = new ArrayList<>();
         synchronized (mSessions) {
             for (int i = 0; i < mSessions.size(); i++) {
                 final PackageInstallerSession session = mSessions.valueAt(i);
                 if (session.userId == userId && !session.hasParentSessionId()) {
-                    result.add(session.generateInfo(false));
+                    result.add(session.generateInfoForCaller(false, callingUid));
                 }
             }
         }
@@ -842,7 +846,8 @@
             for (int i = 0; i < mSessions.size(); i++) {
                 final PackageInstallerSession session = mSessions.valueAt(i);
 
-                SessionInfo info = session.generateInfo(false);
+                SessionInfo info =
+                        session.generateInfoForCaller(false /*withIcon*/, Process.SYSTEM_UID);
                 if (Objects.equals(info.getInstallerPackageName(), installerPackageName)
                         && session.userId == userId && !session.hasParentSessionId()) {
                     result.add(info);
@@ -1302,7 +1307,10 @@
             session.markUpdated();
             writeSessionsAsync();
             if (mOkToSendBroadcasts) {
-                mPm.sendSessionUpdatedBroadcast(session.generateInfo(false),
+                // we don't scrub the data here as this is sent only to the installer several
+                // privileged system packages
+                mPm.sendSessionUpdatedBroadcast(
+                        session.generateInfoForCaller(false/*icon*/, Process.SYSTEM_UID),
                         session.userId);
             }
         }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 97aa79d..483f83e 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -557,11 +557,41 @@
         }
     }
 
-    public SessionInfo generateInfo() {
-        return generateInfo(true);
+    /**
+     * Returns {@code true} if the {@link SessionInfo} object should be produced with potentially
+     * sensitive data scrubbed from its fields.
+     *
+     * @param callingUid the uid of the caller; the recipient of the {@link SessionInfo} that may
+     *                   need to be scrubbed
+     */
+    private boolean shouldScrubData(int callingUid) {
+        return !(callingUid < Process.FIRST_APPLICATION_UID || getInstallerUid() == callingUid);
     }
 
-    public SessionInfo generateInfo(boolean includeIcon) {
+    /**
+     * Generates a {@link SessionInfo} object for the provided uid. This may result in some fields
+     * that may contain sensitive info being filtered.
+     *
+     * @param includeIcon true if the icon should be included in the object
+     * @param callingUid the uid of the caller; the recipient of the {@link SessionInfo} that may
+     *                   need to be scrubbed
+     * @see #shouldScrubData(int)
+     */
+    public SessionInfo generateInfoForCaller(boolean includeIcon, int callingUid) {
+        return generateInfoInternal(includeIcon, shouldScrubData(callingUid));
+    }
+
+    /**
+     * Generates a {@link SessionInfo} object to ensure proper hiding of sensitive fields.
+     *
+     * @param includeIcon true if the icon should be included in the object
+     * @see #generateInfoForCaller(boolean, int)
+     */
+    public SessionInfo generateInfoScrubbed(boolean includeIcon) {
+        return generateInfoInternal(includeIcon, true /*scrubData*/);
+    }
+
+    private SessionInfo generateInfoInternal(boolean includeIcon, boolean scrubData) {
         final SessionInfo info = new SessionInfo();
         synchronized (mLock) {
             info.sessionId = sessionId;
@@ -584,9 +614,13 @@
             info.appLabel = params.appLabel;
 
             info.installLocation = params.installLocation;
-            info.originatingUri = params.originatingUri;
+            if (!scrubData) {
+                info.originatingUri = params.originatingUri;
+            }
             info.originatingUid = params.originatingUid;
-            info.referrerUri = params.referrerUri;
+            if (!scrubData) {
+                info.referrerUri = params.referrerUri;
+            }
             info.grantedRuntimePermissions = params.grantedRuntimePermissions;
             info.whitelistedRestrictedPermissions = params.whitelistedRestrictedPermissions;
             info.installFlags = params.installFlags;
@@ -2664,7 +2698,7 @@
         final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING);
         if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()
                 && (params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
-            mPm.sendSessionCommitBroadcast(generateInfo(), userId);
+            mPm.sendSessionCommitBroadcast(generateInfoScrubbed(true /*icon*/), userId);
         }
 
         mCallback.onSessionFinished(this, success);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 799ce65..61bf5f0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -11514,8 +11514,8 @@
                             "Static shared libs cannot declare permission groups");
                 }
 
-                // Static shared libs cannot declare features
-                if (!pkg.getFeatures().isEmpty()) {
+                // Static shared libs cannot declare attributions
+                if (!pkg.getAttributions().isEmpty()) {
                     throw new PackageManagerException(
                             "Static shared libs cannot declare features");
                 }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 2d16854..62541ab 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -5450,7 +5450,7 @@
                         packagePermissions, sharedUserPermissions);
             }
 
-            mPersistence.writeAsUser(runtimePermissions, UserHandle.of(userId));
+            mPersistence.writeForUser(runtimePermissions, UserHandle.of(userId));
         }
 
         @NonNull
@@ -5504,12 +5504,12 @@
         }
 
         public void deleteUserRuntimePermissionsFile(int userId) {
-            mPersistence.deleteAsUser(UserHandle.of(userId));
+            mPersistence.deleteForUser(UserHandle.of(userId));
         }
 
         @GuardedBy("Settings.this.mLock")
         public void readStateForUserSyncLPr(int userId) {
-            RuntimePermissionsState runtimePermissions = mPersistence.readAsUser(UserHandle.of(
+            RuntimePermissionsState runtimePermissions = mPersistence.readForUser(UserHandle.of(
                     userId));
             if (runtimePermissions == null) {
                 readLegacyStateForUserSyncLPr(userId);
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 83fe556..342c907 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -128,11 +128,12 @@
         }
     }
 
-    ParceledListSlice<PackageInstaller.SessionInfo> getSessions() {
+    ParceledListSlice<PackageInstaller.SessionInfo> getSessions(int callingUid) {
         final List<PackageInstaller.SessionInfo> result = new ArrayList<>();
         synchronized (mStagedSessions) {
             for (int i = 0; i < mStagedSessions.size(); i++) {
-                result.add(mStagedSessions.valueAt(i).generateInfo(false));
+                final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i);
+                result.add(stagedSession.generateInfoForCaller(false /*icon*/, callingUid));
             }
         }
         return new ParceledListSlice<>(result);
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 d3f668c..0e294f70 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -391,8 +391,9 @@
         ArrayMap<String, ProcessInfo> retProcs = new ArrayMap<>(numProcs);
         for (String key : procs.keySet()) {
             ParsedProcess proc = procs.get(key);
-            retProcs.put(proc.getName(), new ProcessInfo(proc.getName(),
-                    new ArraySet<>(proc.getDeniedPermissions())));
+            retProcs.put(proc.getName(),
+                    new ProcessInfo(proc.getName(), new ArraySet<>(proc.getDeniedPermissions()),
+                            proc.getEnableGwpAsan()));
         }
         return retProcs;
     }
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
index 7929579..46b08df 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
@@ -25,7 +25,7 @@
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.parsing.ParsingPackageRead;
-import android.content.pm.parsing.component.ParsedFeature;
+import android.content.pm.parsing.component.ParsedAttribution;
 import android.content.pm.parsing.component.ParsedIntentInfo;
 import android.content.pm.parsing.component.ParsedPermissionGroup;
 import android.os.Bundle;
@@ -147,7 +147,7 @@
     List<ParsedPermissionGroup> getPermissionGroups();
 
     @NonNull
-    List<ParsedFeature> getFeatures();
+    List<ParsedAttribution> getAttributions();
 
     /**
      * Used to determine the default preferred handler of an {@link Intent}.
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index d589353..161f304 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -32,6 +32,7 @@
 import android.app.AppOpsManagerInternal;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -173,6 +174,65 @@
         } catch (RemoteException doesNotHappen) {
             Slog.wtf(LOG_TAG, "Cannot set up app-ops listener");
         }
+
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        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 =
+                    new HashMap<>();
+
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                boolean hasSetupRun = true;
+                try {
+                    hasSetupRun = Settings.Secure.getInt(getContext().getContentResolver(),
+                            Settings.Secure.USER_SETUP_COMPLETE) != 0;
+                } catch (Settings.SettingNotFoundException e) {
+                    // Ignore error, assume setup has run
+                }
+                int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+                // If there is no valid package for the given UID, return immediately
+                if (packageManagerInternal.getPackage(uid) == null) {
+                    return;
+                }
+
+                if (hasSetupRun) {
+                    if (!mUserSetupUids.isEmpty()) {
+                        synchronized (mUserSetupUids) {
+                            for (int i = mUserSetupUids.size() - 1; i >= 0; i--) {
+                                updateUid(mUserSetupUids.get(i));
+                            }
+                            mUserSetupUids.clear();
+                        }
+                    }
+                    updateUid(uid);
+                } else {
+                    synchronized (mUserSetupUids) {
+                        if (!mUserSetupUids.contains(uid)) {
+                            mUserSetupUids.add(uid);
+                        }
+                    }
+                }
+            }
+
+            private void updateUid(int uid) {
+                UserHandle user = UserHandle.getUserHandleForUid(uid);
+                PermissionControllerManager manager = mPermControllerManagers.get(user);
+                if (manager == null) {
+                    manager = new PermissionControllerManager(
+                            getUserContext(getContext(), user), FgThread.getHandler());
+                    mPermControllerManagers.put(user, manager);
+                }
+                manager.updateUserSensitiveForApp(uid);
+            }
+        }, UserHandle.ALL, intentFilter, null, null);
+         */
     }
 
     /**
@@ -182,7 +242,6 @@
      * {@link AppOpsManager#sOpToSwitch share an op} to control the access.
      *
      * @param permission The permission
-     *
      * @return The op that controls the access of the permission
      */
     private static int getSwitchOp(@NonNull String permission) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 1b5cc6a..64edacd43 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5202,7 +5202,7 @@
                     if (dock != null) {
                         int result = ActivityTaskManager.getService()
                                 .startActivityAsUser(null, mContext.getBasePackageName(),
-                                        mContext.getFeatureId(), dock,
+                                        mContext.getAttributionTag(), dock,
                                         dock.resolveTypeIfNeeded(mContext.getContentResolver()),
                                         null, null, 0,
                                         ActivityManager.START_FLAG_ONLY_IF_NEEDED,
@@ -5214,7 +5214,7 @@
                 }
                 int result = ActivityTaskManager.getService()
                         .startActivityAsUser(null, mContext.getBasePackageName(),
-                                mContext.getFeatureId(), mHomeIntent,
+                                mContext.getAttributionTag(), mHomeIntent,
                                 mHomeIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
                                 null, null, 0,
                                 ActivityManager.START_FLAG_ONLY_IF_NEEDED,
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
index 97ce6bd..b33dc8f 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -364,12 +364,12 @@
                     (Map<String, Set<String>>) (Map<String, ?>) snapshotRolesLocked());
         }
 
-        mPersistence.writeAsUser(roles, UserHandle.of(mUserId));
+        mPersistence.writeForUser(roles, UserHandle.of(mUserId));
     }
 
     private void readFile() {
         synchronized (mLock) {
-            RolesState roles = mPersistence.readAsUser(UserHandle.of(mUserId));
+            RolesState roles = mPersistence.readForUser(UserHandle.of(mUserId));
             if (roles == null) {
                 readLegacyFileLocked();
                 scheduleWriteFileLocked();
@@ -545,7 +545,7 @@
                 throw new IllegalStateException("This RoleUserState has already been destroyed");
             }
             mWriteHandler.removeCallbacksAndMessages(null);
-            mPersistence.deleteAsUser(UserHandle.of(mUserId));
+            mPersistence.deleteForUser(UserHandle.of(mUserId));
             mDestroyed = true;
         }
     }
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 98579af5..7f7d668 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -406,8 +406,8 @@
                     case FrameworkStatsLog.BATTERY_VOLTAGE:
                     case FrameworkStatsLog.BATTERY_CYCLE_COUNT:
                         return pullHealthHal(atomTag, data);
-                    case FrameworkStatsLog.APP_FEATURES_OPS:
-                        return pullAppFeaturesOps(atomTag, data);
+                    case FrameworkStatsLog.ATTRIBUTED_APP_OPS:
+                        return pullAttributedAppOps(atomTag, data);
                     default:
                         throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
                 }
@@ -562,7 +562,7 @@
         registerAppsOnExternalStorageInfo();
         registerFaceSettings();
         registerAppOps();
-        registerAppFeaturesOps();
+        registerAttributedAppOps();
         registerRuntimeAppOpAccessMessage();
         registerNotificationRemoteViews();
         registerDangerousPermissionState();
@@ -2898,8 +2898,8 @@
         return StatsManager.PULL_SUCCESS;
     }
 
-    private void registerAppFeaturesOps() {
-        int tagId = FrameworkStatsLog.APP_FEATURES_OPS;
+    private void registerAttributedAppOps() {
+        int tagId = FrameworkStatsLog.ATTRIBUTED_APP_OPS;
         mStatsManager.setPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -2908,7 +2908,7 @@
         );
     }
 
-    int pullAppFeaturesOps(int atomTag, List<StatsEvent> pulledData) {
+    int pullAttributedAppOps(int atomTag, List<StatsEvent> pulledData) {
         final long token = Binder.clearCallingIdentity();
         try {
             AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
@@ -2946,7 +2946,7 @@
         appOps.getHistoricalOps(histOpsRequest, mContext.getMainExecutor(), ops::complete);
         HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS,
                 TimeUnit.MILLISECONDS);
-        return processHistoricalOps(histOps, FrameworkStatsLog.APP_FEATURES_OPS, null);
+        return processHistoricalOps(histOps, FrameworkStatsLog.ATTRIBUTED_APP_OPS, null);
     }
 
     int processHistoricalOps(HistoricalOps histOps, int atomTag, List<StatsEvent> pulledData) {
@@ -2956,15 +2956,15 @@
             final int uid = uidOps.getUid();
             for (int pkgIdx = 0; pkgIdx < uidOps.getPackageCount(); pkgIdx++) {
                 final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(pkgIdx);
-                if (atomTag == FrameworkStatsLog.APP_FEATURES_OPS) {
-                    for (int featureIdx = 0; featureIdx < packageOps.getFeatureCount();
-                            featureIdx++) {
-                        final AppOpsManager.HistoricalFeatureOps featureOps =
-                                packageOps.getFeatureOpsAt(featureIdx);
-                        for (int opIdx = 0; opIdx < featureOps.getOpCount(); opIdx++) {
-                            final AppOpsManager.HistoricalOp op = featureOps.getOpAt(opIdx);
+                if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) {
+                    for (int attributionIdx = 0;
+                            attributionIdx < packageOps.getAttributedOpsCount(); attributionIdx++) {
+                        final AppOpsManager.AttributedHistoricalOps attributedOps =
+                                packageOps.getAttributedOpsAt(attributionIdx);
+                        for (int opIdx = 0; opIdx < attributedOps.getOpCount(); opIdx++) {
+                            final AppOpsManager.HistoricalOp op = attributedOps.getOpAt(opIdx);
                             counter += processHistoricalOp(op, atomTag, pulledData, uid,
-                                    packageOps.getPackageName(), featureOps.getFeatureId());
+                                    packageOps.getPackageName(), attributedOps.getTag());
                         }
                     }
                 } else if (atomTag == FrameworkStatsLog.APP_OPS) {
@@ -2981,18 +2981,19 @@
 
     private int processHistoricalOp(AppOpsManager.HistoricalOp op, int atomTag,
             @Nullable List<StatsEvent> pulledData, int uid, String packageName,
-            @Nullable String feature) {
-        if (atomTag == FrameworkStatsLog.APP_FEATURES_OPS) {
+            @Nullable String attributionTag) {
+        if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) {
             if (pulledData == null) { // this is size estimation call
                 if (op.getForegroundAccessCount(OP_FLAGS_PULLED) + op.getBackgroundAccessCount(
                         OP_FLAGS_PULLED) == 0) {
                     return 0;
                 } else {
-                    return 32 + packageName.length() + (feature == null ? 1 : feature.length());
+                    return 32 + packageName.length() + (attributionTag == null ? 1
+                            : attributionTag.length());
                 }
             } else {
-                if (abs((op.getOpCode() + feature + packageName).hashCode() + RANDOM_SEED) % 100
-                        >= mAppOpsSamplingRate) {
+                if (abs((op.getOpCode() + attributionTag + packageName).hashCode() + RANDOM_SEED)
+                        % 100 >= mAppOpsSamplingRate) {
                     return 0;
                 }
             }
@@ -3002,10 +3003,10 @@
         e.setAtomId(atomTag);
         e.writeInt(uid);
         e.writeString(packageName);
-        if (atomTag == FrameworkStatsLog.APP_FEATURES_OPS) {
-            e.writeString(feature);
+        if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) {
+            e.writeString(attributionTag);
         }
-        if (atomTag == FrameworkStatsLog.APP_FEATURES_OPS) {
+        if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) {
             e.writeString(op.getOpName());
         } else {
             e.writeInt(op.getOpCode());
@@ -3032,7 +3033,7 @@
                 e.writeBoolean(false);
             }
         }
-        if (atomTag == FrameworkStatsLog.APP_FEATURES_OPS) {
+        if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) {
             e.writeInt(mAppOpsSamplingRate);
         }
         pulledData.add(e.build());
@@ -3055,10 +3056,10 @@
             e.writeInt(message.getUid());
             e.writeString(message.getPackageName());
             e.writeString(message.getOp());
-            if (message.getFeatureId() == null) {
+            if (message.getAttributionTag() == null) {
                 e.writeString("");
             } else {
-                e.writeString(message.getFeatureId());
+                e.writeString(message.getAttributionTag());
             }
             e.writeString(message.getMessage());
             e.writeInt(message.getSamplingStrategy());
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 8164526..74a6383 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -41,6 +41,7 @@
 import android.util.SparseArray;
 import android.view.textclassifier.ConversationActions;
 import android.view.textclassifier.SelectionEvent;
+import android.view.textclassifier.SystemTextClassifierMetadata;
 import android.view.textclassifier.TextClassification;
 import android.view.textclassifier.TextClassificationConstants;
 import android.view.textclassifier.TextClassificationContext;
@@ -179,12 +180,12 @@
             TextSelection.Request request, ITextClassifierCallback callback)
             throws RemoteException {
         Objects.requireNonNull(request);
+        Objects.requireNonNull(request.getSystemTextClassifierMetadata());
 
         handleRequest(
-                request.getUserId(),
-                request.getCallingPackageName(),
+                request.getSystemTextClassifierMetadata(),
+                /* verifyCallingPackage= */ true,
                 /* attemptToBind= */ true,
-                request.getUseDefaultTextClassifier(),
                 service -> service.onSuggestSelection(sessionId, request, callback),
                 "onSuggestSelection",
                 callback);
@@ -196,12 +197,12 @@
             TextClassification.Request request, ITextClassifierCallback callback)
             throws RemoteException {
         Objects.requireNonNull(request);
+        Objects.requireNonNull(request.getSystemTextClassifierMetadata());
 
         handleRequest(
-                request.getUserId(),
-                request.getCallingPackageName(),
+                request.getSystemTextClassifierMetadata(),
+                /* verifyCallingPackage= */ true,
                 /* attemptToBind= */ true,
-                request.getUseDefaultTextClassifier(),
                 service -> service.onClassifyText(sessionId, request, callback),
                 "onClassifyText",
                 callback);
@@ -213,12 +214,12 @@
             TextLinks.Request request, ITextClassifierCallback callback)
             throws RemoteException {
         Objects.requireNonNull(request);
+        Objects.requireNonNull(request.getSystemTextClassifierMetadata());
 
         handleRequest(
-                request.getUserId(),
-                request.getCallingPackageName(),
+                request.getSystemTextClassifierMetadata(),
+                /* verifyCallingPackage= */ true,
                 /* attemptToBind= */ true,
-                request.getUseDefaultTextClassifier(),
                 service -> service.onGenerateLinks(sessionId, request, callback),
                 "onGenerateLinks",
                 callback);
@@ -229,12 +230,12 @@
             @Nullable TextClassificationSessionId sessionId, SelectionEvent event)
             throws RemoteException {
         Objects.requireNonNull(event);
+        Objects.requireNonNull(event.getSystemTextClassifierMetadata());
 
         handleRequest(
-                event.getUserId(),
-                /* callingPackageName= */ null,
+                event.getSystemTextClassifierMetadata(),
+                /* verifyCallingPackage= */ false,
                 /* attemptToBind= */ false,
-                event.getUseDefaultTextClassifier(),
                 service -> service.onSelectionEvent(sessionId, event),
                 "onSelectionEvent",
                 NO_OP_CALLBACK);
@@ -246,18 +247,14 @@
             TextClassifierEvent event) throws RemoteException {
         Objects.requireNonNull(event);
 
-        final int userId = event.getEventContext() == null
-                ? UserHandle.getCallingUserId()
-                : event.getEventContext().getUserId();
-        final boolean useDefaultTextClassifier =
-                event.getEventContext() != null
-                        ? event.getEventContext().getUseDefaultTextClassifier()
-                        : true;
+        final TextClassificationContext eventContext = event.getEventContext();
+        final SystemTextClassifierMetadata systemTcMetadata =
+                eventContext != null ? eventContext.getSystemTextClassifierMetadata() : null;
+
         handleRequest(
-                userId,
-                /* callingPackageName= */ null,
+                systemTcMetadata,
+                /* verifyCallingPackage= */ false,
                 /* attemptToBind= */ false,
-                useDefaultTextClassifier,
                 service -> service.onTextClassifierEvent(sessionId, event),
                 "onTextClassifierEvent",
                 NO_OP_CALLBACK);
@@ -269,12 +266,12 @@
             TextLanguage.Request request,
             ITextClassifierCallback callback) throws RemoteException {
         Objects.requireNonNull(request);
+        Objects.requireNonNull(request.getSystemTextClassifierMetadata());
 
         handleRequest(
-                request.getUserId(),
-                request.getCallingPackageName(),
+                request.getSystemTextClassifierMetadata(),
+                /* verifyCallingPackage= */ true,
                 /* attemptToBind= */ true,
-                request.getUseDefaultTextClassifier(),
                 service -> service.onDetectLanguage(sessionId, request, callback),
                 "onDetectLanguage",
                 callback);
@@ -286,12 +283,12 @@
             ConversationActions.Request request,
             ITextClassifierCallback callback) throws RemoteException {
         Objects.requireNonNull(request);
+        Objects.requireNonNull(request.getSystemTextClassifierMetadata());
 
         handleRequest(
-                request.getUserId(),
-                request.getCallingPackageName(),
+                request.getSystemTextClassifierMetadata(),
+                /* verifyCallingPackage= */ true,
                 /* attemptToBind= */ true,
-                request.getUseDefaultTextClassifier(),
                 service -> service.onSuggestConversationActions(sessionId, request, callback),
                 "onSuggestConversationActions",
                 callback);
@@ -303,13 +300,12 @@
             throws RemoteException {
         Objects.requireNonNull(sessionId);
         Objects.requireNonNull(classificationContext);
+        Objects.requireNonNull(classificationContext.getSystemTextClassifierMetadata());
 
-        final int userId = classificationContext.getUserId();
         handleRequest(
-                userId,
-                classificationContext.getPackageName(),
+                classificationContext.getSystemTextClassifierMetadata(),
+                /* verifyCallingPackage= */ true,
                 /* attemptToBind= */ false,
-                classificationContext.getUseDefaultTextClassifier(),
                 service -> {
                     service.onCreateTextClassificationSession(classificationContext, sessionId);
                     mSessionCache.put(sessionId, classificationContext);
@@ -333,11 +329,13 @@
                     textClassificationContext != null
                             ? textClassificationContext.useDefaultTextClassifier
                             : true;
+            final SystemTextClassifierMetadata sysTcMetadata = new SystemTextClassifierMetadata(
+                    "", userId, useDefaultTextClassifier);
+
             handleRequest(
-                    userId,
-                    /* callingPackageName= */ null,
+                    sysTcMetadata,
+                    /* verifyCallingPackage= */ false,
                     /* attemptToBind= */ false,
-                    useDefaultTextClassifier,
                     service -> {
                         service.onDestroyTextClassificationSession(sessionId);
                         mSessionCache.remove(sessionId);
@@ -412,10 +410,9 @@
     }
 
     private void handleRequest(
-            @UserIdInt int userId,
-            @Nullable String callingPackageName,
+            @Nullable SystemTextClassifierMetadata sysTcMetadata,
+            boolean verifyCallingPackage,
             boolean attemptToBind,
-            boolean useDefaultTextClassifier,
             @NonNull ThrowingConsumer<ITextClassifierService> textClassifierServiceConsumer,
             @NonNull String methodName,
             @NonNull ITextClassifierCallback callback) throws RemoteException {
@@ -423,8 +420,17 @@
         Objects.requireNonNull(methodName);
         Objects.requireNonNull(callback);
 
+        final int userId =
+                sysTcMetadata == null ? UserHandle.getCallingUserId() : sysTcMetadata.getUserId();
+        final String callingPackageName =
+                sysTcMetadata == null ? null : sysTcMetadata.getCallingPackageName();
+        final boolean useDefaultTextClassifier =
+                sysTcMetadata == null ? true : sysTcMetadata.useDefaultTextClassifier();
+
         try {
-            validateCallingPackage(callingPackageName);
+            if (verifyCallingPackage) {
+                validateCallingPackage(callingPackageName);
+            }
             validateUser(userId);
         } catch (Exception e) {
             throw new RemoteException("Invalid request: " + e.getMessage(), e,
@@ -636,8 +642,10 @@
         public final boolean useDefaultTextClassifier;
 
         StrippedTextClassificationContext(TextClassificationContext textClassificationContext) {
-            userId = textClassificationContext.getUserId();
-            useDefaultTextClassifier = textClassificationContext.getUseDefaultTextClassifier();
+            SystemTextClassifierMetadata sysTcMetadata =
+                    textClassificationContext.getSystemTextClassifierMetadata();
+            userId = sysTcMetadata.getUserId();
+            useDefaultTextClassifier = sysTcMetadata.useDefaultTextClassifier();
         }
     }
 
diff --git a/services/core/java/com/android/server/tv/UinputBridge.java b/services/core/java/com/android/server/tv/UinputBridge.java
index 752aa66..a2fe5fc 100644
--- a/services/core/java/com/android/server/tv/UinputBridge.java
+++ b/services/core/java/com/android/server/tv/UinputBridge.java
@@ -28,7 +28,7 @@
 public final class UinputBridge {
     private final CloseGuard mCloseGuard = CloseGuard.get();
     private long mPtr;
-    private IBinder mToken = null;
+    private IBinder mToken;
 
     private static native long nativeOpen(String name, String uniqueId, int width, int height,
                                           int maxPointers);
@@ -39,6 +39,25 @@
     private static native void nativeSendPointerUp(long ptr, int pointerId);
     private static native void nativeSendPointerSync(long ptr);
 
+    /** Opens a gamepad - will support gamepad key and axis sending */
+    private static native long nativeGamepadOpen(String name, String uniqueId);
+
+    /** Marks the specified key up/down for a gamepad */
+    private static native void nativeSendGamepadKey(long ptr, int keyIndex, boolean down);
+
+    /**
+     * Gamepads pre-define the following axes:
+     *   - Left joystick X, axis == ABS_X == 0, range [0, 254]
+     *   - Left joystick Y, axis == ABS_Y == 1, range [0, 254]
+     *   - Right joystick X, axis == ABS_RX == 3, range [0, 254]
+     *   - Right joystick Y, axis == ABS_RY == 4, range [0, 254]
+     *   - Left trigger, axis == ABS_Z == 2, range [0, 254]
+     *   - Right trigger, axis == ABS_RZ == 5, range [0, 254]
+     *   - DPad X, axis == ABS_HAT0X == 0x10, range [-1, 1]
+     *   - DPad Y, axis == ABS_HAT0Y == 0x11, range [-1, 1]
+     */
+    private static native void nativeSendGamepadAxisValue(long ptr, int axis, int value);
+
     public UinputBridge(IBinder token, String name, int width, int height, int maxPointers)
                         throws IOException {
         if (width < 1 || height < 1) {
@@ -58,12 +77,31 @@
         mCloseGuard.open("close");
     }
 
+    /** Constructor used by static factory methods */
+    private UinputBridge(IBinder token, long ptr) {
+        mPtr = ptr;
+        mToken = token;
+        mCloseGuard.open("close");
+    }
+
+    /** Opens a UinputBridge that supports gamepad buttons and axes. */
+    public static UinputBridge openGamepad(IBinder token, String name)
+            throws IOException {
+        if (token == null) {
+            throw new IllegalArgumentException("Token cannot be null");
+        }
+        long ptr = nativeGamepadOpen(name, token.toString());
+        if (ptr == 0) {
+            throw new IOException("Could not open uinput device " + name);
+        }
+
+        return new UinputBridge(token, ptr);
+    }
+
     @Override
     protected void finalize() throws Throwable {
         try {
-            if (mCloseGuard != null) {
-                mCloseGuard.warnIfOpen();
-            }
+            mCloseGuard.warnIfOpen();
             close(mToken);
         } finally {
             mToken = null;
@@ -119,7 +157,35 @@
         if (isTokenValid(token)) {
             nativeSendPointerSync(mPtr);
         }
+    }
 
+    /** Send a gamepad key
+     *  @param keyIndex - the index of the w3-spec key
+     *  @param down - is the key pressed ?
+     */
+    public void sendGamepadKey(IBinder token, int keyIndex, boolean down) {
+        if (isTokenValid(token)) {
+            nativeSendGamepadKey(mPtr, keyIndex, down);
+        }
+    }
+
+    /** Send a gamepad axis value.
+     *   - Left joystick X, axis == ABS_X == 0, range [0, 254]
+     *   - Left joystick Y, axis == ABS_Y == 1, range [0, 254]
+     *   - Right joystick X, axis == ABS_RX == 3, range [0, 254]
+     *   - Right joystick Y, axis == ABS_RY == 4, range [0, 254]
+     *   - Left trigger, axis == ABS_Z == 2, range [0, 254]
+     *   - Right trigger, axis == ABS_RZ == 5, range [0, 254]
+     *   - DPad X, axis == ABS_HAT0X == 0x10, range [-1, 1]
+     *   - DPad Y, axis == ABS_HAT0Y == 0x11, range [-1, 1]
+     *
+     * @param axis is the axis index
+     * @param value is the value to set for that axis
+     */
+    public void sendGamepadAxisValue(IBinder token, int axis, int value) {
+        if (isTokenValid(token)) {
+            nativeSendGamepadAxisValue(mPtr, axis, value);
+        }
     }
 
     public void clear(IBinder token) {
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 04d551d..b43d8b7 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -24,6 +24,8 @@
 import android.media.tv.tunerresourcemanager.IResourcesReclaimListener;
 import android.media.tv.tunerresourcemanager.ITunerResourceManager;
 import android.media.tv.tunerresourcemanager.ResourceClientProfile;
+import android.media.tv.tunerresourcemanager.TunerDemuxRequest;
+import android.media.tv.tunerresourcemanager.TunerDescramblerRequest;
 import android.media.tv.tunerresourcemanager.TunerFrontendInfo;
 import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
 import android.media.tv.tunerresourcemanager.TunerLnbRequest;
@@ -189,6 +191,24 @@
         }
 
         @Override
+        public boolean requestDemux(@NonNull TunerDemuxRequest request,
+                    @NonNull int[] demuxHandle) {
+            if (DEBUG) {
+                Slog.d(TAG, "requestDemux(request=" + request + ")");
+            }
+            return true;
+        }
+
+        @Override
+        public boolean requestDescrambler(@NonNull TunerDescramblerRequest request,
+                    @NonNull int[] descrambleHandle) {
+            if (DEBUG) {
+                Slog.d(TAG, "requestDescrambler(request=" + request + ")");
+            }
+            return true;
+        }
+
+        @Override
         public boolean requestCasSession(
                 @NonNull CasSessionRequest request, @NonNull int[] sessionResourceId) {
             if (DEBUG) {
@@ -214,6 +234,20 @@
         }
 
         @Override
+        public void releaseDemux(int demuxHandle) {
+            if (DEBUG) {
+                Slog.d(TAG, "releaseDemux(demuxHandle=" + demuxHandle + ")");
+            }
+        }
+
+        @Override
+        public void releaseDescrambler(int descramblerHandle) {
+            if (DEBUG) {
+                Slog.d(TAG, "releaseDescrambler(descramblerHandle=" + descramblerHandle + ")");
+            }
+        }
+
+        @Override
         public void releaseCasSession(int sessionResourceId) {
             if (DEBUG) {
                 Slog.d(TAG, "releaseCasSession(sessionResourceId=" + sessionResourceId + ")");
diff --git a/services/core/java/com/android/server/twilight/TwilightService.java b/services/core/java/com/android/server/twilight/TwilightService.java
index 761fbf8..88a60dd 100644
--- a/services/core/java/com/android/server/twilight/TwilightService.java
+++ b/services/core/java/com/android/server/twilight/TwilightService.java
@@ -50,7 +50,7 @@
         implements AlarmManager.OnAlarmListener, Handler.Callback, LocationListener {
 
     private static final String TAG = "TwilightService";
-    private static final String FEATURE_ID = "TwilightService";
+    private static final String ATTRIBUTION_TAG = "TwilightService";
     private static final boolean DEBUG = false;
 
     private static final int MSG_START_LISTENING = 1;
@@ -74,7 +74,7 @@
     protected TwilightState mLastTwilightState;
 
     public TwilightService(Context context) {
-        super(context.createFeatureContext(FEATURE_ID));
+        super(context.createAttributionContext(ATTRIBUTION_TAG));
         mHandler = new Handler(Looper.getMainLooper(), this);
     }
 
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 6eb3c0f..a298b89 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2184,47 +2184,6 @@
         }
     }
 
-    /**
-     * Called when the wallpaper needs to zoom out.
-     *
-     * @param zoom from 0 to 1 (inclusive) where 1 means fully zoomed out, 0 means fully zoomed in.
-     * @param callingPackage package name calling this API.
-     * @param displayId id of the display whose zoom is updating.
-     */
-    public void setWallpaperZoomOut(float zoom, String callingPackage, int displayId) {
-        if (!isWallpaperSupported(callingPackage)) {
-            return;
-        }
-        synchronized (mLock) {
-            if (!isValidDisplay(displayId)) {
-                throw new IllegalArgumentException("Cannot find display with id=" + displayId);
-            }
-            int userId = UserHandle.getCallingUserId();
-            if (mCurrentUserId != userId) {
-                return; // Don't change the properties now
-            }
-            WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
-            if (zoom < 0 || zoom > 1f) {
-                throw new IllegalArgumentException("zoom must be between 0 and one: " + zoom);
-            }
-
-            if (wallpaper.connection != null) {
-                final WallpaperConnection.DisplayConnector connector = wallpaper.connection
-                        .getDisplayConnectorOrCreate(displayId);
-                final IWallpaperEngine engine = connector != null ? connector.mEngine : null;
-                if (engine != null) {
-                    try {
-                        engine.setZoomOut(zoom);
-                    } catch (RemoteException e) {
-                        if (DEBUG) {
-                            Slog.w(TAG, "Couldn't set wallpaper zoom", e);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
     @Deprecated
     @Override
     public ParcelFileDescriptor getWallpaper(String callingPkg, IWallpaperManagerCallback cb,
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index b6ad241..e73c928 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1589,6 +1589,11 @@
             info.taskAffinity = uid + ":" + info.taskAffinity;
         }
         taskAffinity = info.taskAffinity;
+        if (info.windowLayout != null && info.windowLayout.windowLayoutAffinity != null
+                && !info.windowLayout.windowLayoutAffinity.startsWith(uid)) {
+            info.windowLayout.windowLayoutAffinity =
+                    uid + ":" + info.windowLayout.windowLayoutAffinity;
+        }
         stateNotNeeded = (aInfo.flags & FLAG_STATE_NOT_NEEDED) != 0;
         nonLocalizedLabel = aInfo.nonLocalizedLabel;
         labelRes = aInfo.labelRes;
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 9bad799..4ebb423 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -153,7 +153,6 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
 import android.view.DisplayInfo;
-import android.view.ITaskOrganizer;
 import android.view.SurfaceControl;
 
 import com.android.internal.annotations.GuardedBy;
@@ -307,8 +306,6 @@
     // TODO(task-hierarchy): remove when tiles can be actual parents
     TaskTile mTile = null;
 
-    private int mLastTaskOrganizerWindowingMode = -1;
-
     private final Handler mHandler;
 
     private class ActivityStackHandler extends Handler {
@@ -635,8 +632,6 @@
 
         super.onConfigurationChanged(newParentConfig);
 
-        updateTaskOrganizerState();
-
         // Only need to update surface size here since the super method will handle updating
         // surface position.
         updateSurfaceSize(getPendingTransaction());
@@ -692,30 +687,6 @@
         }
     }
 
-    void updateTaskOrganizerState() {
-        if (!isRootTask()) {
-            return;
-        }
-
-        final int windowingMode = getWindowingMode();
-        if (windowingMode == mLastTaskOrganizerWindowingMode) {
-            // If our windowing mode hasn't actually changed, then just stick
-            // with our old organizer. This lets us implement the semantic
-            // where SysUI can continue to manage it's old tasks
-            // while CTS temporarily takes over the registration.
-            return;
-        }
-        /*
-         * Different windowing modes may be managed by different task organizers. If
-         * getTaskOrganizer returns null, we still call setTaskOrganizer to
-         * make sure we clear it.
-         */
-        final ITaskOrganizer org =
-            mWmService.mAtmService.mTaskOrganizerController.getTaskOrganizer(windowingMode);
-        setTaskOrganizer(org);
-        mLastTaskOrganizerWindowingMode = windowingMode;
-    }
-
     @Override
     public void setWindowingMode(int windowingMode) {
         // Calling Task#setWindowingMode() for leaf task since this is the a specialization of
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 688c9ae..c7a1391 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -29,7 +29,6 @@
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
 import static android.app.WaitResult.LAUNCH_STATE_COLD;
 import static android.app.WaitResult.LAUNCH_STATE_HOT;
-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;
@@ -178,6 +177,9 @@
     private Intent mNewTaskIntent;
     private ActivityStack mSourceStack;
     private ActivityStack mTargetStack;
+    // The task that the last activity was started into. We currently reset the actual start
+    // activity's task and as a result may not have a reference to the task in all cases
+    private Task mTargetTask;
     private boolean mMovedToFront;
     private boolean mNoAnimation;
     private boolean mKeepCurTransition;
@@ -545,6 +547,7 @@
         mNewTaskIntent = starter.mNewTaskIntent;
         mSourceStack = starter.mSourceStack;
 
+        mTargetTask = starter.mTargetTask;
         mTargetStack = starter.mTargetStack;
         mMovedToFront = starter.mMovedToFront;
         mNoAnimation = starter.mNoAnimation;
@@ -1368,7 +1371,10 @@
         // it waits for the new activity to become visible instead, {@link #waitResultIfNeeded}.
         mSupervisor.reportWaitingActivityLaunchedIfNeeded(r, result);
 
-        if (startedActivityStack == null) {
+        final Task targetTask = r.getTask() != null
+                ? r.getTask()
+                : mTargetTask;
+        if (startedActivityStack == null || targetTask == null) {
             return;
         }
 
@@ -1379,19 +1385,10 @@
             // The activity was already running so it wasn't started, but either brought to the
             // front or the new intent was delivered to it since it was already in front. Notify
             // anyone interested in this piece of information.
-            switch (startedActivityStack.getWindowingMode()) {
-                case WINDOWING_MODE_PINNED:
-                    mService.getTaskChangeNotificationController().notifyPinnedActivityRestartAttempt(
-                            clearedTask);
-                    break;
-                case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
-                    final ActivityStack homeStack =
-                            startedActivityStack.getDisplay().getOrCreateRootHomeTask();
-                    if (homeStack != null && homeStack.shouldBeVisible(null /* starting */)) {
-                        mService.mWindowManager.showRecentApps();
-                    }
-                    break;
-            }
+            final ActivityStack homeStack = targetTask.getDisplayContent().getRootHomeTask();
+            final boolean homeTaskVisible = homeStack != null && homeStack.shouldBeVisible(null);
+            mService.getTaskChangeNotificationController().notifyActivityRestartAttempt(
+                    targetTask.getTaskInfo(), homeTaskVisible, clearedTask);
         }
     }
 
@@ -1517,6 +1514,7 @@
         // Compute if there is an existing task that should be used for.
         final Task targetTask = reusedTask != null ? reusedTask : computeTargetTask();
         final boolean newTask = targetTask == null;
+        mTargetTask = targetTask;
 
         computeLaunchParams(r, sourceRecord, targetTask);
 
@@ -2018,6 +2016,7 @@
         mSourceStack = null;
 
         mTargetStack = null;
+        mTargetTask = null;
         mMovedToFront = false;
         mNoAnimation = false;
         mKeepCurTransition = false;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 21d300a..7bacc42 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -356,6 +356,8 @@
     ActivityManagerInternal mAmInternal;
     UriGrantsManagerInternal mUgmInternal;
     private PackageManagerInternal mPmInternal;
+    /** The cached sys ui service component name from package manager. */
+    private ComponentName mSysUiServiceComponent;
     private PermissionPolicyInternal mPermissionPolicyInternal;
     @VisibleForTesting
     final ActivityTaskManagerInternal mInternal;
@@ -5869,6 +5871,14 @@
         return mPmInternal;
     }
 
+    ComponentName getSysUiServiceComponentLocked() {
+        if (mSysUiServiceComponent == null) {
+            final PackageManagerInternal pm = getPackageManagerInternalLocked();
+            mSysUiServiceComponent = pm.getSystemUiServiceComponent();
+        }
+        return mSysUiServiceComponent;
+    }
+
     PermissionPolicyInternal getPermissionPolicyInternal() {
         if (mPermissionPolicyInternal == null) {
             mPermissionPolicyInternal = LocalServices.getService(PermissionPolicyInternal.class);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 3352bd5..98e3d07 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2224,9 +2224,7 @@
                             .addTaggedData(MetricsEvent.FIELD_DISPLAY_ID, getDisplayId()));
         }
 
-        // If there was no pinned stack, we still need to notify the controller of the display info
-        // update as a result of the config change.
-        if (mPinnedStackControllerLocked != null && !hasPinnedTask()) {
+        if (mPinnedStackControllerLocked != null) {
             mPinnedStackControllerLocked.onDisplayInfoChanged(getDisplayInfo());
         }
     }
@@ -4934,6 +4932,12 @@
                 scheduleAnimation();
             }
         }
+
+        @Override
+        boolean shouldMagnify() {
+            // Omitted from Screen-Magnification
+            return false;
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index d5a0d05..ba61667 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -28,6 +28,7 @@
 import static android.view.InsetsState.ITYPE_BOTTOM_DISPLAY_CUTOUT;
 import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
 import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
+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;
@@ -439,10 +440,12 @@
                     updateDreamingSleepToken(msg.arg1 != 0);
                     break;
                 case MSG_REQUEST_TRANSIENT_BARS:
-                    WindowState targetBar = (msg.arg1 == MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS)
-                            ? mStatusBar : mNavigationBar;
-                    if (targetBar != null) {
-                        requestTransientBars(targetBar);
+                    synchronized (mLock) {
+                        WindowState targetBar = (msg.arg1 == MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS)
+                                ? mStatusBar : mNavigationBar;
+                        if (targetBar != null) {
+                            requestTransientBars(targetBar);
+                        }
                     }
                     break;
                 case MSG_DISPOSE_INPUT_CONSUMER:
@@ -498,15 +501,20 @@
                 new SystemGesturesPointerEventListener.Callbacks() {
                     @Override
                     public void onSwipeFromTop() {
-                        if (mStatusBar != null) {
-                            requestTransientBars(mStatusBar);
+                        synchronized (mLock) {
+                            if (mStatusBar != null) {
+                                requestTransientBars(mStatusBar);
+                            }
                         }
                     }
 
                     @Override
                     public void onSwipeFromBottom() {
-                        if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_BOTTOM) {
-                            requestTransientBars(mNavigationBar);
+                        synchronized (mLock) {
+                            if (mNavigationBar != null
+                                    && mNavigationBarPosition == NAV_BAR_BOTTOM) {
+                                requestTransientBars(mNavigationBar);
+                            }
                         }
                     }
 
@@ -516,12 +524,13 @@
                         synchronized (mLock) {
                             mDisplayContent.calculateSystemGestureExclusion(
                                     excludedRegion, null /* outUnrestricted */);
-                        }
-                        final boolean sideAllowed = mNavigationBarAlwaysShowOnSideGesture
-                                || mNavigationBarPosition == NAV_BAR_RIGHT;
-                        if (mNavigationBar != null && sideAllowed
-                                && !mSystemGestures.currentGestureStartedInRegion(excludedRegion)) {
-                            requestTransientBars(mNavigationBar);
+                            final boolean sideAllowed = mNavigationBarAlwaysShowOnSideGesture
+                                    || mNavigationBarPosition == NAV_BAR_RIGHT;
+                            if (mNavigationBar != null && sideAllowed
+                                    && !mSystemGestures.currentGestureStartedInRegion(
+                                            excludedRegion)) {
+                                requestTransientBars(mNavigationBar);
+                            }
                         }
                         excludedRegion.recycle();
                     }
@@ -532,12 +541,13 @@
                         synchronized (mLock) {
                             mDisplayContent.calculateSystemGestureExclusion(
                                     excludedRegion, null /* outUnrestricted */);
-                        }
-                        final boolean sideAllowed = mNavigationBarAlwaysShowOnSideGesture
-                                || mNavigationBarPosition == NAV_BAR_LEFT;
-                        if (mNavigationBar != null && sideAllowed
-                                && !mSystemGestures.currentGestureStartedInRegion(excludedRegion)) {
-                            requestTransientBars(mNavigationBar);
+                            final boolean sideAllowed = mNavigationBarAlwaysShowOnSideGesture
+                                    || mNavigationBarPosition == NAV_BAR_LEFT;
+                            if (mNavigationBar != null && sideAllowed
+                                    && !mSystemGestures.currentGestureStartedInRegion(
+                                            excludedRegion)) {
+                                requestTransientBars(mNavigationBar);
+                            }
                         }
                         excludedRegion.recycle();
                     }
@@ -1469,8 +1479,14 @@
      */
     public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {
         displayFrames.onBeginLayout();
-        updateInsetsStateForDisplayCutout(displayFrames,
-                mDisplayContent.getInsetsStateController().getRawInsetsState());
+        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);
         mSystemGestures.screenWidth = displayFrames.mUnrestricted.width();
         mSystemGestures.screenHeight = displayFrames.mUnrestricted.height();
 
@@ -3149,47 +3165,46 @@
     }
 
     private void requestTransientBars(WindowState swipeTarget) {
-        synchronized (mLock) {
-            if (!mService.mPolicy.isUserSetupComplete()) {
-                // Swipe-up for navigation bar is disabled during setup
+        if (!mService.mPolicy.isUserSetupComplete()) {
+            // Swipe-up for navigation bar is disabled during setup
+            return;
+        }
+        if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) {
+            if (swipeTarget == mNavigationBar
+                    && !getInsetsPolicy().isHidden(ITYPE_NAVIGATION_BAR)) {
+                // Don't show status bar when swiping on already visible navigation bar
                 return;
             }
-            if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) {
-                if (swipeTarget == mNavigationBar
-                        && !getInsetsPolicy().isHidden(ITYPE_NAVIGATION_BAR)) {
-                    // Don't show status bar when swiping on already visible navigation bar
-                    return;
-                }
-                final InsetsControlTarget controlTarget =
-                        swipeTarget.getControllableInsetProvider().getControlTarget();
+            final InsetsSourceProvider provider = swipeTarget.getControllableInsetProvider();
+            final InsetsControlTarget controlTarget = provider != null
+                    ? provider.getControlTarget() : null;
 
-                // No transient mode on lockscreen (in notification shade window).
-                if (controlTarget == null || controlTarget == getNotificationShade()) {
+            // No transient mode on lockscreen (in notification shade window).
+            if (controlTarget == null || controlTarget == getNotificationShade()) {
+                return;
+            }
+            if (controlTarget.canShowTransient()) {
+                mDisplayContent.getInsetsPolicy().showTransient(IntArray.wrap(
+                        new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}));
+            } else {
+                controlTarget.showInsets(Type.systemBars(), false);
+            }
+        } else {
+            boolean sb = mStatusBarController.checkShowTransientBarLw();
+            boolean nb = mNavigationBarController.checkShowTransientBarLw()
+                    && !isNavBarEmpty(mLastSystemUiFlags);
+            if (sb || nb) {
+                // Don't show status bar when swiping on already visible navigation bar
+                if (!nb && swipeTarget == mNavigationBar) {
+                    if (DEBUG) Slog.d(TAG, "Not showing transient bar, wrong swipe target");
                     return;
                 }
-                if (controlTarget.canShowTransient()) {
-                    mDisplayContent.getInsetsPolicy().showTransient(IntArray.wrap(
-                            new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}));
-                } else {
-                    controlTarget.showInsets(Type.systemBars(), false);
-                }
-            } else {
-                boolean sb = mStatusBarController.checkShowTransientBarLw();
-                boolean nb = mNavigationBarController.checkShowTransientBarLw()
-                        && !isNavBarEmpty(mLastSystemUiFlags);
-                if (sb || nb) {
-                    // Don't show status bar when swiping on already visible navigation bar
-                    if (!nb && swipeTarget == mNavigationBar) {
-                        if (DEBUG) Slog.d(TAG, "Not showing transient bar, wrong swipe target");
-                        return;
-                    }
-                    if (sb) mStatusBarController.showTransient();
-                    if (nb) mNavigationBarController.showTransient();
-                    updateSystemUiVisibilityLw();
-                }
+                if (sb) mStatusBarController.showTransient();
+                if (nb) mNavigationBarController.showTransient();
+                updateSystemUiVisibilityLw();
             }
-            mImmersiveModeConfirmation.confirmCurrentPrompt();
         }
+        mImmersiveModeConfirmation.confirmCurrentPrompt();
     }
 
     private void disposeInputConsumer(InputConsumer inputConsumer) {
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 01f9888..30912e5 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -36,6 +36,7 @@
 import android.util.SparseArray;
 import android.view.InsetsAnimationControlCallbacks;
 import android.view.InsetsAnimationControlImpl;
+import android.view.InsetsAnimationControlRunner;
 import android.view.InsetsController;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
@@ -44,6 +45,7 @@
 import android.view.SyncRtSurfaceTransactionApplier;
 import android.view.ViewRootImpl;
 import android.view.WindowInsetsAnimation;
+import android.view.WindowInsetsAnimation.Bounds;
 import android.view.WindowInsetsAnimationControlListener;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -327,7 +329,7 @@
         InsetsPolicyAnimationControlCallbacks mControlCallbacks;
 
         InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback) {
-            super(show);
+            super(show, true /* useSfVsync */);
             mFinishCallback = finishCallback;
             mControlCallbacks = new InsetsPolicyAnimationControlCallbacks(this);
         }
@@ -360,8 +362,6 @@
                         mFocusedWin.getDisplayContent().getBounds(), getState(),
                         mListener, typesReady, this, mListener.getDurationMs(),
                         InsetsController.INTERPOLATOR, true,
-                        show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
-                                : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN,
                         show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE);
                 SurfaceAnimationThread.getHandler().post(
                         () -> mListener.onReady(mAnimationControl, typesReady));
@@ -377,7 +377,7 @@
             }
 
             @Override
-            public void notifyFinished(InsetsAnimationControlImpl controller, boolean shown) {
+            public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) {
                 // Nothing's needed here. Finish steps is handled in the listener
                 // onAnimationFinished callback.
             }
@@ -406,14 +406,21 @@
                     applyParams(t, surfaceParams, mTmpFloat9);
                 }
                 t.apply();
+                t.close();
+            }
+
+            // Since we don't push applySurfaceParams to a Handler-queue we don't need
+            // to push release in this case.
+            @Override
+            public void releaseSurfaceControlFromRt(SurfaceControl sc) {
+                sc.release();
             }
 
             @Override
             public void startAnimation(InsetsAnimationControlImpl controller,
                     WindowInsetsAnimationControlListener listener, int types,
                     WindowInsetsAnimation animation,
-                    WindowInsetsAnimation.Bounds bounds,
-                    int layoutDuringAnimation) {
+                    Bounds bounds) {
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 6ff029b..ee36db9 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.InsetsState.ITYPE_CAPTION_BAR;
 import static android.view.InsetsState.ITYPE_IME;
 import static android.view.InsetsState.ITYPE_INVALID;
@@ -29,6 +30,8 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.WindowConfiguration;
+import android.app.WindowConfiguration.WindowingMode;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.SparseArray;
@@ -74,30 +77,28 @@
 
     /**
      * When dispatching window state to the client, we'll need to exclude the source that represents
-     * the window that is being dispatched.
+     * the window that is being dispatched. We also need to exclude certain types of insets source
+     * for client within specific windowing modes.
      *
      * @param target The client we dispatch the state to.
      * @return The state stripped of the necessary information.
      */
     InsetsState getInsetsForDispatch(@NonNull WindowState target) {
         final InsetsSourceProvider provider = target.getControllableInsetProvider();
-        if (provider == null) {
-            return mState;
-        }
-        final @InternalInsetsType int type = provider.getSource().getType();
-        return getInsetsForType(type);
+        final @InternalInsetsType int type = provider != null
+                ? provider.getSource().getType() : ITYPE_INVALID;
+        return getInsetsForTypeAndWindowingMode(type, target.getWindowingMode());
     }
 
     InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
         final @InternalInsetsType int type = getInsetsTypeForWindowType(attrs.type);
-        if (type == ITYPE_INVALID) {
-            return mState;
-        }
-        return getInsetsForType(type);
+        final WindowToken token = mDisplayContent.getWindowToken(attrs.token);
+        final @WindowingMode int windowingMode = token != null
+                ? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
+        return getInsetsForTypeAndWindowingMode(type, windowingMode);
     }
 
-    @InternalInsetsType
-    private static int getInsetsTypeForWindowType(int type) {
+    private static @InternalInsetsType int getInsetsTypeForWindowType(int type) {
         switch(type) {
             case TYPE_STATUS_BAR:
                 return ITYPE_STATUS_BAR;
@@ -110,36 +111,48 @@
         }
     }
 
-    private InsetsState getInsetsForType(@InternalInsetsType int type) {
-        final InsetsState state = new InsetsState();
-        state.set(mState);
-        state.removeSource(type);
+    /** @see #getInsetsForDispatch */
+    private InsetsState getInsetsForTypeAndWindowingMode(@InternalInsetsType int type,
+            @WindowingMode int windowingMode) {
+        InsetsState state = mState;
 
-        // Navigation bar doesn't get influenced by anything else
-        if (type == ITYPE_NAVIGATION_BAR) {
-            state.removeSource(ITYPE_IME);
-            state.removeSource(ITYPE_STATUS_BAR);
-            state.removeSource(ITYPE_CAPTION_BAR);
-        }
+        if (type != ITYPE_INVALID) {
+            state = new InsetsState(state);
+            state.removeSource(type);
 
-        // Status bar doesn't get influenced by caption bar
-        if (type == ITYPE_STATUS_BAR) {
-            state.removeSource(ITYPE_CAPTION_BAR);
-        }
+            // Navigation bar doesn't get influenced by anything else
+            if (type == ITYPE_NAVIGATION_BAR) {
+                state.removeSource(ITYPE_IME);
+                state.removeSource(ITYPE_STATUS_BAR);
+                state.removeSource(ITYPE_CAPTION_BAR);
+            }
 
-        // IME needs different frames for certain cases (e.g. navigation bar in gesture nav).
-        if (type == ITYPE_IME) {
-            for (int i = mProviders.size() - 1; i >= 0; i--) {
-                InsetsSourceProvider otherProvider = mProviders.valueAt(i);
-                if (otherProvider.overridesImeFrame()) {
-                    InsetsSource override =
-                            new InsetsSource(state.getSource(otherProvider.getSource().getType()));
-                    override.setFrame(otherProvider.getImeOverrideFrame());
-                    state.addSource(override);
+            // Status bar doesn't get influenced by caption bar
+            if (type == ITYPE_STATUS_BAR) {
+                state.removeSource(ITYPE_CAPTION_BAR);
+            }
+
+            // IME needs different frames for certain cases (e.g. navigation bar in gesture nav).
+            if (type == ITYPE_IME) {
+                for (int i = mProviders.size() - 1; i >= 0; i--) {
+                    InsetsSourceProvider otherProvider = mProviders.valueAt(i);
+                    if (otherProvider.overridesImeFrame()) {
+                        InsetsSource override =
+                                new InsetsSource(
+                                        state.getSource(otherProvider.getSource().getType()));
+                        override.setFrame(otherProvider.getImeOverrideFrame());
+                        state.addSource(override);
+                    }
                 }
             }
         }
 
+        if (WindowConfiguration.isFloating(windowingMode)) {
+            state = new InsetsState(state);
+            state.removeSource(ITYPE_STATUS_BAR);
+            state.removeSource(ITYPE_NAVIGATION_BAR);
+        }
+
         return state;
     }
 
diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
index 660706e..9371c0e 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
@@ -16,7 +16,9 @@
 
 package com.android.server.wm;
 
+import android.annotation.Nullable;
 import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManagerInternal;
 import android.graphics.Rect;
 import android.os.Environment;
@@ -87,9 +89,17 @@
      * launching activity of tasks) to {@link PersistableLaunchParams} that stores launch metadata
      * that are stable across reboots.
      */
-    private final SparseArray<ArrayMap<ComponentName, PersistableLaunchParams>> mMap =
+    private final SparseArray<ArrayMap<ComponentName, PersistableLaunchParams>> mLaunchParamsMap =
             new SparseArray<>();
 
+    /**
+     * A map from {@link android.content.pm.ActivityInfo.WindowLayout#windowLayoutAffinity} to
+     * activity's component name for reverse queries from window layout affinities to activities.
+     * Used to decide if we should use another activity's record with the same affinity.
+     */
+    private final ArrayMap<String, ArraySet<ComponentName>> mWindowLayoutAffinityMap =
+            new ArrayMap<>();
+
     LaunchParamsPersister(PersisterQueue persisterQueue, ActivityStackSupervisor supervisor) {
         this(persisterQueue, supervisor, Environment::getDataSystemCeDirectory);
     }
@@ -112,7 +122,7 @@
     }
 
     void onCleanupUser(int userId) {
-        mMap.remove(userId);
+        mLaunchParamsMap.remove(userId);
     }
 
     private void loadLaunchParams(int userId) {
@@ -128,7 +138,7 @@
         final File[] paramsFiles = launchParamsFolder.listFiles();
         final ArrayMap<ComponentName, PersistableLaunchParams> map =
                 new ArrayMap<>(paramsFiles.length);
-        mMap.put(userId, map);
+        mLaunchParamsMap.put(userId, map);
 
         for (File paramsFile : paramsFiles) {
             if (!paramsFile.isFile()) {
@@ -179,10 +189,12 @@
                         continue;
                     }
 
-                    params.restoreFromXml(parser);
+                    params.restore(paramsFile, parser);
                 }
 
                 map.put(name, params);
+                addComponentNameToLaunchParamAffinityMapIfNotNull(
+                        name, params.mWindowLayoutAffinity);
             } catch (Exception e) {
                 Slog.w(TAG, "Failed to restore launch params for " + name, e);
                 filesToDelete.add(paramsFile);
@@ -204,19 +216,17 @@
         final ComponentName name = task.realActivity;
         final int userId = task.mUserId;
         PersistableLaunchParams params;
-        ArrayMap<ComponentName, PersistableLaunchParams> map = mMap.get(userId);
+        ArrayMap<ComponentName, PersistableLaunchParams> map = mLaunchParamsMap.get(userId);
         if (map == null) {
             map = new ArrayMap<>();
-            mMap.put(userId, map);
+            mLaunchParamsMap.put(userId, map);
         }
 
-        params = map.get(name);
-        if (params == null) {
-            params = new PersistableLaunchParams();
-            map.put(name, params);
-        }
+        params = map.computeIfAbsent(name, componentName -> new PersistableLaunchParams());
         final boolean changed = saveTaskToLaunchParam(task, display, params);
 
+        addComponentNameToLaunchParamAffinityMapIfNotNull(name, params.mWindowLayoutAffinity);
+
         if (changed) {
             mPersisterQueue.updateLastOrAddItem(
                     new LaunchParamsWriteQueueItem(userId, name, params),
@@ -243,19 +253,63 @@
             params.mBounds.setEmpty();
         }
 
+        String launchParamAffinity = task.mWindowLayoutAffinity;
+        changed |= Objects.equals(launchParamAffinity, params.mWindowLayoutAffinity);
+        params.mWindowLayoutAffinity = launchParamAffinity;
+
+        if (changed) {
+            params.mTimestamp = System.currentTimeMillis();
+        }
+
         return changed;
     }
 
+    private void addComponentNameToLaunchParamAffinityMapIfNotNull(
+            ComponentName name, String launchParamAffinity) {
+        if (launchParamAffinity == null) {
+            return;
+        }
+        mWindowLayoutAffinityMap.computeIfAbsent(launchParamAffinity, affinity -> new ArraySet<>())
+                .add(name);
+    }
+
     void getLaunchParams(Task task, ActivityRecord activity, LaunchParams outParams) {
         final ComponentName name = task != null ? task.realActivity : activity.mActivityComponent;
         final int userId = task != null ? task.mUserId : activity.mUserId;
+        final String windowLayoutAffinity;
+        if (task != null) {
+            windowLayoutAffinity = task.mWindowLayoutAffinity;
+        } else {
+            ActivityInfo.WindowLayout layout = activity.info.windowLayout;
+            windowLayoutAffinity = layout == null ? null : layout.windowLayoutAffinity;
+        }
 
         outParams.reset();
-        Map<ComponentName, PersistableLaunchParams> map = mMap.get(userId);
+        Map<ComponentName, PersistableLaunchParams> map = mLaunchParamsMap.get(userId);
         if (map == null) {
             return;
         }
-        final PersistableLaunchParams persistableParams = map.get(name);
+
+        // First use its own record as a reference.
+        PersistableLaunchParams persistableParams = map.get(name);
+        // Next we'll compare these params against all existing params with the same affinity and
+        // use the newest one.
+        if (windowLayoutAffinity != null
+                && mWindowLayoutAffinityMap.get(windowLayoutAffinity) != null) {
+            ArraySet<ComponentName> candidates = mWindowLayoutAffinityMap.get(windowLayoutAffinity);
+            for (int i = 0; i < candidates.size(); ++i) {
+                ComponentName candidate = candidates.valueAt(i);
+                final PersistableLaunchParams candidateParams = map.get(candidate);
+                if (candidateParams == null) {
+                    continue;
+                }
+
+                if (persistableParams == null
+                        || candidateParams.mTimestamp > persistableParams.mTimestamp) {
+                    persistableParams = candidateParams;
+                }
+            }
+        }
 
         if (persistableParams == null) {
             return;
@@ -272,10 +326,10 @@
 
     void removeRecordForPackage(String packageName) {
         final List<File> fileToDelete = new ArrayList<>();
-        for (int i = 0; i < mMap.size(); ++i) {
-            int userId = mMap.keyAt(i);
+        for (int i = 0; i < mLaunchParamsMap.size(); ++i) {
+            int userId = mLaunchParamsMap.keyAt(i);
             final File launchParamsFolder = getLaunchParamFolder(userId);
-            ArrayMap<ComponentName, PersistableLaunchParams> map = mMap.valueAt(i);
+            ArrayMap<ComponentName, PersistableLaunchParams> map = mLaunchParamsMap.valueAt(i);
             for (int j = map.size() - 1; j >= 0; --j) {
                 final ComponentName name = map.keyAt(j);
                 if (name.getPackageName().equals(packageName)) {
@@ -409,6 +463,7 @@
         private static final String ATTR_WINDOWING_MODE = "windowing_mode";
         private static final String ATTR_DISPLAY_UNIQUE_ID = "display_unique_id";
         private static final String ATTR_BOUNDS = "bounds";
+        private static final String ATTR_WINDOW_LAYOUT_AFFINITY = "window_layout_affinity";
 
         /** The bounds within the parent container. */
         final Rect mBounds = new Rect();
@@ -419,14 +474,29 @@
         /** The windowing mode to be in. */
         int mWindowingMode;
 
+        /**
+         * Last {@link android.content.pm.ActivityInfo.WindowLayout#windowLayoutAffinity} of the
+         * window.
+         */
+        @Nullable String mWindowLayoutAffinity;
+
+        /**
+         * Timestamp from {@link System#currentTimeMillis()} when this record is captured, or last
+         * modified time when the record is restored from storage.
+         */
+        long mTimestamp;
+
         void saveToXml(XmlSerializer serializer) throws IOException {
             serializer.attribute(null, ATTR_DISPLAY_UNIQUE_ID, mDisplayUniqueId);
             serializer.attribute(null, ATTR_WINDOWING_MODE,
                     Integer.toString(mWindowingMode));
             serializer.attribute(null, ATTR_BOUNDS, mBounds.flattenToString());
+            if (mWindowLayoutAffinity != null) {
+                serializer.attribute(null, ATTR_WINDOW_LAYOUT_AFFINITY, mWindowLayoutAffinity);
+            }
         }
 
-        void restoreFromXml(XmlPullParser parser) {
+        void restore(File xmlFile, XmlPullParser parser) {
             for (int i = 0; i < parser.getAttributeCount(); ++i) {
                 final String attrValue = parser.getAttributeValue(i);
                 switch (parser.getAttributeName(i)) {
@@ -443,16 +513,28 @@
                         }
                         break;
                     }
+                    case ATTR_WINDOW_LAYOUT_AFFINITY:
+                        mWindowLayoutAffinity = attrValue;
+                        break;
                 }
             }
+
+            // The modified time could be a few seconds later than the timestamp when the record is
+            // captured, which is a good enough estimate to the capture time after a reboot or a
+            // user switch.
+            mTimestamp = xmlFile.lastModified();
         }
 
         @Override
         public String toString() {
             final StringBuilder builder = new StringBuilder("PersistableLaunchParams{");
-            builder.append("windowingMode=" + mWindowingMode);
+            builder.append(" windowingMode=" + mWindowingMode);
             builder.append(" displayUniqueId=" + mDisplayUniqueId);
             builder.append(" bounds=" + mBounds);
+            if (mWindowLayoutAffinity != null) {
+                builder.append(" launchParamsAffinity=" + mWindowLayoutAffinity);
+            }
+            builder.append(" timestamp=" + mTimestamp);
             builder.append(" }");
             return builder.toString();
         }
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 9df4248..5f3732a 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -356,6 +356,31 @@
     }
 
     @Override
+    public void setWallpaperZoomOut(IBinder window, float zoom) {
+        if (Float.compare(0f, zoom) > 0 || Float.compare(1f, zoom) < 0 || Float.isNaN(zoom)) {
+            throw new IllegalArgumentException("Zoom must be a valid float between 0 and 1: "
+                    + zoom);
+        }
+        synchronized (mService.mGlobalLock) {
+            long ident = Binder.clearCallingIdentity();
+            try {
+                actionOnWallpaper(window, (wpController, windowState) ->
+                        wpController.setWallpaperZoomOut(windowState, zoom));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public void setShouldZoomOutWallpaper(IBinder window, boolean shouldZoom) {
+        synchronized (mService.mGlobalLock) {
+            actionOnWallpaper(window, (wpController, windowState) ->
+                    wpController.setShouldZoomOutWallpaper(windowState, shouldZoom));
+        }
+    }
+
+    @Override
     public void wallpaperOffsetsComplete(IBinder window) {
         synchronized (mService.mGlobalLock) {
             actionOnWallpaper(window, (wpController, windowState) ->
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b2db99b..5ab5fbc 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -199,6 +199,7 @@
     private static final String ATTR_MIN_WIDTH = "min_width";
     private static final String ATTR_MIN_HEIGHT = "min_height";
     private static final String ATTR_PERSIST_TASK_VERSION = "persist_task_version";
+    private static final String ATTR_WINDOW_LAYOUT_AFFINITY = "window_layout_affinity";
 
     // Current version of the task record we persist. Used to check if we need to run any upgrade
     // code.
@@ -233,6 +234,8 @@
 
     String affinity;        // The affinity name for this task, or null; may change identity.
     String rootAffinity;    // Initial base affinity, or null; does not change from initial root.
+    String mWindowLayoutAffinity; // Launch param affinity of this task or null. Used when saving
+                                // launch params of this task.
     IVoiceInteractionSession voiceSession;    // Voice interaction session driving task
     IVoiceInteractor voiceInteractor;         // Associated interactor to provide to app
     Intent intent;          // The original intent that started the task. Note that this value can
@@ -474,6 +477,7 @@
      * taskAppeared callback, and emit a taskRemoved callback when the Task is vanished.
      */
     ITaskOrganizer mTaskOrganizer;
+    private int mLastTaskOrganizerWindowingMode = -1;
 
     /**
      * Last Picture-in-Picture params applicable to the task. Updated when the app
@@ -1000,6 +1004,8 @@
                 origActivity = new ComponentName(info.packageName, info.name);
             }
         }
+        mWindowLayoutAffinity =
+                info.windowLayout == null ? null : info.windowLayout.windowLayoutAffinity;
 
         final int intentFlags = intent == null ? 0 : intent.getFlags();
         if ((intentFlags & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
@@ -1929,6 +1935,7 @@
         // TODO: Should also take care of Pip mode changes here.
 
         saveLaunchingStateIfNeeded();
+        updateTaskOrganizerState(false /* forceUpdate */);
     }
 
     /**
@@ -3408,6 +3415,9 @@
                 pw.println();
             }
         }
+        if (mWindowLayoutAffinity != null) {
+            pw.print(prefix); pw.print("windowLayoutAffinity="); pw.println(mWindowLayoutAffinity);
+        }
         if (voiceSession != null || voiceInteractor != null) {
             pw.print(prefix); pw.print("VOICE: session=0x");
             pw.print(Integer.toHexString(System.identityHashCode(voiceSession)));
@@ -3589,6 +3599,9 @@
         } else if (rootAffinity != null) {
             out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@");
         }
+        if (mWindowLayoutAffinity != null) {
+            out.attribute(null, ATTR_WINDOW_LAYOUT_AFFINITY, mWindowLayoutAffinity);
+        }
         out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset));
         out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents));
         out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
@@ -3746,6 +3759,7 @@
             String affinity = null;
             String rootAffinity = null;
             boolean hasRootAffinity = false;
+            String windowLayoutAffinity = null;
             boolean rootHasReset = false;
             boolean autoRemoveRecents = false;
             boolean askedCompatMode = false;
@@ -3798,6 +3812,9 @@
                         rootAffinity = attrValue;
                         hasRootAffinity = true;
                         break;
+                    case ATTR_WINDOW_LAYOUT_AFFINITY:
+                        windowLayoutAffinity = attrValue;
+                        break;
                     case ATTR_ROOTHASRESET:
                         rootHasReset = Boolean.parseBoolean(attrValue);
                         break;
@@ -3953,6 +3970,7 @@
                     realActivitySuspended, userSetupComplete, minWidth, minHeight, null /*stack*/);
             task.mLastNonFullscreenBounds = lastNonFullscreenBounds;
             task.setBounds(lastNonFullscreenBounds);
+            task.mWindowLayoutAffinity = windowLayoutAffinity;
 
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                 task.addChild(activities.get(activityNdx));
@@ -4018,6 +4036,39 @@
     // Called on Binder death.
     void taskOrganizerDied() {
         mTaskOrganizer = null;
+        mLastTaskOrganizerWindowingMode = -1;
+    }
+
+    /**
+     * Called when the task state changes (ie. from windowing mode change) an the task organizer
+     * state should also be updated.
+     *
+     * @param forceUpdate Updates the task organizer to the one currently specified in the task
+     *                    org controller for the task's windowing mode, ignoring the cached
+     *                    windowing mode checks.
+     */
+    void updateTaskOrganizerState(boolean forceUpdate) {
+        if (!isRootTask()) {
+            return;
+        }
+
+        final int windowingMode = getWindowingMode();
+        if (!forceUpdate && windowingMode == mLastTaskOrganizerWindowingMode) {
+            // If our windowing mode hasn't actually changed, then just stick
+            // with our old organizer. This lets us implement the semantic
+            // where SysUI can continue to manage it's old tasks
+            // while CTS temporarily takes over the registration.
+            return;
+        }
+        /*
+         * Different windowing modes may be managed by different task organizers. If
+         * getTaskOrganizer returns null, we still call setTaskOrganizer to
+         * make sure we clear it.
+         */
+        final ITaskOrganizer org =
+                mWmService.mAtmService.mTaskOrganizerController.getTaskOrganizer(windowingMode);
+        setTaskOrganizer(org);
+        mLastTaskOrganizerWindowingMode = windowingMode;
     }
 
     @Override
@@ -4123,8 +4174,16 @@
         return mMainWindowSizeChangeTransaction;
     }
 
+    void setActivityWindowingMode(int windowingMode) {
+        PooledConsumer c = PooledLambda.obtainConsumer(ActivityRecord::setWindowingMode,
+            PooledLambda.__(ActivityRecord.class), windowingMode);
+        forAllActivities(c);
+        c.recycle();
+    }
+
     @Override
     long getProtoFieldId() {
         return TASK;
     }
+
 }
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 96a9127..e4f10d9 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -30,13 +30,15 @@
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 
+import com.android.internal.os.SomeArgs;
+
 import java.util.ArrayList;
 
 class TaskChangeNotificationController {
     private static final int LOG_STACK_STATE_MSG = 1;
     private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG = 2;
     private static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 3;
-    private static final int NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 4;
+    private static final int NOTIFY_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 4;
     private static final int NOTIFY_FORCED_RESIZABLE_MSG = 6;
     private static final int NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG = 7;
     private static final int NOTIFY_TASK_ADDED_LISTENERS_MSG = 8;
@@ -118,8 +120,10 @@
         l.onActivityUnpinned();
     };
 
-    private final TaskStackConsumer mNotifyPinnedActivityRestartAttempt = (l, m) -> {
-        l.onPinnedActivityRestartAttempt(m.arg1 != 0);
+    private final TaskStackConsumer mNotifyActivityRestartAttempt = (l, m) -> {
+        SomeArgs args = (SomeArgs) m.obj;
+        l.onActivityRestartAttempt((RunningTaskInfo) args.arg1, args.argi1 != 0,
+                args.argi2 != 0);
     };
 
     private final TaskStackConsumer mNotifyActivityForcedResizable = (l, m) -> {
@@ -220,8 +224,8 @@
                 case NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG:
                     forAllRemoteListeners(mNotifyActivityUnpinned, msg);
                     break;
-                case NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG:
-                    forAllRemoteListeners(mNotifyPinnedActivityRestartAttempt, msg);
+                case NOTIFY_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG:
+                    forAllRemoteListeners(mNotifyActivityRestartAttempt, msg);
                     break;
                 case NOTIFY_FORCED_RESIZABLE_MSG:
                     forAllRemoteListeners(mNotifyActivityForcedResizable, msg);
@@ -266,6 +270,9 @@
                     forAllRemoteListeners(mNotifyTaskFocusChanged, msg);
                     break;
             }
+            if (msg.obj instanceof SomeArgs) {
+                ((SomeArgs) msg.obj).recycle();
+            }
         }
     }
 
@@ -358,15 +365,18 @@
 
     /**
      * Notifies all listeners when an attempt was made to start an an activity that is already
-     * running in the pinned stack and the activity was not actually started, but the task is
-     * either brought to the front or a new Intent is delivered to it.
+     * running, but the task is either brought to the front or a new Intent is delivered to it.
      */
-    void notifyPinnedActivityRestartAttempt(boolean clearedTask) {
-        mHandler.removeMessages(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG);
-        final Message msg =
-                mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG,
-                        clearedTask ? 1 : 0, 0);
-        forAllLocalListeners(mNotifyPinnedActivityRestartAttempt, msg);
+    void notifyActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
+            boolean clearedTask) {
+        mHandler.removeMessages(NOTIFY_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG);
+        final SomeArgs args = SomeArgs.obtain();
+        args.arg1 = task;
+        args.argi1 = homeTaskVisible ? 1 : 0;
+        args.argi2 = clearedTask ? 1 : 0;
+        final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG,
+                        args);
+        forAllLocalListeners(mNotifyActivityRestartAttempt, msg);
         msg.sendToTarget();
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 4093fe5..9cbc9ee 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -105,7 +105,7 @@
 
 
         TaskOrganizerState(ITaskOrganizer organizer, int windowingMode,
-                TaskOrganizerState replacing) {
+                @Nullable TaskOrganizerState replacing) {
             mOrganizer = organizer;
             mDeathRecipient = new DeathRecipient(organizer, windowingMode);
             try {
@@ -203,10 +203,27 @@
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
+                if (getTaskOrganizer(windowingMode) != null) {
+                    Slog.w(TAG, "Task organizer already exists for windowing mode: "
+                            + windowingMode);
+                }
+                final TaskOrganizerState previousState =
+                        mTaskOrganizersForWindowingMode.get(windowingMode);
                 final TaskOrganizerState state = new TaskOrganizerState(organizer, windowingMode,
-                        mTaskOrganizersForWindowingMode.get(windowingMode));
+                        previousState);
                 mTaskOrganizersForWindowingMode.put(windowingMode, state);
                 mTaskOrganizerStates.put(organizer.asBinder(), state);
+
+                if (previousState == null) {
+                    // Only in the case where this is the root task organizer for the given
+                    // windowing mode, we add report all existing tasks in that mode to the new
+                    // task organizer.
+                    mService.mRootWindowContainer.forAllTasks((task) -> {
+                        if (task.getWindowingMode() == windowingMode) {
+                            task.updateTaskOrganizerState(true /* forceUpdate */);
+                        }
+                    });
+                }
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -553,18 +570,28 @@
             WindowContainerTransaction.Change c) {
         int effects = sanitizeAndApplyChange(wc, c);
 
+        final Task tr = wc.asTask();
+
         final SurfaceControl.Transaction t = c.getBoundsChangeTransaction();
         if (t != null) {
-            Task tr = (Task) wc;
             tr.setMainWindowSizeChangeTransaction(t);
         }
 
         Rect enterPipBounds = c.getEnterPipBounds();
         if (enterPipBounds != null) {
-            Task tr = (Task) wc;
             mService.mStackSupervisor.updatePictureInPictureMode(tr,
                     enterPipBounds, true);
         }
+
+        final int windowingMode = c.getWindowingMode();
+        if (windowingMode > -1) {
+            tr.setWindowingMode(windowingMode);
+        }
+        final int childWindowingMode = c.getActivityWindowingMode();
+        if (childWindowingMode > -1) {
+            tr.setActivityWindowingMode(childWindowingMode);
+        }
+
         return effects;
     }
 
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 669eb78..57d0a33 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -41,6 +41,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.ArraySet;
+import android.util.MathUtils;
 import android.util.Slog;
 import android.view.DisplayInfo;
 import android.view.SurfaceControl;
@@ -52,6 +53,7 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.function.Consumer;
 
 /**
  * Controls wallpaper windows visibility, ordering, and so on.
@@ -75,8 +77,10 @@
     private float mLastWallpaperY = -1;
     private float mLastWallpaperXStep = -1;
     private float mLastWallpaperYStep = -1;
+    private float mLastWallpaperZoomOut = 0;
     private int mLastWallpaperDisplayOffsetX = Integer.MIN_VALUE;
     private int mLastWallpaperDisplayOffsetY = Integer.MIN_VALUE;
+    private final float mMaxWallpaperScale;
 
     // This is set when we are waiting for a wallpaper to tell us it is done
     // changing its scroll position.
@@ -191,9 +195,21 @@
         return false;
     };
 
+    /**
+     * @see #computeLastWallpaperZoomOut()
+     */
+    private Consumer<WindowState>  mComputeMaxZoomOutFunction = windowState -> {
+        if (!windowState.mIsWallpaper
+                && Float.compare(windowState.mWallpaperZoomOut, mLastWallpaperZoomOut) > 0) {
+            mLastWallpaperZoomOut = windowState.mWallpaperZoomOut;
+        }
+    };
+
     WallpaperController(WindowManagerService service, DisplayContent displayContent) {
         mService = service;
         mDisplayContent = displayContent;
+        mMaxWallpaperScale = service.mContext.getResources()
+                .getFloat(com.android.internal.R.dimen.config_wallpaperMaxScale);
     }
 
     WindowState getWallpaperTarget() {
@@ -325,20 +341,30 @@
             rawChanged = true;
         }
 
-        boolean changed = wallpaperWin.mWinAnimator.setWallpaperOffset(xOffset, yOffset);
+        if (Float.compare(wallpaperWin.mWallpaperZoomOut, mLastWallpaperZoomOut) != 0) {
+            wallpaperWin.mWallpaperZoomOut = mLastWallpaperZoomOut;
+            rawChanged = true;
+        }
+
+        boolean changed = wallpaperWin.mWinAnimator.setWallpaperOffset(xOffset, yOffset,
+                wallpaperWin.mShouldScaleWallpaper
+                        ? zoomOutToScale(wallpaperWin.mWallpaperZoomOut) : 1);
 
         if (rawChanged && (wallpaperWin.mAttrs.privateFlags &
                 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) != 0) {
             try {
                 if (DEBUG_WALLPAPER) Slog.v(TAG, "Report new wp offset "
                         + wallpaperWin + " x=" + wallpaperWin.mWallpaperX
-                        + " y=" + wallpaperWin.mWallpaperY);
+                        + " y=" + wallpaperWin.mWallpaperY
+                        + " zoom=" + wallpaperWin.mWallpaperZoomOut);
                 if (sync) {
                     mWaitingOnWallpaper = wallpaperWin;
                 }
                 wallpaperWin.mClient.dispatchWallpaperOffsets(
                         wallpaperWin.mWallpaperX, wallpaperWin.mWallpaperY,
-                        wallpaperWin.mWallpaperXStep, wallpaperWin.mWallpaperYStep, sync);
+                        wallpaperWin.mWallpaperXStep, wallpaperWin.mWallpaperYStep,
+                        wallpaperWin.mWallpaperZoomOut, sync);
+
                 if (sync) {
                     if (mWaitingOnWallpaper != null) {
                         long start = SystemClock.uptimeMillis();
@@ -378,6 +404,20 @@
         }
     }
 
+    void setWallpaperZoomOut(WindowState window, float zoom) {
+        if (Float.compare(window.mWallpaperZoomOut, zoom) != 0) {
+            window.mWallpaperZoomOut = zoom;
+            updateWallpaperOffsetLocked(window, false);
+        }
+    }
+
+    void setShouldZoomOutWallpaper(WindowState window, boolean shouldZoom) {
+        if (shouldZoom != window.mShouldScaleWallpaper) {
+            window.mShouldScaleWallpaper = shouldZoom;
+            updateWallpaperOffsetLocked(window, false);
+        }
+    }
+
     void setWindowWallpaperDisplayOffset(WindowState window, int x, int y) {
         if (window.mWallpaperDisplayOffsetX != x || window.mWallpaperDisplayOffsetY != y)  {
             window.mWallpaperDisplayOffsetX = x;
@@ -420,6 +460,7 @@
             } else if (changingTarget.mWallpaperY >= 0) {
                 mLastWallpaperY = changingTarget.mWallpaperY;
             }
+            computeLastWallpaperZoomOut();
             if (target.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
                 mLastWallpaperDisplayOffsetX = target.mWallpaperDisplayOffsetX;
             } else if (changingTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
@@ -593,6 +634,9 @@
                 mLastWallpaperX = mWallpaperTarget.mWallpaperX;
                 mLastWallpaperXStep = mWallpaperTarget.mWallpaperXStep;
             }
+            if (mWallpaperTarget.mWallpaperZoomOut >= 0) {
+                mLastWallpaperZoomOut = mWallpaperTarget.mWallpaperZoomOut;
+            }
             if (mWallpaperTarget.mWallpaperY >= 0) {
                 mLastWallpaperY = mWallpaperTarget.mWallpaperY;
                 mLastWallpaperYStep = mWallpaperTarget.mWallpaperYStep;
@@ -762,6 +806,23 @@
         return mTmpTopWallpaper;
     }
 
+    /**
+     * Each window can request a zoom, example:
+     * - User is in overview, zoomed out.
+     * - User also pulls down the shade.
+     *
+     * This means that we always have to choose the largest zoom out that we have, otherwise
+     * we'll have conflicts and break the "depth system" mental model.
+     */
+    private void computeLastWallpaperZoomOut() {
+        mLastWallpaperZoomOut = 0;
+        mDisplayContent.forAllWindows(mComputeMaxZoomOutFunction, true);
+    }
+
+    private float zoomOutToScale(float zoom) {
+        return MathUtils.lerp(1, mMaxWallpaperScale, 1 - zoom);
+    }
+
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("displayId="); pw.println(mDisplayContent.getDisplayId());
         pw.print(prefix); pw.print("mWallpaperTarget="); pw.println(mWallpaperTarget);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index e8897e1..ec90097 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -275,7 +275,7 @@
     RemoteToken mRemoteToken = null;
 
     BLASTSyncEngine mBLASTSyncEngine = new BLASTSyncEngine();
-    SurfaceControl.Transaction mBLASTSyncTransaction = new SurfaceControl.Transaction();
+    SurfaceControl.Transaction mBLASTSyncTransaction;
     boolean mUsingBLASTSyncTransaction = false;
     BLASTSyncEngine.TransactionReadyListener mWaitingListener;
     int mWaitingSyncId;
@@ -283,6 +283,7 @@
     WindowContainer(WindowManagerService wms) {
         mWmService = wms;
         mPendingTransaction = wms.mTransactionFactory.get();
+        mBLASTSyncTransaction = wms.mTransactionFactory.get();
         mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);
         mSurfaceFreezer = new SurfaceFreezer(this, wms);
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 42a66ad..ecbbb03 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2447,10 +2447,6 @@
             // of a transaction to avoid artifacts.
             win.mAnimatingExit = true;
         } else {
-            final DisplayContent displayContent = win.getDisplayContent();
-            if (displayContent.mInputMethodWindow == win) {
-                displayContent.setInputMethodWindowLocked(null);
-            }
             boolean stopped = win.mActivityRecord != null ? win.mActivityRecord.mAppStopped : true;
             // We set mDestroying=true so ActivityRecord#notifyAppStopped in-to destroy surfaces
             // will later actually destroy the surface if we do not do so here. Normally we leave
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 833bb83..32eb932 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -53,6 +53,7 @@
 import android.content.res.Configuration;
 import android.os.Build;
 import android.os.Message;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.ArraySet;
@@ -200,6 +201,9 @@
     /** Whether our process is currently running a {@link IRemoteAnimationRunner} */
     private boolean mRunningRemoteAnimation;
 
+    /** Whether this process is owned by the System UI package. */
+    final boolean mIsSysUiPackage;
+
     public WindowProcessController(@NonNull ActivityTaskManagerService atm, ApplicationInfo info,
             String name, int uid, int userId, Object owner, WindowProcessListener listener) {
         mInfo = info;
@@ -210,6 +214,10 @@
         mListener = listener;
         mAtm = atm;
         mDisplayId = INVALID_DISPLAY;
+
+        mIsSysUiPackage = info.packageName.equals(
+                mAtm.getSysUiServiceComponentLocked().getPackageName());
+
         onConfigurationChanged(atm.getGlobalConfiguration());
     }
 
@@ -1077,6 +1085,12 @@
      * always track the configuration of the non-finishing activity last added to the process.
      */
     private void updateActivityConfigurationListener() {
+        if (mIsSysUiPackage || mUid == Process.SYSTEM_UID) {
+            // This is a system owned process and should not use an activity config.
+            // TODO(b/151161907): Remove after support for display-independent (raw) SysUi configs.
+            return;
+        }
+
         for (int i = mActivities.size() - 1; i >= 0; i--) {
             final ActivityRecord activityRecord = mActivities.get(i);
             if (!activityRecord.finishing && !activityRecord.containsListener(this)) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index fc28cd5..c44be4d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -415,6 +415,7 @@
     float mHScale=1, mVScale=1;
     float mLastHScale=1, mLastVScale=1;
     final Matrix mTmpMatrix = new Matrix();
+    final float[] mTmpMatrixArray = new float[9];
 
     private final WindowFrames mWindowFrames = new WindowFrames();
 
@@ -446,6 +447,14 @@
     float mWallpaperX = -1;
     float mWallpaperY = -1;
 
+    // If a window showing a wallpaper: the requested zoom out for the
+    // wallpaper; if a wallpaper window: the currently applied zoom.
+    float mWallpaperZoomOut = -1;
+
+    // If a wallpaper window: whether the wallpaper should be scaled when zoomed, if set
+    // to false, mWallpaperZoom will be ignored here and just passed to the WallpaperService.
+    boolean mShouldScaleWallpaper;
+
     // If a window showing a wallpaper: what fraction of the offset
     // range corresponds to a full virtual screen.
     float mWallpaperXStep = -1;
@@ -3923,6 +3932,9 @@
             pw.println(prefix + "mWallpaperXStep=" + mWallpaperXStep
                     + " mWallpaperYStep=" + mWallpaperYStep);
         }
+        if (mWallpaperZoomOut != -1) {
+            pw.println(prefix + "mWallpaperZoomOut=" + mWallpaperZoomOut);
+        }
         if (mWallpaperDisplayOffsetX != Integer.MIN_VALUE
                 || mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
             pw.println(prefix + "mWallpaperDisplayOffsetX=" + mWallpaperDisplayOffsetX
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 79dc536..563710b 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -16,6 +16,12 @@
 
 package com.android.server.wm;
 
+import static android.graphics.Matrix.MSCALE_X;
+import static android.graphics.Matrix.MSCALE_Y;
+import static android.graphics.Matrix.MSKEW_X;
+import static android.graphics.Matrix.MSKEW_Y;
+import static android.graphics.Matrix.MTRANS_X;
+import static android.graphics.Matrix.MTRANS_Y;
 import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
 import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
@@ -217,6 +223,10 @@
     int mXOffset = 0;
     int mYOffset = 0;
 
+    // A scale factor for the surface contents, that will be applied from the center of the visible
+    // region.
+    float mWallpaperScale = 1f;
+
     /**
      * A flag to determine if the WSA needs to offset its position to compensate for the stack's
      * position update before the WSA surface has resized.
@@ -1034,7 +1044,12 @@
                         }
                     }
                 }
-                mSurfaceController.setPositionInTransaction(xOffset, yOffset, recoveringMemory);
+                if (!mIsWallpaper) {
+                    mSurfaceController.setPositionInTransaction(xOffset, yOffset, recoveringMemory);
+                } else {
+                    setWallpaperPositionAndScale(
+                            xOffset, yOffset, mWallpaperScale, recoveringMemory);
+                }
             }
         }
 
@@ -1051,11 +1066,15 @@
 
 
         if (!w.mSeamlesslyRotated) {
-            applyCrop(clipRect, recoveringMemory);
-            mSurfaceController.setMatrixInTransaction(mDsDx * w.mHScale * mExtraHScale,
-                    mDtDx * w.mVScale * mExtraVScale,
-                    mDtDy * w.mHScale * mExtraHScale,
-                    mDsDy * w.mVScale * mExtraVScale, recoveringMemory);
+            if (!mIsWallpaper) {
+                applyCrop(clipRect, recoveringMemory);
+                mSurfaceController.setMatrixInTransaction(mDsDx * w.mHScale * mExtraHScale,
+                        mDtDx * w.mVScale * mExtraVScale,
+                        mDtDy * w.mHScale * mExtraHScale,
+                        mDsDy * w.mVScale * mExtraVScale, recoveringMemory);
+            } else {
+                setWallpaperPositionAndScale(mXOffset, mYOffset, mWallpaperScale, recoveringMemory);
+            }
         }
 
         if (mSurfaceResized) {
@@ -1202,18 +1221,18 @@
         mSurfaceController.setTransparentRegionHint(region);
     }
 
-    boolean setWallpaperOffset(int dx, int dy) {
-        if (mXOffset == dx && mYOffset == dy) {
+    boolean setWallpaperOffset(int dx, int dy, float scale) {
+        if (mXOffset == dx && mYOffset == dy && Float.compare(mWallpaperScale, scale) == 0) {
             return false;
         }
         mXOffset = dx;
         mYOffset = dy;
+        mWallpaperScale = scale;
 
         try {
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setWallpaperOffset");
             mService.openSurfaceTransaction();
-            mSurfaceController.setPositionInTransaction(dx, dy, false);
-            applyCrop(null, false);
+            setWallpaperPositionAndScale(dx, dy, scale, false);
         } catch (RuntimeException e) {
             Slog.w(TAG, "Error positioning surface of " + mWin
                     + " pos=(" + dx + "," + dy + ")", e);
@@ -1225,6 +1244,27 @@
         }
     }
 
+    private void setWallpaperPositionAndScale(int dx, int dy, float scale,
+            boolean recoveringMemory) {
+        DisplayInfo displayInfo = mWin.getDisplayInfo();
+        Matrix matrix = mWin.mTmpMatrix;
+        matrix.setTranslate(dx, dy);
+        matrix.postScale(scale, scale, displayInfo.logicalWidth / 2f,
+                displayInfo.logicalHeight / 2f);
+        matrix.getValues(mWin.mTmpMatrixArray);
+        matrix.reset();
+
+        mSurfaceController.setPositionInTransaction(mWin.mTmpMatrixArray[MTRANS_X],
+                mWin.mTmpMatrixArray[MTRANS_Y], recoveringMemory);
+        mSurfaceController.setMatrixInTransaction(
+                mDsDx * mWin.mTmpMatrixArray[MSCALE_X] * mWin.mHScale * mExtraHScale,
+                mDtDx * mWin.mTmpMatrixArray[MSKEW_Y] * mWin.mVScale * mExtraVScale,
+                mDtDy * mWin.mTmpMatrixArray[MSKEW_X] * mWin.mHScale * mExtraHScale,
+                mDsDy * mWin.mTmpMatrixArray[MSCALE_Y] * mWin.mVScale * mExtraVScale,
+                recoveringMemory);
+        applyCrop(null, recoveringMemory);
+    }
+
     /**
      * Try to change the pixel format without recreating the surface. This
      * will be common in the case of changing from PixelFormat.OPAQUE to
diff --git a/services/core/jni/com_android_server_tv_GamepadKeys.h b/services/core/jni/com_android_server_tv_GamepadKeys.h
new file mode 100644
index 0000000..11fc903
--- /dev/null
+++ b/services/core/jni/com_android_server_tv_GamepadKeys.h
@@ -0,0 +1,79 @@
+#ifndef ANDROIDTVREMOTE_SERVICE_JNI_GAMEPAD_KEYS_H_
+#define ANDROIDTVREMOTE_SERVICE_JNI_GAMEPAD_KEYS_H_
+
+#include <linux/input.h>
+
+namespace android {
+
+// Follows the W3 spec for gamepad buttons and their corresponding mapping into
+// Linux keycodes. Note that gamepads are generally not very well standardized
+// and various controllers will result in different buttons. This mapping tries
+// to be reasonable.
+//
+// W3 Button spec: https://www.w3.org/TR/gamepad/#remapping
+//
+// Standard gamepad keycodes are added plus 2 additional buttons (e.g. Stadia
+// has "Assistant" and "Share", PS4 has the touchpad button).
+//
+// To generate this list, PS4, XBox, Stadia and Nintendo Switch Pro were tested.
+static const int GAMEPAD_KEY_CODES[19] = {
+        // Right-side buttons. A/B/X/Y or circle/triangle/square/X or similar
+        BTN_A, // "South", A, GAMEPAD and SOUTH have the same constant
+        BTN_B, // "East", BTN_B, BTN_EAST have the same constant
+        BTN_X, // "West", Note that this maps to X and NORTH in constants
+        BTN_Y, // "North", Note that this maps to Y and WEST in constants
+
+        BTN_TL, // "Left Bumper" / "L1" - Nintendo sends BTN_WEST instead
+        BTN_TR, // "Right Bumper" / "R1" - Nintendo sends BTN_Z instead
+
+        // For triggers, gamepads vary:
+        //   - Stadia sends analog values over ABS_GAS/ABS_BRAKE and sends
+        //     TriggerHappy3/4 as digital presses
+        //   - PS4 and Xbox send analog values as ABS_Z/ABS_RZ
+        //   - Nintendo Pro sends BTN_TL/BTN_TR (since bumpers behave differently)
+        // As placeholders we chose the stadia trigger-happy values since TL/TR are
+        // sent for bumper button presses
+        BTN_TRIGGER_HAPPY4, // "Left Trigger" / "L2"
+        BTN_TRIGGER_HAPPY3, // "Right Trigger" / "R2"
+
+        BTN_SELECT, // "Select/Back". Often "options" or similar
+        BTN_START,  // "Start/forward". Often "hamburger" icon
+
+        BTN_THUMBL, // "Left Joystick Pressed"
+        BTN_THUMBR, // "Right Joystick Pressed"
+
+        // For DPads, gamepads generally only send axis changes
+        // on ABS_HAT0X and ABS_HAT0Y.
+        KEY_UP,    // "Digital Pad up"
+        KEY_DOWN,  // "Digital Pad down"
+        KEY_LEFT,  // "Digital Pad left"
+        KEY_RIGHT, // "Digital Pad right"
+
+        BTN_MODE, // "Main button" (Stadia/PS/XBOX/Home)
+
+        BTN_TRIGGER_HAPPY1, // Extra button: "Assistant" for Stadia
+        BTN_TRIGGER_HAPPY2, // Extra button: "Share" for Stadia
+};
+
+// Defines information for an axis.
+struct Axis {
+    int number;
+    int rangeMin;
+    int rangeMax;
+};
+
+// List of all axes supported by a gamepad
+static const Axis GAMEPAD_AXES[] = {
+        {ABS_X, 0, 254},    // Left joystick X
+        {ABS_Y, 0, 254},    // Left joystick Y
+        {ABS_RX, 0, 254},   // Right joystick X
+        {ABS_RY, 0, 254},   // Right joystick Y
+        {ABS_Z, 0, 254},    // Left trigger
+        {ABS_RZ, 0, 254},   // Right trigger
+        {ABS_HAT0X, -1, 1}, // DPad X
+        {ABS_HAT0Y, -1, 1}, // DPad Y
+};
+
+} // namespace android
+
+#endif // ANDROIDTVREMOTE_SERVICE_JNI_GAMEPAD_KEYS_H_
diff --git a/services/core/jni/com_android_server_tv_TvKeys.h b/services/core/jni/com_android_server_tv_TvKeys.h
index 4895f34..7eacdf6 100644
--- a/services/core/jni/com_android_server_tv_TvKeys.h
+++ b/services/core/jni/com_android_server_tv_TvKeys.h
@@ -110,4 +110,4 @@
 
 } // namespace android
 
-#endif // SERVICE_JNI_KEYS_H_
+#endif // ANDROIDTVREMOTE_SERVICE_JNI_KEYS_H_
diff --git a/services/core/jni/com_android_server_tv_TvUinputBridge.cpp b/services/core/jni/com_android_server_tv_TvUinputBridge.cpp
index c832c18..0e96bd7 100644
--- a/services/core/jni/com_android_server_tv_TvUinputBridge.cpp
+++ b/services/core/jni/com_android_server_tv_TvUinputBridge.cpp
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "TvRemote-native-uiBridge"
 
+#include "com_android_server_tv_GamepadKeys.h"
 #include "com_android_server_tv_TvKeys.h"
 
 #include "jni.h"
@@ -92,27 +93,156 @@
     }
 }
 
+static const int kInvalidFileDescriptor = -1;
+
+// Convenience class to manage an opened /dev/uinput device
+class UInputDescriptor {
+public:
+    UInputDescriptor() : mFd(kInvalidFileDescriptor) {
+        memset(&mUinputDescriptor, 0, sizeof(mUinputDescriptor));
+    }
+
+    // Auto-closes any open /dev/uinput descriptor unless detached.
+    ~UInputDescriptor();
+
+    // Open /dev/uinput and prepare to register
+    // the device with the given name and unique Id
+    bool Open(const char* name, const char* uniqueId);
+
+    // Checks if the current file descriptor is valid
+    bool IsValid() const { return mFd != kInvalidFileDescriptor; }
+
+    void EnableKey(int keyCode);
+
+    void EnableAxesEvents();
+    void EnableAxis(int axis, int rangeMin, int rangeMax);
+
+    bool Create();
+
+    // Detaches from the current file descriptor
+    // Returns the file descriptor for /dev/uniput
+    int Detach();
+
+private:
+    int mFd;
+    struct uinput_user_dev mUinputDescriptor;
+};
+
+UInputDescriptor::~UInputDescriptor() {
+    if (mFd != kInvalidFileDescriptor) {
+        close(mFd);
+        mFd = kInvalidFileDescriptor;
+    }
+}
+
+int UInputDescriptor::Detach() {
+    int fd = mFd;
+    mFd = kInvalidFileDescriptor;
+    return fd;
+}
+
+bool UInputDescriptor::Open(const char* name, const char* uniqueId) {
+    if (IsValid()) {
+        ALOGE("UInput device already open");
+        return false;
+    }
+
+    mFd = ::open("/dev/uinput", O_WRONLY | O_NDELAY);
+    if (mFd < 0) {
+        ALOGE("Cannot open /dev/uinput: %s.", strerror(errno));
+        mFd = kInvalidFileDescriptor;
+        return false;
+    }
+
+    // write device unique id to the phys property
+    ioctl(mFd, UI_SET_PHYS, uniqueId);
+
+    memset(&mUinputDescriptor, 0, sizeof(mUinputDescriptor));
+    strlcpy(mUinputDescriptor.name, name, UINPUT_MAX_NAME_SIZE);
+    mUinputDescriptor.id.version = 1;
+    mUinputDescriptor.id.bustype = BUS_VIRTUAL;
+
+    // All UInput devices we use process keys
+    ioctl(mFd, UI_SET_EVBIT, EV_KEY);
+
+    return true;
+}
+
+void UInputDescriptor::EnableKey(int keyCode) {
+    ioctl(mFd, UI_SET_KEYBIT, keyCode);
+}
+
+void UInputDescriptor::EnableAxesEvents() {
+    ioctl(mFd, UI_SET_EVBIT, EV_ABS);
+}
+
+void UInputDescriptor::EnableAxis(int axis, int rangeMin, int rangeMax) {
+    if ((axis < 0) || (axis >= NELEM(mUinputDescriptor.absmin))) {
+        ALOGE("Invalid axis number: %d", axis);
+        return;
+    }
+
+    if (ioctl(mFd, UI_SET_ABSBIT, axis) != 0) {
+        ALOGE("Failed to set absbit for %d", axis);
+    }
+
+    mUinputDescriptor.absmin[axis] = rangeMin;
+    mUinputDescriptor.absmax[axis] = rangeMax;
+    mUinputDescriptor.absfuzz[axis] = 0;
+    mUinputDescriptor.absflat[axis] = 0;
+}
+
+bool UInputDescriptor::Create() {
+    // register the input device
+    if (write(mFd, &mUinputDescriptor, sizeof(mUinputDescriptor)) != sizeof(mUinputDescriptor)) {
+        ALOGE("Cannot write uinput_user_dev to fd %d: %s.", mFd, strerror(errno));
+        return false;
+    }
+
+    if (ioctl(mFd, UI_DEV_CREATE) != 0) {
+        ALOGE("Unable to create uinput device: %s.", strerror(errno));
+        return false;
+    }
+
+    ALOGV("Created uinput device, fd=%d.", mFd);
+
+    return true;
+}
+
 class NativeConnection {
 public:
+    enum class ConnectionType {
+        kRemoteDevice,
+        kGamepadDevice,
+    };
+
     ~NativeConnection();
 
     static NativeConnection* open(const char* name, const char* uniqueId,
             int32_t width, int32_t height, int32_t maxPointerId);
 
+    static NativeConnection* openGamepad(const char* name, const char* uniqueId);
+
     void sendEvent(int32_t type, int32_t code, int32_t value);
 
     int32_t getMaxPointers() const { return mMaxPointers; }
 
+    ConnectionType getType() const { return mType; }
+
+    bool IsGamepad() const { return getType() == ConnectionType::kGamepadDevice; }
+
+    bool IsRemote() const { return getType() == ConnectionType::kRemoteDevice; }
+
 private:
-    NativeConnection(int fd, int32_t maxPointers);
+    NativeConnection(int fd, int32_t maxPointers, ConnectionType type);
 
     const int mFd;
     const int32_t mMaxPointers;
+    const ConnectionType mType;
 };
 
-NativeConnection::NativeConnection(int fd, int32_t maxPointers) :
-        mFd(fd), mMaxPointers(maxPointers) {
-}
+NativeConnection::NativeConnection(int fd, int32_t maxPointers, ConnectionType type)
+      : mFd(fd), mMaxPointers(maxPointers), mType(type) {}
 
 NativeConnection::~NativeConnection() {
     ALOGI("Un-Registering uinput device %d.", mFd);
@@ -125,44 +255,50 @@
     ALOGI("Registering uinput device %s: touch pad size %dx%d, "
             "max pointers %d.", name, width, height, maxPointers);
 
-    int fd = ::open("/dev/uinput", O_WRONLY | O_NDELAY);
-    if (fd < 0) {
-        ALOGE("Cannot open /dev/uinput: %s.", strerror(errno));
-        return nullptr;
-    }
-
-    struct uinput_user_dev uinp;
-    memset(&uinp, 0, sizeof(struct uinput_user_dev));
-    strlcpy(uinp.name, name, UINPUT_MAX_NAME_SIZE);
-    uinp.id.version = 1;
-    uinp.id.bustype = BUS_VIRTUAL;
-
-    // initialize keymap
     initKeysMap();
 
-    // write device unique id to the phys property
-    ioctl(fd, UI_SET_PHYS, uniqueId);
-
-    // set the keys mapped
-    ioctl(fd, UI_SET_EVBIT, EV_KEY);
-    for (size_t i = 0; i < NELEM(KEYS); i++) {
-        ioctl(fd, UI_SET_KEYBIT, KEYS[i].linuxKeyCode);
-    }
-
-    // register the input device
-    if (write(fd, &uinp, sizeof(uinp)) != sizeof(uinp)) {
-        ALOGE("Cannot write uinput_user_dev to fd %d: %s.", fd, strerror(errno));
-        close(fd);
-        return NULL;
-    }
-    if (ioctl(fd, UI_DEV_CREATE) != 0) {
-        ALOGE("Unable to create uinput device: %s.", strerror(errno));
-        close(fd);
+    UInputDescriptor descriptor;
+    if (!descriptor.Open(name, uniqueId)) {
         return nullptr;
     }
 
-    ALOGV("Created uinput device, fd=%d.", fd);
-    return new NativeConnection(fd, maxPointers);
+    // set the keys mapped
+    for (size_t i = 0; i < NELEM(KEYS); i++) {
+        descriptor.EnableKey(KEYS[i].linuxKeyCode);
+    }
+
+    if (!descriptor.Create()) {
+        return nullptr;
+    }
+
+    return new NativeConnection(descriptor.Detach(), maxPointers, ConnectionType::kRemoteDevice);
+}
+
+NativeConnection* NativeConnection::openGamepad(const char* name, const char* uniqueId) {
+    ALOGI("Registering uinput device %s: gamepad", name);
+
+    UInputDescriptor descriptor;
+    if (!descriptor.Open(name, uniqueId)) {
+        return nullptr;
+    }
+
+    // set the keys mapped for gamepads
+    for (size_t i = 0; i < NELEM(GAMEPAD_KEY_CODES); i++) {
+        descriptor.EnableKey(GAMEPAD_KEY_CODES[i]);
+    }
+
+    // define the axes that are required
+    descriptor.EnableAxesEvents();
+    for (size_t i = 0; i < NELEM(GAMEPAD_AXES); i++) {
+        const Axis& axis = GAMEPAD_AXES[i];
+        descriptor.EnableAxis(axis.number, axis.rangeMin, axis.rangeMax);
+    }
+
+    if (!descriptor.Create()) {
+        return nullptr;
+    }
+
+    return new NativeConnection(descriptor.Detach(), 0, ConnectionType::kGamepadDevice);
 }
 
 void NativeConnection::sendEvent(int32_t type, int32_t code, int32_t value) {
@@ -174,7 +310,6 @@
     write(mFd, &iev, sizeof(iev));
 }
 
-
 static jlong nativeOpen(JNIEnv* env, jclass clazz,
         jstring nameStr, jstring uniqueIdStr,
         jint width, jint height, jint maxPointers) {
@@ -186,6 +321,14 @@
     return reinterpret_cast<jlong>(connection);
 }
 
+static jlong nativeGamepadOpen(JNIEnv* env, jclass clazz, jstring nameStr, jstring uniqueIdStr) {
+    ScopedUtfChars name(env, nameStr);
+    ScopedUtfChars uniqueId(env, uniqueIdStr);
+
+    NativeConnection* connection = NativeConnection::openGamepad(name.c_str(), uniqueId.c_str());
+    return reinterpret_cast<jlong>(connection);
+}
+
 static void nativeClose(JNIEnv* env, jclass clazz, jlong ptr) {
     NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
     delete connection;
@@ -194,6 +337,12 @@
 static void nativeSendKey(JNIEnv* env, jclass clazz, jlong ptr, jint keyCode, jboolean down) {
     int32_t code = getLinuxKeyCode(keyCode);
     NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
+
+    if (connection->IsGamepad()) {
+        ALOGE("Invalid key even for a gamepad - need to send gamepad events");
+        return;
+    }
+
     if (code != KEY_UNKNOWN) {
         connection->sendEvent(EV_KEY, code, down ? 1 : 0);
     } else {
@@ -201,10 +350,44 @@
     }
 }
 
+static void nativeSendGamepadKey(JNIEnv* env, jclass clazz, jlong ptr, jint keyIndex,
+                                 jboolean down) {
+    NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
+
+    if (!connection->IsGamepad()) {
+        ALOGE("Invalid gamepad key for non-gamepad device");
+        return;
+    }
+
+    if ((keyIndex < 0) || (keyIndex >= NELEM(GAMEPAD_KEY_CODES))) {
+        ALOGE("Invalid gamepad key index: %d", keyIndex);
+        return;
+    }
+
+    connection->sendEvent(EV_KEY, GAMEPAD_KEY_CODES[keyIndex], down ? 1 : 0);
+}
+
+static void nativeSendGamepadAxisValue(JNIEnv* env, jclass clazz, jlong ptr, jint axis,
+                                       jint value) {
+    NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
+
+    if (!connection->IsGamepad()) {
+        ALOGE("Invalid axis send for non-gamepad device");
+        return;
+    }
+
+    connection->sendEvent(EV_ABS, axis, value);
+}
+
 static void nativeSendPointerDown(JNIEnv* env, jclass clazz, jlong ptr,
         jint pointerId, jint x, jint y) {
     NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
 
+    if (connection->IsGamepad()) {
+        ALOGE("Invalid pointer down event for a gamepad.");
+        return;
+    }
+
     int32_t slot = findSlot(pointerId);
     if (slot == SLOT_UNKNOWN) {
         slot = assignSlot(pointerId);
@@ -221,6 +404,11 @@
         jint pointerId) {
     NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
 
+    if (connection->IsGamepad()) {
+        ALOGE("Invalid pointer up event for a gamepad.");
+        return;
+    }
+
     int32_t slot = findSlot(pointerId);
     if (slot != SLOT_UNKNOWN) {
         connection->sendEvent(EV_ABS, ABS_MT_SLOT, slot);
@@ -238,17 +426,34 @@
     NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
 
     // Clear keys.
-    for (size_t i = 0; i < NELEM(KEYS); i++) {
-        connection->sendEvent(EV_KEY, KEYS[i].linuxKeyCode, 0);
-    }
+    if (connection->IsRemote()) {
+        for (size_t i = 0; i < NELEM(KEYS); i++) {
+            connection->sendEvent(EV_KEY, KEYS[i].linuxKeyCode, 0);
+        }
 
-    // Clear pointers.
-    int32_t slot = SLOT_UNKNOWN;
-    for (int32_t i = 0; i < connection->getMaxPointers(); i++) {
-        slot = findSlot(i);
-        if (slot != SLOT_UNKNOWN) {
-            connection->sendEvent(EV_ABS, ABS_MT_SLOT, slot);
-            connection->sendEvent(EV_ABS, ABS_MT_TRACKING_ID, -1);
+        // Clear pointers.
+        int32_t slot = SLOT_UNKNOWN;
+        for (int32_t i = 0; i < connection->getMaxPointers(); i++) {
+            slot = findSlot(i);
+            if (slot != SLOT_UNKNOWN) {
+                connection->sendEvent(EV_ABS, ABS_MT_SLOT, slot);
+                connection->sendEvent(EV_ABS, ABS_MT_TRACKING_ID, -1);
+            }
+        }
+    } else {
+        for (size_t i = 0; i < NELEM(GAMEPAD_KEY_CODES); i++) {
+            connection->sendEvent(EV_KEY, GAMEPAD_KEY_CODES[i], 0);
+        }
+
+        for (size_t i = 0; i < NELEM(GAMEPAD_AXES); i++) {
+            const Axis& axis = GAMEPAD_AXES[i];
+            if ((axis.number == ABS_Z) || (axis.number == ABS_RZ)) {
+                // Mark triggers unpressed
+                connection->sendEvent(EV_ABS, axis.number, 0);
+            } else {
+                // Joysticks and dpad rests on center
+                connection->sendEvent(EV_ABS, axis.number, (axis.rangeMin + axis.rangeMax) / 2);
+            }
         }
     }
 
@@ -261,20 +466,16 @@
  */
 
 static JNINativeMethod gUinputBridgeMethods[] = {
-    { "nativeOpen", "(Ljava/lang/String;Ljava/lang/String;III)J",
-        (void*)nativeOpen },
-    { "nativeClose", "(J)V",
-        (void*)nativeClose },
-    { "nativeSendKey", "(JIZ)V",
-        (void*)nativeSendKey },
-    { "nativeSendPointerDown", "(JIII)V",
-        (void*)nativeSendPointerDown },
-    { "nativeSendPointerUp", "(JI)V",
-        (void*)nativeSendPointerUp },
-    { "nativeClear", "(J)V",
-        (void*)nativeClear },
-    { "nativeSendPointerSync", "(J)V",
-        (void*)nativeSendPointerSync },
+        {"nativeOpen", "(Ljava/lang/String;Ljava/lang/String;III)J", (void*)nativeOpen},
+        {"nativeGamepadOpen", "(Ljava/lang/String;Ljava/lang/String;)J", (void*)nativeGamepadOpen},
+        {"nativeClose", "(J)V", (void*)nativeClose},
+        {"nativeSendKey", "(JIZ)V", (void*)nativeSendKey},
+        {"nativeSendPointerDown", "(JIII)V", (void*)nativeSendPointerDown},
+        {"nativeSendPointerUp", "(JI)V", (void*)nativeSendPointerUp},
+        {"nativeClear", "(J)V", (void*)nativeClear},
+        {"nativeSendPointerSync", "(J)V", (void*)nativeSendPointerSync},
+        {"nativeSendGamepadKey", "(JIZ)V", (void*)nativeSendGamepadKey},
+        {"nativeSendGamepadAxisValue", "(JII)V", (void*)nativeSendGamepadAxisValue},
 };
 
 int register_android_server_tv_TvUinputBridge(JNIEnv* env) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 312d2d2..6f41631 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -86,6 +86,7 @@
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
+import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
 import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
 import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
 import static android.provider.Telephony.Carriers.DPC_URI;
@@ -1198,7 +1199,7 @@
         // Whether the admin explicitly requires personal apps to be suspended
         boolean mSuspendPersonalApps = false;
         // Maximum time the profile owned by this admin can be off.
-        long mProfileMaximumTimeOff = 0;
+        long mProfileMaximumTimeOffMillis = 0;
         // Time by which the profile should be turned on according to System.currentTimeMillis().
         long mProfileOffDeadline = 0;
 
@@ -1439,10 +1440,11 @@
             if (mSuspendPersonalApps) {
                 writeAttributeValueToXml(out, TAG_SUSPEND_PERSONAL_APPS, mSuspendPersonalApps);
             }
-            if (mProfileMaximumTimeOff != 0) {
-                writeAttributeValueToXml(out, TAG_PROFILE_MAXIMUM_TIME_OFF, mProfileMaximumTimeOff);
+            if (mProfileMaximumTimeOffMillis != 0) {
+                writeAttributeValueToXml(out, TAG_PROFILE_MAXIMUM_TIME_OFF,
+                        mProfileMaximumTimeOffMillis);
             }
-            if (mProfileMaximumTimeOff != 0) {
+            if (mProfileMaximumTimeOffMillis != 0) {
                 writeAttributeValueToXml(out, TAG_PROFILE_OFF_DEADLINE, mProfileOffDeadline);
             }
             if (!TextUtils.isEmpty(mAlwaysOnVpnPackage)) {
@@ -1691,7 +1693,7 @@
                     mSuspendPersonalApps = Boolean.parseBoolean(
                             parser.getAttributeValue(null, ATTR_VALUE));
                 } else if (TAG_PROFILE_MAXIMUM_TIME_OFF.equals(tag)) {
-                    mProfileMaximumTimeOff =
+                    mProfileMaximumTimeOffMillis =
                             Long.parseLong(parser.getAttributeValue(null, ATTR_VALUE));
                 } else if (TAG_PROFILE_OFF_DEADLINE.equals(tag)) {
                     mProfileOffDeadline =
@@ -1929,8 +1931,8 @@
                 pw.println(mCrossProfilePackages);
             pw.print("mSuspendPersonalApps=");
                 pw.println(mSuspendPersonalApps);
-            pw.print("mProfileMaximumTimeOff=");
-                pw.println(mProfileMaximumTimeOff);
+            pw.print("mProfileMaximumTimeOffMillis=");
+                pw.println(mProfileMaximumTimeOffMillis);
             pw.print("mProfileOffDeadline=");
                 pw.println(mProfileOffDeadline);
             pw.print("mAlwaysOnVpnPackage=");
@@ -5874,6 +5876,14 @@
         }
     }
 
+    private void enforceNetworkStackOrProfileOrDeviceOwner(ComponentName who) {
+        if (mContext.checkCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK)
+                == PackageManager.PERMISSION_GRANTED) {
+            return;
+        }
+        enforceProfileOrDeviceOwner(who);
+    }
+
     private void enforceDeviceOwnerOrProfileOwnerOnOrganizationOwnedDevice(ComponentName who) {
         synchronized (getLockObject()) {
             getActiveAdminForCallerLocked(
@@ -6870,7 +6880,7 @@
 
     @Override
     public boolean isAlwaysOnVpnLockdownEnabled(ComponentName admin) throws SecurityException {
-        enforceProfileOrDeviceOwner(admin);
+        enforceNetworkStackOrProfileOrDeviceOwner(admin);
 
         final int userId = mInjector.userHandleGetCallingUserId();
         return mInjector.binderWithCleanCallingIdentity(
@@ -7786,7 +7796,7 @@
      * Set whether auto time is enabled on the device.
      */
     @Override
-    public void setAutoTime(ComponentName who, boolean enabled) {
+    public void setAutoTimeEnabled(ComponentName who, boolean enabled) {
         if (!mHasFeature) {
             return;
         }
@@ -7807,7 +7817,7 @@
      * Returns whether auto time is used on the device or not.
      */
     @Override
-    public boolean getAutoTime(ComponentName who) {
+    public boolean getAutoTimeEnabled(ComponentName who) {
         if (!mHasFeature) {
             return false;
         }
@@ -7821,7 +7831,7 @@
      * Set whether auto time zone is enabled on the device.
      */
     @Override
-    public void setAutoTimeZone(ComponentName who, boolean enabled) {
+    public void setAutoTimeZoneEnabled(ComponentName who, boolean enabled) {
         if (!mHasFeature) {
             return;
         }
@@ -7842,7 +7852,7 @@
      * Returns whether auto time zone is used on the device or not.
      */
     @Override
-    public boolean getAutoTimeZone(ComponentName who) {
+    public boolean getAutoTimeZoneEnabled(ComponentName who) {
         if (!mHasFeature) {
             return false;
         }
@@ -15716,18 +15726,18 @@
         }
         boolean shouldSaveSettings = false;
         if (profileOwner.mProfileOffDeadline != 0
-                && (profileOwner.mProfileMaximumTimeOff == 0 || unlocked)) {
+                && (profileOwner.mProfileMaximumTimeOffMillis == 0 || unlocked)) {
             // There is a deadline but either there is no policy or the profile is unlocked -> clear
             // the deadline.
             Slog.i(LOG_TAG, "Profile off deadline is reset to zero");
             profileOwner.mProfileOffDeadline = 0;
             shouldSaveSettings = true;
         } else if (profileOwner.mProfileOffDeadline == 0
-                && (profileOwner.mProfileMaximumTimeOff != 0 && !unlocked)) {
+                && (profileOwner.mProfileMaximumTimeOffMillis != 0 && !unlocked)) {
             // There profile is locked and there is a policy, but the deadline is not set -> set the
             // deadline.
             Slog.i(LOG_TAG, "Profile off deadline is set.");
-            profileOwner.mProfileOffDeadline = now + profileOwner.mProfileMaximumTimeOff;
+            profileOwner.mProfileOffDeadline = now + profileOwner.mProfileMaximumTimeOffMillis;
             shouldSaveSettings = true;
         }
 
@@ -15828,7 +15838,7 @@
     }
 
     @Override
-    public void setManagedProfileMaximumTimeOff(ComponentName who, long timeoutMs) {
+    public void setManagedProfileMaximumTimeOff(ComponentName who, long timeoutMillis) {
         final int userId = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
             final ActiveAdmin admin = getActiveAdminForCallerLocked(who,
@@ -15837,10 +15847,10 @@
             // DO shouldn't be able to use this method.
             enforceProfileOwnerOfOrganizationOwnedDevice(admin);
             enforceHandlesCheckPolicyComplianceIntent(userId, admin.info.getPackageName());
-            if (admin.mProfileMaximumTimeOff == timeoutMs) {
+            if (admin.mProfileMaximumTimeOffMillis == timeoutMillis) {
                 return;
             }
-            admin.mProfileMaximumTimeOff = timeoutMs;
+            admin.mProfileMaximumTimeOffMillis = timeoutMillis;
             saveSettingsLocked(userId);
         }
 
@@ -15850,7 +15860,7 @@
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_MANAGED_PROFILE_MAXIMUM_TIME_OFF)
                 .setAdmin(who)
-                .setTimePeriod(timeoutMs)
+                .setTimePeriod(timeoutMillis)
                 .write();
     }
 
@@ -15874,7 +15884,7 @@
                     false /* parent */);
             // DO shouldn't be able to use this method.
             enforceProfileOwnerOfOrganizationOwnedDevice(admin);
-            return admin.mProfileMaximumTimeOff;
+            return admin.mProfileMaximumTimeOffMillis;
         }
     }
 
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 2426e8f..cccd0133 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -28,6 +28,7 @@
 #include <androidfw/ZipFileRO.h>
 #include <androidfw/ZipUtils.h>
 #include <binder/BinderService.h>
+#include <binder/Nullable.h>
 #include <binder/ParcelFileDescriptor.h>
 #include <binder/Status.h>
 #include <sys/stat.h>
@@ -917,19 +918,23 @@
 }
 
 bool IncrementalService::startLoading(StorageId storage) const {
-    const auto ifs = getIfs(storage);
-    if (!ifs) {
-        return false;
-    }
-    std::unique_lock l(ifs->lock);
-    if (ifs->dataLoaderStatus != IDataLoaderStatusListener::DATA_LOADER_CREATED) {
-        if (ifs->dataLoaderReady.wait_for(l, Seconds(5)) == std::cv_status::timeout) {
-            LOG(ERROR) << "Timeout waiting for data loader to be ready";
+    {
+        std::unique_lock l(mLock);
+        const auto& ifs = getIfsLocked(storage);
+        if (!ifs) {
             return false;
         }
+        if (ifs->dataLoaderStatus != IDataLoaderStatusListener::DATA_LOADER_CREATED) {
+            ifs->dataLoaderStartRequested = true;
+            return true;
+        }
     }
+    return startDataLoader(storage);
+}
+
+bool IncrementalService::startDataLoader(MountId mountId) const {
     sp<IDataLoader> dataloader;
-    auto status = mDataLoaderManager->getDataLoader(ifs->mountId, &dataloader);
+    auto status = mDataLoaderManager->getDataLoader(mountId, &dataloader);
     if (!status.isOk()) {
         return false;
     }
@@ -1065,15 +1070,9 @@
         }
         return true; // eventually...
     }
-    if (base::GetBoolProperty("incremental.skip_loader", false)) {
-        LOG(INFO) << "Skipped data loader because of incremental.skip_loader property";
-        std::unique_lock l(ifs.lock);
-        ifs.savedDataLoaderParams.reset();
-        return true;
-    }
 
     std::unique_lock l(ifs.lock);
-    if (ifs.dataLoaderStatus == IDataLoaderStatusListener::DATA_LOADER_CREATED) {
+    if (ifs.dataLoaderStatus != -1) {
         LOG(INFO) << "Skipped data loader preparation because it already exists";
         return true;
     }
@@ -1085,7 +1084,7 @@
         return false;
     }
     FileSystemControlParcel fsControlParcel;
-    fsControlParcel.incremental = std::make_unique<IncrementalFileSystemControlParcel>();
+    fsControlParcel.incremental = aidl::make_nullable<IncrementalFileSystemControlParcel>();
     fsControlParcel.incremental->cmd.reset(base::unique_fd(::dup(ifs.control.cmd)));
     fsControlParcel.incremental->pendingReads.reset(
             base::unique_fd(::dup(ifs.control.pendingReads)));
@@ -1226,30 +1225,42 @@
         externalListener->onStatusChanged(mountId, newStatus);
     }
 
-    std::unique_lock l(incrementalService.mLock);
-    const auto& ifs = incrementalService.getIfsLocked(mountId);
-    if (!ifs) {
-        LOG(WARNING) << "Received data loader status " << int(newStatus) << " for unknown mount "
-                     << mountId;
-        return binder::Status::ok();
+    bool startRequested = false;
+    {
+        std::unique_lock l(incrementalService.mLock);
+        const auto& ifs = incrementalService.getIfsLocked(mountId);
+        if (!ifs) {
+            LOG(WARNING) << "Received data loader status " << int(newStatus) << " for unknown mount "
+                         << mountId;
+            return binder::Status::ok();
+        }
+        ifs->dataLoaderStatus = newStatus;
+
+        if (newStatus == IDataLoaderStatusListener::DATA_LOADER_DESTROYED) {
+            ifs->dataLoaderStatus = IDataLoaderStatusListener::DATA_LOADER_STOPPED;
+            incrementalService.deleteStorageLocked(*ifs, std::move(l));
+            return binder::Status::ok();
+        }
+
+        startRequested = ifs->dataLoaderStartRequested;
     }
-    ifs->dataLoaderStatus = newStatus;
+
     switch (newStatus) {
         case IDataLoaderStatusListener::DATA_LOADER_NO_CONNECTION: {
             // TODO(b/150411019): handle data loader connection loss
             break;
         }
         case IDataLoaderStatusListener::DATA_LOADER_CONNECTION_OK: {
-            ifs->dataLoaderStatus = IDataLoaderStatusListener::DATA_LOADER_STARTED;
+            // TODO(b/150411019): handle data loader connection loss
             break;
         }
         case IDataLoaderStatusListener::DATA_LOADER_CREATED: {
-            ifs->dataLoaderReady.notify_one();
+            if (startRequested) {
+                incrementalService.startDataLoader(mountId);
+            }
             break;
         }
         case IDataLoaderStatusListener::DATA_LOADER_DESTROYED: {
-            ifs->dataLoaderStatus = IDataLoaderStatusListener::DATA_LOADER_STOPPED;
-            incrementalService.deleteStorageLocked(*ifs, std::move(l));
             break;
         }
         case IDataLoaderStatusListener::DATA_LOADER_STARTED: {
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index 8ff441b..406b32e 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -170,7 +170,7 @@
         std::optional<DataLoaderParamsParcel> savedDataLoaderParams;
         std::atomic<int> nextStorageDirNo{0};
         std::atomic<int> dataLoaderStatus = -1;
-        std::condition_variable dataLoaderReady;
+        bool dataLoaderStartRequested = false;
         TimePoint connectionLostTime = TimePoint();
         const IncrementalService& incrementalService;
 
@@ -208,6 +208,8 @@
 
     bool prepareDataLoader(IncFsMount& ifs, DataLoaderParamsParcel* params = nullptr,
                            const DataLoaderStatusListener* externalListener = nullptr);
+    bool startDataLoader(MountId mountId) const;
+
     BindPathMap::const_iterator findStorageLocked(std::string_view path) const;
     StorageId findStorageId(std::string_view path) const;
 
diff --git a/services/people/java/com/android/server/people/data/ConversationStore.java b/services/people/java/com/android/server/people/data/ConversationStore.java
index 8481e5b..28e3d4b 100644
--- a/services/people/java/com/android/server/people/data/ConversationStore.java
+++ b/services/people/java/com/android/server/people/data/ConversationStore.java
@@ -89,25 +89,21 @@
      * Loads conversations from disk to memory in a background thread. This should be called
      * after the device powers on and the user has been unlocked.
      */
-    @MainThread
-    void loadConversationsFromDisk() {
-        mScheduledExecutorService.execute(() -> {
-            synchronized (this) {
-                ConversationInfosProtoDiskReadWriter conversationInfosProtoDiskReadWriter =
-                        getConversationInfosProtoDiskReadWriter();
-                if (conversationInfosProtoDiskReadWriter == null) {
-                    return;
-                }
-                List<ConversationInfo> conversationsOnDisk =
-                        conversationInfosProtoDiskReadWriter.read(CONVERSATIONS_FILE_NAME);
-                if (conversationsOnDisk == null) {
-                    return;
-                }
-                for (ConversationInfo conversationInfo : conversationsOnDisk) {
-                    updateConversationsInMemory(conversationInfo);
-                }
-            }
-        });
+    @WorkerThread
+    synchronized void loadConversationsFromDisk() {
+        ConversationInfosProtoDiskReadWriter conversationInfosProtoDiskReadWriter =
+                getConversationInfosProtoDiskReadWriter();
+        if (conversationInfosProtoDiskReadWriter == null) {
+            return;
+        }
+        List<ConversationInfo> conversationsOnDisk =
+                conversationInfosProtoDiskReadWriter.read(CONVERSATIONS_FILE_NAME);
+        if (conversationsOnDisk == null) {
+            return;
+        }
+        for (ConversationInfo conversationInfo : conversationsOnDisk) {
+            updateConversationsInMemory(conversationInfo);
+        }
     }
 
     /**
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 4e0afc5..7085f96 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -28,6 +28,7 @@
 import android.app.prediction.AppTargetEvent;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -87,13 +88,13 @@
 
     private static final String TAG = "DataManager";
 
-    private static final long QUERY_EVENTS_MAX_AGE_MS = DateUtils.DAY_IN_MILLIS;
+    private static final long QUERY_EVENTS_MAX_AGE_MS = 5L * DateUtils.MINUTE_IN_MILLIS;
     private static final long USAGE_STATS_QUERY_INTERVAL_SEC = 120L;
 
     private final Context mContext;
     private final Injector mInjector;
-    private final ScheduledExecutorService mUsageStatsQueryExecutor;
-    private final ScheduledExecutorService mDiskReadWriterExecutor;
+    private final ScheduledExecutorService mScheduledExecutor;
+    private final Object mLock = new Object();
 
     private final SparseArray<UserData> mUserDataArray = new SparseArray<>();
     private final SparseArray<BroadcastReceiver> mBroadcastReceivers = new SparseArray<>();
@@ -118,8 +119,7 @@
     DataManager(Context context, Injector injector) {
         mContext = context;
         mInjector = injector;
-        mUsageStatsQueryExecutor = mInjector.createScheduledExecutor();
-        mDiskReadWriterExecutor = mInjector.createScheduledExecutor();
+        mScheduledExecutor = mInjector.createScheduledExecutor();
     }
 
     /** Initialization. Called when the system services are up running. */
@@ -138,103 +138,56 @@
 
     /** This method is called when a user is unlocked. */
     public void onUserUnlocked(int userId) {
-        UserData userData = mUserDataArray.get(userId);
-        if (userData == null) {
-            userData = new UserData(userId, mDiskReadWriterExecutor);
-            mUserDataArray.put(userId, userData);
+        synchronized (mLock) {
+            UserData userData = mUserDataArray.get(userId);
+            if (userData == null) {
+                userData = new UserData(userId, mScheduledExecutor);
+                mUserDataArray.put(userId, userData);
+            }
+            userData.setUserUnlocked();
         }
-        userData.setUserUnlocked();
-        updateDefaultDialer(userData);
-        updateDefaultSmsApp(userData);
-
-        ScheduledFuture<?> scheduledFuture = mUsageStatsQueryExecutor.scheduleAtFixedRate(
-                new UsageStatsQueryRunnable(userId), 1L, USAGE_STATS_QUERY_INTERVAL_SEC,
-                TimeUnit.SECONDS);
-        mUsageStatsQueryFutures.put(userId, scheduledFuture);
-
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED);
-        intentFilter.addAction(SmsApplication.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL);
-        BroadcastReceiver broadcastReceiver = new PerUserBroadcastReceiver(userId);
-        mBroadcastReceivers.put(userId, broadcastReceiver);
-        mContext.registerReceiverAsUser(
-                broadcastReceiver, UserHandle.of(userId), intentFilter, null, null);
-
-        ContentObserver contactsContentObserver = new ContactsContentObserver(
-                BackgroundThread.getHandler());
-        mContactsContentObservers.put(userId, contactsContentObserver);
-        mContext.getContentResolver().registerContentObserver(
-                Contacts.CONTENT_URI, /* notifyForDescendants= */ true,
-                contactsContentObserver, userId);
-
-        NotificationListener notificationListener = new NotificationListener();
-        mNotificationListeners.put(userId, notificationListener);
-        try {
-            notificationListener.registerAsSystemService(mContext,
-                    new ComponentName(mContext, getClass()), userId);
-        } catch (RemoteException e) {
-            // Should never occur for local calls.
-        }
-
-        PackageMonitor packageMonitor = new PerUserPackageMonitor();
-        packageMonitor.register(mContext, null, UserHandle.of(userId), true);
-        mPackageMonitors.put(userId, packageMonitor);
-
-        if (userId == UserHandle.USER_SYSTEM) {
-            // The call log and MMS/SMS messages are shared across user profiles. So only need to
-            // register the content observers once for the primary user.
-            // TODO: Register observers after the conversations and events being loaded from disk.
-            mCallLogContentObserver = new CallLogContentObserver(BackgroundThread.getHandler());
-            mContext.getContentResolver().registerContentObserver(
-                    CallLog.CONTENT_URI, /* notifyForDescendants= */ true,
-                    mCallLogContentObserver, UserHandle.USER_SYSTEM);
-
-            mMmsSmsContentObserver = new MmsSmsContentObserver(BackgroundThread.getHandler());
-            mContext.getContentResolver().registerContentObserver(
-                    MmsSms.CONTENT_URI, /* notifyForDescendants= */ false,
-                    mMmsSmsContentObserver, UserHandle.USER_SYSTEM);
-        }
-
-        DataMaintenanceService.scheduleJob(mContext, userId);
+        mScheduledExecutor.execute(() -> setupUser(userId));
     }
 
     /** This method is called when a user is stopping. */
     public void onUserStopping(int userId) {
-        if (mUserDataArray.indexOfKey(userId) >= 0) {
-            mUserDataArray.get(userId).setUserStopped();
-        }
-        if (mUsageStatsQueryFutures.indexOfKey(userId) >= 0) {
-            mUsageStatsQueryFutures.get(userId).cancel(true);
-        }
-        if (mBroadcastReceivers.indexOfKey(userId) >= 0) {
-            mContext.unregisterReceiver(mBroadcastReceivers.get(userId));
-        }
-        if (mContactsContentObservers.indexOfKey(userId) >= 0) {
-            mContext.getContentResolver().unregisterContentObserver(
-                    mContactsContentObservers.get(userId));
-        }
-        if (mNotificationListeners.indexOfKey(userId) >= 0) {
-            try {
-                mNotificationListeners.get(userId).unregisterAsSystemService();
-            } catch (RemoteException e) {
-                // Should never occur for local calls.
+        synchronized (mLock) {
+            ContentResolver contentResolver = mContext.getContentResolver();
+            if (mUserDataArray.indexOfKey(userId) >= 0) {
+                mUserDataArray.get(userId).setUserStopped();
             }
-        }
-        if (mPackageMonitors.indexOfKey(userId) >= 0) {
-            mPackageMonitors.get(userId).unregister();
-        }
-        if (userId == UserHandle.USER_SYSTEM) {
-            if (mCallLogContentObserver != null) {
-                mContext.getContentResolver().unregisterContentObserver(mCallLogContentObserver);
-                mCallLogContentObserver = null;
+            if (mUsageStatsQueryFutures.indexOfKey(userId) >= 0) {
+                mUsageStatsQueryFutures.get(userId).cancel(true);
             }
-            if (mMmsSmsContentObserver != null) {
-                mContext.getContentResolver().unregisterContentObserver(mMmsSmsContentObserver);
-                mCallLogContentObserver = null;
+            if (mBroadcastReceivers.indexOfKey(userId) >= 0) {
+                mContext.unregisterReceiver(mBroadcastReceivers.get(userId));
             }
-        }
+            if (mContactsContentObservers.indexOfKey(userId) >= 0) {
+                contentResolver.unregisterContentObserver(mContactsContentObservers.get(userId));
+            }
+            if (mNotificationListeners.indexOfKey(userId) >= 0) {
+                try {
+                    mNotificationListeners.get(userId).unregisterAsSystemService();
+                } catch (RemoteException e) {
+                    // Should never occur for local calls.
+                }
+            }
+            if (mPackageMonitors.indexOfKey(userId) >= 0) {
+                mPackageMonitors.get(userId).unregister();
+            }
+            if (userId == UserHandle.USER_SYSTEM) {
+                if (mCallLogContentObserver != null) {
+                    contentResolver.unregisterContentObserver(mCallLogContentObserver);
+                    mCallLogContentObserver = null;
+                }
+                if (mMmsSmsContentObserver != null) {
+                    contentResolver.unregisterContentObserver(mMmsSmsContentObserver);
+                    mCallLogContentObserver = null;
+                }
+            }
 
-        DataMaintenanceService.cancelJob(mContext, userId);
+            DataMaintenanceService.cancelJob(mContext, userId);
+        }
     }
 
     /**
@@ -288,6 +241,9 @@
             return;
         }
         UserData userData = getUnlockedUserData(appTarget.getUser().getIdentifier());
+        if (userData == null) {
+            return;
+        }
         PackageData packageData = userData.getOrCreatePackageData(appTarget.getPackageName());
         String mimeType = intentFilter != null ? intentFilter.getDataType(0) : null;
         @Event.EventType int eventType = mimeTypeToShareEventType(mimeType);
@@ -353,6 +309,68 @@
         userData.restore(payload);
     }
 
+    private void setupUser(@UserIdInt int userId) {
+        synchronized (mLock) {
+            UserData userData = getUnlockedUserData(userId);
+            if (userData == null) {
+                return;
+            }
+            userData.loadUserData();
+
+            updateDefaultDialer(userData);
+            updateDefaultSmsApp(userData);
+
+            ScheduledFuture<?> scheduledFuture = mScheduledExecutor.scheduleAtFixedRate(
+                    new UsageStatsQueryRunnable(userId), 1L, USAGE_STATS_QUERY_INTERVAL_SEC,
+                    TimeUnit.SECONDS);
+            mUsageStatsQueryFutures.put(userId, scheduledFuture);
+
+            IntentFilter intentFilter = new IntentFilter();
+            intentFilter.addAction(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED);
+            intentFilter.addAction(SmsApplication.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL);
+            BroadcastReceiver broadcastReceiver = new PerUserBroadcastReceiver(userId);
+            mBroadcastReceivers.put(userId, broadcastReceiver);
+            mContext.registerReceiverAsUser(
+                    broadcastReceiver, UserHandle.of(userId), intentFilter, null, null);
+
+            ContentObserver contactsContentObserver = new ContactsContentObserver(
+                    BackgroundThread.getHandler());
+            mContactsContentObservers.put(userId, contactsContentObserver);
+            mContext.getContentResolver().registerContentObserver(
+                    Contacts.CONTENT_URI, /* notifyForDescendants= */ true,
+                    contactsContentObserver, userId);
+
+            NotificationListener notificationListener = new NotificationListener();
+            mNotificationListeners.put(userId, notificationListener);
+            try {
+                notificationListener.registerAsSystemService(mContext,
+                        new ComponentName(mContext, getClass()), userId);
+            } catch (RemoteException e) {
+                // Should never occur for local calls.
+            }
+
+            PackageMonitor packageMonitor = new PerUserPackageMonitor();
+            packageMonitor.register(mContext, null, UserHandle.of(userId), true);
+            mPackageMonitors.put(userId, packageMonitor);
+
+            if (userId == UserHandle.USER_SYSTEM) {
+                // The call log and MMS/SMS messages are shared across user profiles. So only need
+                // to register the content observers once for the primary user.
+                mCallLogContentObserver = new CallLogContentObserver(BackgroundThread.getHandler());
+                mContext.getContentResolver().registerContentObserver(
+                        CallLog.CONTENT_URI, /* notifyForDescendants= */ true,
+                        mCallLogContentObserver, UserHandle.USER_SYSTEM);
+
+                mMmsSmsContentObserver = new MmsSmsContentObserver(BackgroundThread.getHandler());
+                mContext.getContentResolver().registerContentObserver(
+                        MmsSms.CONTENT_URI, /* notifyForDescendants= */ false,
+                        mMmsSmsContentObserver, UserHandle.USER_SYSTEM);
+            }
+
+            DataMaintenanceService.scheduleJob(mContext, userId);
+        }
+    }
+
     private int mimeTypeToShareEventType(String mimeType) {
         if (mimeType.startsWith("text/")) {
             return Event.TYPE_SHARE_TEXT;
diff --git a/services/people/java/com/android/server/people/data/EventStore.java b/services/people/java/com/android/server/people/data/EventStore.java
index 00d4241..9cf84c9 100644
--- a/services/people/java/com/android/server/people/data/EventStore.java
+++ b/services/people/java/com/android/server/people/data/EventStore.java
@@ -17,9 +17,9 @@
 package com.android.server.people.data;
 
 import android.annotation.IntDef;
-import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.WorkerThread;
 import android.net.Uri;
 import android.util.ArrayMap;
 
@@ -90,20 +90,16 @@
      * Loads existing {@link EventHistoryImpl}s from disk. This should be called when device powers
      * on and user is unlocked.
      */
-    @MainThread
-    void loadFromDisk() {
-        mScheduledExecutorService.execute(() -> {
-            synchronized (this) {
-                for (@EventCategory int category = 0; category < mEventsCategoryDirs.size();
-                        category++) {
-                    File categoryDir = mEventsCategoryDirs.get(category);
-                    Map<String, EventHistoryImpl> existingEventHistoriesImpl =
-                            EventHistoryImpl.eventHistoriesImplFromDisk(categoryDir,
-                                    mScheduledExecutorService);
-                    mEventHistoryMaps.get(category).putAll(existingEventHistoriesImpl);
-                }
-            }
-        });
+    @WorkerThread
+    synchronized void loadFromDisk() {
+        for (@EventCategory int category = 0; category < mEventsCategoryDirs.size();
+                category++) {
+            File categoryDir = mEventsCategoryDirs.get(category);
+            Map<String, EventHistoryImpl> existingEventHistoriesImpl =
+                    EventHistoryImpl.eventHistoriesImplFromDisk(categoryDir,
+                            mScheduledExecutorService);
+            mEventHistoryMaps.get(category).putAll(existingEventHistoriesImpl);
+        }
     }
 
     /**
diff --git a/services/people/java/com/android/server/people/data/PackageData.java b/services/people/java/com/android/server/people/data/PackageData.java
index 3e4c992..28837d5 100644
--- a/services/people/java/com/android/server/people/data/PackageData.java
+++ b/services/people/java/com/android/server/people/data/PackageData.java
@@ -25,6 +25,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.annotation.WorkerThread;
 import android.content.LocusId;
 import android.os.FileUtils;
 import android.text.TextUtils;
@@ -77,6 +78,7 @@
      * Returns a map of package directory names as keys and their associated {@link PackageData}.
      * This should be called when device is powered on and unlocked.
      */
+    @WorkerThread
     @NonNull
     static Map<String, PackageData> packagesDataFromDisk(@UserIdInt int userId,
             @NonNull Predicate<String> isDefaultDialerPredicate,
diff --git a/services/people/java/com/android/server/people/data/UserData.java b/services/people/java/com/android/server/people/data/UserData.java
index ed8c595..429d5b7 100644
--- a/services/people/java/com/android/server/people/data/UserData.java
+++ b/services/people/java/com/android/server/people/data/UserData.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.annotation.WorkerThread;
 import android.os.Environment;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -75,12 +76,6 @@
 
     void setUserUnlocked() {
         mIsUnlocked = true;
-
-        // Ensures per user root directory for people data is present, and attempt to load
-        // data from disk.
-        mPerUserPeopleDataDir.mkdirs();
-        mPackageDataMap.putAll(PackageData.packagesDataFromDisk(mUserId, this::isDefaultDialer,
-                this::isDefaultSmsApp, mScheduledExecutorService, mPerUserPeopleDataDir));
     }
 
     void setUserStopped() {
@@ -91,6 +86,15 @@
         return mIsUnlocked;
     }
 
+    @WorkerThread
+    void loadUserData() {
+        mPerUserPeopleDataDir.mkdir();
+        Map<String, PackageData> packageDataMap = PackageData.packagesDataFromDisk(
+                mUserId, this::isDefaultDialer, this::isDefaultSmsApp, mScheduledExecutorService,
+                mPerUserPeopleDataDir);
+        mPackageDataMap.putAll(packageDataMap);
+    }
+
     /**
      * Gets the {@link PackageData} for the specified {@code packageName} if exists; otherwise
      * creates a new instance and returns it.
diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
index c94bb87..5c82200 100644
--- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
@@ -79,6 +79,7 @@
     private static final String CALLING_PACKAGE2 = "com.package.name2";
     private static final String NAMESPACE1 = "namespace1";
     private static final String NAMESPACE2 = "namespace2";
+    private static final String DISABLE_RESCUE_PARTY_FLAG = "disable_rescue_party";
 
     private MockitoSession mSession;
     private HashMap<String, String> mSystemSettingsMap;
@@ -316,6 +317,13 @@
 
     @Test
     public void testExplicitlyEnablingAndDisablingRescue() {
+        // mock the DeviceConfig get call to avoid hitting
+        // android.permission.READ_DEVICE_CONFIG when calling real DeviceConfig.
+        doReturn(true)
+                .when(() -> DeviceConfig.getBoolean(
+                    eq(DeviceConfig.NAMESPACE_CONFIGURATION),
+                    eq(DISABLE_RESCUE_PARTY_FLAG),
+                    eq(false)));
         SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false));
         SystemProperties.set(PROP_DISABLE_RESCUE, Boolean.toString(true));
         assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage,
@@ -327,6 +335,22 @@
     }
 
     @Test
+    public void testDisablingRescueByDeviceConfigFlag() {
+        doReturn(true)
+                .when(() -> DeviceConfig.getBoolean(
+                    eq(DeviceConfig.NAMESPACE_CONFIGURATION),
+                    eq(DISABLE_RESCUE_PARTY_FLAG),
+                    eq(false)));
+        SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false));
+
+        assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage,
+                PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING), false);
+
+        // Restore the property value initalized in SetUp()
+        SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
+    }
+
+    @Test
     public void testHealthCheckLevels() {
         RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
index 95f4e83..c45ee7b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
@@ -55,6 +55,7 @@
 import android.text.TextUtils;
 import android.util.Pair;
 
+import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.appop.AppOpsService;
 import com.android.server.wm.ActivityTaskManagerService;
@@ -125,6 +126,8 @@
         mAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
         mAms.mAtmInternal = spy(mAms.mActivityTaskManager.getAtmInternal());
         mAms.mPackageManagerInt = mPackageManagerInt;
+        doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
+        LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
     }
 
     @After
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index fc2ae40..053a798 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -75,8 +75,10 @@
 
 import android.app.IApplicationThread;
 import android.app.IServiceConnection;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.ServiceInfo;
 import android.os.Build;
 import android.os.IBinder;
@@ -87,6 +89,7 @@
 import android.util.ArraySet;
 import android.util.SparseArray;
 
+import com.android.server.LocalServices;
 import com.android.server.wm.ActivityServiceConnectionsHolder;
 import com.android.server.wm.ActivityTaskManagerService;
 import com.android.server.wm.WindowProcessController;
@@ -127,6 +130,7 @@
     private static final String MOCKAPP5_PROCESSNAME = "test #5";
     private static final String MOCKAPP5_PACKAGENAME = "com.android.test.test5";
     private static Context sContext;
+    private static PackageManagerInternal sPackageManagerInternal;
     private static ActivityManagerService sService;
 
     @BeforeClass
@@ -134,13 +138,21 @@
         sContext = getInstrumentation().getTargetContext();
         System.setProperty("dexmaker.share_classloader", "true");
 
+        sPackageManagerInternal = mock(PackageManagerInternal.class);
+        doReturn(new ComponentName("", "")).when(sPackageManagerInternal)
+                .getSystemUiServiceComponent();
+        LocalServices.addService(PackageManagerInternal.class, sPackageManagerInternal);
+
         sService = mock(ActivityManagerService.class);
         sService.mActivityTaskManager = new ActivityTaskManagerService(sContext);
         sService.mActivityTaskManager.initialize(null, null, sContext.getMainLooper());
+        sService.mPackageManagerInt = sPackageManagerInternal;
         sService.mAtmInternal = spy(sService.mActivityTaskManager.getAtmInternal());
 
         sService.mConstants = new ActivityManagerConstants(sContext, sService,
                 sContext.getMainThreadHandler());
+        setFieldValue(ActivityManagerService.class, sService, "mContext",
+                sContext);
         ProcessList pr = new ProcessList();
         pr.init(sService, new ActiveUids(sService, false), null);
         setFieldValue(ActivityManagerService.class, sService, "mProcessList",
@@ -972,7 +984,7 @@
         sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
         doReturn(null).when(sService).getTopAppLocked();
 
-        assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, VISIBLE_APP_ADJ,
+        assertProcStates(app, PROCESS_STATE_BOUND_TOP, VISIBLE_APP_ADJ,
                 SCHED_GROUP_DEFAULT);
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
index 959dc05..de6f55b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
@@ -147,7 +147,7 @@
         AndroidPackage mockMyPkg = mock(AndroidPackage.class);
         when(mockMyPkg.isPrivileged()).thenReturn(false);
         when(mockMyPkg.getUid()).thenReturn(mMyUid);
-        when(mockMyPkg.getFeatures()).thenReturn(Collections.emptyList());
+        when(mockMyPkg.getAttributions()).thenReturn(Collections.emptyList());
 
         when(mockPackageManagerInternal.getPackage(sMyPackageName)).thenReturn(mockMyPkg);
         doReturn(mockPackageManagerInternal).when(
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index eb2dd64..8be9213 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -251,6 +251,28 @@
     }
 
     @Test
+    public void testAllowRemoveOverrideNoOverride() throws Exception {
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addDisabledChangeWithId(1234L)
+                .addLoggingOnlyChangeWithId(2L)
+                .build();
+        ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+                .withPackageName("com.some.package")
+                .build();
+        when(mPackageManager.getApplicationInfo(eq("com.some.package"), anyInt()))
+                .thenReturn(applicationInfo);
+
+        // Reject all override attempts.
+        // Force the validator to prevent overriding the change by using a user build.
+        when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
+        when(mBuildClassifier.isFinalBuild()).thenReturn(true);
+        // Try to remove a non existing override, and it doesn't fail.
+        assertThat(compatConfig.removeOverride(1234L, "com.some.package")).isFalse();
+        assertThat(compatConfig.removeOverride(2L, "com.some.package")).isFalse();
+        compatConfig.removePackageOverrides("com.some.package");
+    }
+
+    @Test
     public void testRemovePackageOverride() throws Exception {
         CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
                 .addEnabledChangeWithId(1234L)
@@ -267,6 +289,49 @@
     }
 
     @Test
+    public void testEnableTargetSdkChangesForPackage() throws Exception {
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addEnabledChangeWithId(1L)
+                .addDisabledChangeWithId(2L)
+                .addTargetSdkChangeWithId(3, 3L)
+                .addTargetSdkChangeWithId(4, 4L)
+                .build();
+        ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+                .withPackageName("foo.bar")
+                .withTargetSdk(2)
+                .build();
+
+        assertThat(compatConfig.isChangeEnabled(3, applicationInfo)).isFalse();
+        assertThat(compatConfig.isChangeEnabled(4, applicationInfo)).isFalse();
+
+        assertThat(compatConfig.enableTargetSdkChangesForPackage("foo.bar", 3)).isEqualTo(1);
+        assertThat(compatConfig.isChangeEnabled(3, applicationInfo)).isTrue();
+        assertThat(compatConfig.isChangeEnabled(4, applicationInfo)).isFalse();
+    }
+
+    @Test
+    public void testDisableTargetSdkChangesForPackage() throws Exception {
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addEnabledChangeWithId(1L)
+                .addDisabledChangeWithId(2L)
+                .addTargetSdkChangeWithId(3, 3L)
+                .addTargetSdkChangeWithId(4, 4L)
+                .build();
+        ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+                .withPackageName("foo.bar")
+                .withTargetSdk(2)
+                .build();
+
+        assertThat(compatConfig.enableTargetSdkChangesForPackage("foo.bar", 3)).isEqualTo(1);
+        assertThat(compatConfig.isChangeEnabled(3, applicationInfo)).isTrue();
+        assertThat(compatConfig.isChangeEnabled(4, applicationInfo)).isFalse();
+
+        assertThat(compatConfig.disableTargetSdkChangesForPackage("foo.bar", 3)).isEqualTo(1);
+        assertThat(compatConfig.isChangeEnabled(3, applicationInfo)).isFalse();
+        assertThat(compatConfig.isChangeEnabled(4, applicationInfo)).isFalse();
+    }
+
+    @Test
     public void testLookupChangeId() throws Exception {
         CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
                 .addEnabledChangeWithIdAndName(1234L, "MY_CHANGE")
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 2a6a24e..7571f09 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3872,79 +3872,80 @@
             Settings.System.SCREEN_BRIGHTNESS, "0", DpmMockContext.CALLER_USER_HANDLE);
     }
 
-    public void testSetAutoTimeModifiesSetting() throws Exception {
+    public void testSetAutoTimeEnabledModifiesSetting() throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
-        dpm.setAutoTime(admin1, true);
+        dpm.setAutoTimeEnabled(admin1, true);
         verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 1);
 
-        dpm.setAutoTime(admin1, false);
+        dpm.setAutoTimeEnabled(admin1, false);
         verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 0);
     }
 
-    public void testSetAutoTimeWithPOOnUser0() throws Exception {
+    public void testSetAutoTimeEnabledWithPOOnUser0() throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupProfileOwnerOnUser0();
-        dpm.setAutoTime(admin1, true);
+        dpm.setAutoTimeEnabled(admin1, true);
         verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 1);
 
-        dpm.setAutoTime(admin1, false);
+        dpm.setAutoTimeEnabled(admin1, false);
         verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 0);
     }
 
-    public void testSetAutoTimeFailWithPONotOnUser0() throws Exception {
+    public void testSetAutoTimeEnabledFailWithPONotOnUser0() throws Exception {
         setupProfileOwner();
-        assertExpectException(SecurityException.class, null, () -> dpm.setAutoTime(admin1, false));
+        assertExpectException(SecurityException.class, null,
+                () -> dpm.setAutoTimeEnabled(admin1, false));
         verify(getServices().settings, never()).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 0);
     }
 
-    public void testSetAutoTimeWithPOOfOrganizationOwnedDevice() throws Exception {
+    public void testSetAutoTimeEnabledWithPOOfOrganizationOwnedDevice() throws Exception {
         setupProfileOwner();
         configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
 
-        dpm.setAutoTime(admin1, true);
+        dpm.setAutoTimeEnabled(admin1, true);
         verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 1);
 
-        dpm.setAutoTime(admin1, false);
+        dpm.setAutoTimeEnabled(admin1, false);
         verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 0);
     }
 
-    public void testSetAutoTimeZoneModifiesSetting() throws Exception {
+    public void testSetAutoTimeZoneEnabledModifiesSetting() throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
-        dpm.setAutoTimeZone(admin1, true);
+        dpm.setAutoTimeZoneEnabled(admin1, true);
         verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 1);
 
-        dpm.setAutoTimeZone(admin1, false);
+        dpm.setAutoTimeZoneEnabled(admin1, false);
         verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 0);
     }
 
-    public void testSetAutoTimeZoneWithPOOnUser0() throws Exception {
+    public void testSetAutoTimeZoneEnabledWithPOOnUser0() throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupProfileOwnerOnUser0();
-        dpm.setAutoTimeZone(admin1, true);
+        dpm.setAutoTimeZoneEnabled(admin1, true);
         verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 1);
 
-        dpm.setAutoTimeZone(admin1, false);
+        dpm.setAutoTimeZoneEnabled(admin1, false);
         verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 0);
     }
 
-    public void testSetAutoTimeZoneFailWithPONotOnUser0() throws Exception {
+    public void testSetAutoTimeZoneEnabledFailWithPONotOnUser0() throws Exception {
         setupProfileOwner();
         assertExpectException(SecurityException.class, null,
-                () -> dpm.setAutoTimeZone(admin1, false));
+                () -> dpm.setAutoTimeZoneEnabled(admin1, false));
         verify(getServices().settings, never()).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE,
                 0);
     }
 
-    public void testSetAutoTimeZoneWithPOOfOrganizationOwnedDevice() throws Exception {
+    public void testSetAutoTimeZoneEnabledWithPOOfOrganizationOwnedDevice() throws Exception {
         setupProfileOwner();
         configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
 
-        dpm.setAutoTimeZone(admin1, true);
+        dpm.setAutoTimeZoneEnabled(admin1, true);
         verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 1);
 
-        dpm.setAutoTimeZone(admin1, false);
+        dpm.setAutoTimeZoneEnabled(admin1, false);
         verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 0);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index 3dd1504..81545d4 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -28,6 +28,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -321,6 +322,11 @@
         // we cannot check installer cert because it seems to be device specific.
         assertEquals(VERSION_CODE, appInstallMetadata.getVersionCode());
         assertFalse(appInstallMetadata.isPreInstalled());
+        // Asserting source stamp not present.
+        assertFalse(appInstallMetadata.isStampPresent());
+        assertFalse(appInstallMetadata.isStampVerified());
+        assertFalse(appInstallMetadata.isStampTrusted());
+        assertNull(appInstallMetadata.getStampCertificateHash());
         // These are hardcoded in the test apk android manifest
         Map<String, String> allowedInstallers =
                 appInstallMetadata.getAllowedInstallersAndCertificates();
diff --git a/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java
index b0def60..ccbaee4 100644
--- a/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java
@@ -18,6 +18,11 @@
 
 import static android.hardware.lights.LightsRequest.Builder;
 
+import static android.graphics.Color.BLACK;
+import static android.graphics.Color.BLUE;
+import static android.graphics.Color.GREEN;
+import static android.graphics.Color.WHITE;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import android.content.Context;
@@ -92,7 +97,7 @@
 
         // When the session requests to turn 3/4 lights on:
         LightsManager.LightsSession session = manager.openSession();
-        session.setLights(new Builder()
+        session.requestLights(new Builder()
                 .setLight(manager.getLights().get(0), new LightState(0xf1))
                 .setLight(manager.getLights().get(1), new LightState(0xf2))
                 .setLight(manager.getLights().get(2), new LightState(0xf3))
@@ -114,18 +119,18 @@
         Light micLight = manager.getLights().get(0);
 
         // The light should begin by being off.
-        assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0x00000000);
+        assertThat(manager.getLightState(micLight).getColor()).isEqualTo(BLACK);
 
         // When a session commits changes:
         LightsManager.LightsSession session = manager.openSession();
-        session.setLights(new Builder().setLight(micLight, new LightState(0xff00ff00)).build());
+        session.requestLights(new Builder().setLight(micLight, new LightState(GREEN)).build());
         // Then the light should turn on.
-        assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0xff00ff00);
+        assertThat(manager.getLightState(micLight).getColor()).isEqualTo(GREEN);
 
         // When the session goes away:
         session.close();
         // Then the light should turn off.
-        assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0x00000000);
+        assertThat(manager.getLightState(micLight).getColor()).isEqualTo(BLACK);
     }
 
     @Test
@@ -138,15 +143,15 @@
         LightsManager.LightsSession session2 = manager.openSession();
 
         // When session1 and session2 both request the same light:
-        session1.setLights(new Builder().setLight(micLight, new LightState(0xff0000ff)).build());
-        session2.setLights(new Builder().setLight(micLight, new LightState(0xffffffff)).build());
+        session1.requestLights(new Builder().setLight(micLight, new LightState(BLUE)).build());
+        session2.requestLights(new Builder().setLight(micLight, new LightState(WHITE)).build());
         // Then session1 should win because it was created first.
-        assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0xff0000ff);
+        assertThat(manager.getLightState(micLight).getColor()).isEqualTo(BLUE);
 
         // When session1 goes away:
         session1.close();
         // Then session2 should have its request go into effect.
-        assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0xffffffff);
+        assertThat(manager.getLightState(micLight).getColor()).isEqualTo(WHITE);
 
         // When session2 goes away:
         session2.close();
@@ -162,10 +167,10 @@
 
         // When the session turns a light on:
         LightsManager.LightsSession session = manager.openSession();
-        session.setLights(new Builder().setLight(micLight, new LightState(0xffffffff)).build());
+        session.requestLights(new Builder().setLight(micLight, new LightState(WHITE)).build());
 
         // And then the session clears it again:
-        session.setLights(new Builder().clearLight(micLight).build());
+        session.requestLights(new Builder().clearLight(micLight).build());
 
         // Then the light should turn back off.
         assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0);
diff --git a/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java b/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java
index 4e63237..e10cbbf 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java
@@ -273,9 +273,8 @@
         // Ensure that futures were cancelled and the immediate flush occurred.
         assertEquals(0, mMockScheduledExecutorService.getFutures().size());
 
-        // Expect to see 2 executes: loadConversationFromDisk and saveConversationsToDisk.
-        // loadConversationFromDisk gets called each time we call #resetConversationStore().
-        assertEquals(2, mMockScheduledExecutorService.getExecutes().size());
+        // Expect to see 1 execute: saveConversationsToDisk.
+        assertEquals(1, mMockScheduledExecutorService.getExecutes().size());
 
         resetConversationStore();
         ConversationInfo out1 = mConversationStore.getConversation(SHORTCUT_ID);
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index a4d63ac..5199604 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -183,6 +183,11 @@
 
         when(mExecutorService.scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(
                 TimeUnit.class))).thenReturn(mScheduledFuture);
+        doAnswer(ans -> {
+            Runnable runnable = (Runnable) ans.getArguments()[0];
+            runnable.run();
+            return null;
+        }).when(mExecutorService).execute(any(Runnable.class));
 
         when(mUserManager.getEnabledProfiles(USER_ID_PRIMARY))
                 .thenReturn(Arrays.asList(
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 9f3bf02..6eec649 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityManager.PROCESS_STATE_TOP;
 import static android.app.ActivityManager.START_ABORTED;
+import static android.app.ActivityManager.START_CANCELED;
 import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
 import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
 import static android.app.ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
@@ -202,10 +203,11 @@
         final IApplicationThread caller = mock(IApplicationThread.class);
         final WindowProcessListener listener = mock(WindowProcessListener.class);
 
+        final ApplicationInfo ai = new ApplicationInfo();
+        ai.packageName = "com.android.test.package";
         final WindowProcessController wpc =
                 containsConditions(preconditions, PRECONDITION_NO_CALLER_APP)
-                ? null : new WindowProcessController(
-                        service, mock(ApplicationInfo.class), null, 0, -1, null, listener);
+                ? null : new WindowProcessController(service, ai, null, 0, -1, null, listener);
         doReturn(wpc).when(service).getProcessController(anyObject());
 
         final Intent intent = new Intent();
@@ -344,6 +346,7 @@
         doReturn(false).when(mMockPackageManager).isInstantAppInstallerComponent(any());
         doReturn(null).when(mMockPackageManager).resolveIntent(any(), any(), anyInt(), anyInt(),
                 anyInt(), anyBoolean(), anyInt());
+        doReturn(new ComponentName("", "")).when(mMockPackageManager).getSystemUiServiceComponent();
 
         // Never review permissions
         doReturn(false).when(mMockPackageManager).isPermissionsReviewRequired(any(), anyInt());
@@ -655,6 +658,7 @@
         final WindowProcessListener listener = mock(WindowProcessListener.class);
         final ApplicationInfo ai = new ApplicationInfo();
         ai.uid = callingUid;
+        ai.packageName = "com.android.test.package";
         final WindowProcessController callerApp =
                 new WindowProcessController(mService, ai, null, callingUid, -1, null, listener);
         callerApp.setHasForegroundActivities(hasForegroundActivities);
@@ -892,7 +896,7 @@
                 .execute();
 
         // Simulate a failed start
-        starter.postStartActivityProcessing(null, START_ABORTED, null);
+        starter.postStartActivityProcessing(null, START_CANCELED, null);
 
         verify(recentTasks, times(1)).setFreezeTaskListReordering();
         verify(recentTasks, times(1)).resetFreezeTaskListReorderingOnTimeout();
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 5cf1fbb..a380ece 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.view.InsetsState.ITYPE_IME;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
@@ -69,7 +71,7 @@
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
         statusBar.setControllableInsetProvider(getController().getSourceProvider(ITYPE_STATUS_BAR));
-        assertNotNull(getController().getInsetsForDispatch(app).getSource(ITYPE_STATUS_BAR));
+        assertNotNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_STATUS_BAR));
     }
 
     @Test
@@ -101,6 +103,34 @@
     }
 
     @Test
+    public void testStripForDispatch_pip() {
+        final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+        final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
+        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
+        app.setWindowingMode(WINDOWING_MODE_PINNED);
+
+        assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_STATUS_BAR));
+        assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_NAVIGATION_BAR));
+    }
+
+    @Test
+    public void testStripForDispatch_freeform() {
+        final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+        final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
+        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
+        app.setWindowingMode(WINDOWING_MODE_FREEFORM);
+
+        assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_STATUS_BAR));
+        assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_NAVIGATION_BAR));
+    }
+
+    @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/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index ac4c228f..8f3ff52 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -37,8 +37,8 @@
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.server.LocalServices;
 import com.android.server.pm.PackageList;
@@ -72,6 +72,10 @@
             ComponentName.createRelative("com.android.foo", ".BarActivity");
     private static final ComponentName ALTERNATIVE_COMPONENT =
             ComponentName.createRelative("com.android.foo", ".AlternativeBarActivity");
+    private static final String TEST_WINDOW_LAYOUT_AFFINITY = "135:.Affinity";
+    private static final String TEST_ALTERNATIVE_WINDOW_LAYOUT_AFFINITY =
+            "246:.AlternativeAffinity";
+    private static final String TEST_DIFFERENT_AFFINITY_WITH_SAME_UID = "135:.DifferentAffinity";
 
     private static final int TEST_WINDOWING_MODE = WINDOWING_MODE_FREEFORM;
     private static final Rect TEST_BOUNDS = new Rect(100, 200, 300, 400);
@@ -99,11 +103,12 @@
     public void setUp() throws Exception {
         mPersisterQueue = new TestPersisterQueue();
 
-        final File cacheFolder = InstrumentationRegistry.getContext().getCacheDir();
+        final File cacheFolder =
+                InstrumentationRegistry.getInstrumentation().getContext().getCacheDir();
         mFolder = new File(cacheFolder, "launch_params_tests");
         deleteRecursively(mFolder);
 
-        mDisplayUniqueId = "test:" + Integer.toString(sNextUniqueId++);
+        mDisplayUniqueId = "test:" + sNextUniqueId++;
         mTestDisplay = new TestDisplayContent.Builder(mService, 1000, 1500)
                 .setUniqueId(mDisplayUniqueId).build();
         when(mRootWindowContainer.getDisplayContent(eq(mDisplayUniqueId)))
@@ -131,6 +136,7 @@
         LocalServices.addService(PackageManagerInternal.class, mMockPmi);
         when(mMockPmi.getPackageList(any())).thenReturn(new PackageList(
                 Collections.singletonList(TEST_COMPONENT.getPackageName()), /* observer */ null));
+        when(mMockPmi.getSystemUiServiceComponent()).thenReturn(new ComponentName("", ""));
         mTarget.onSystemReady();
 
         final ArgumentCaptor<PackageManagerInternal.PackageListObserver> observerCaptor =
@@ -210,6 +216,61 @@
     }
 
     @Test
+    public void testUsesRecordWithSameWindowLayoutAffinityInSameInstance_NoPreviousRecord() {
+        mTestTask.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
+        mTarget.saveTask(mTestTask);
+
+        mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
+        mTarget.getLaunchParams(mTaskWithDifferentComponent, null, mResult);
+
+        assertEquals(mTestDisplay.mDisplayId, mResult.mPreferredDisplayId);
+        assertEquals(TEST_WINDOWING_MODE, mResult.mWindowingMode);
+        assertEquals(TEST_BOUNDS, mResult.mBounds);
+    }
+
+    @Test
+    public void testUsesRecordWithSameWindowLayoutAffinityInSameInstance_HasOldPreviousRecord()
+            throws Exception {
+        mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
+        mTarget.saveTask(mTaskWithDifferentComponent);
+
+        Thread.sleep(1);  // Sleep 1ms so that the timestamp can for sure increase.
+
+        mTestTask.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
+        mTarget.saveTask(mTestTask);
+
+        mTarget.getLaunchParams(mTaskWithDifferentComponent, null, mResult);
+
+        assertEquals(mTestDisplay.mDisplayId, mResult.mPreferredDisplayId);
+        assertEquals(TEST_WINDOWING_MODE, mResult.mWindowingMode);
+        assertEquals(TEST_BOUNDS, mResult.mBounds);
+    }
+
+    @Test
+    public void testReturnsEmptyLaunchParamsUidInLaunchAffinityMismatch() {
+        mTestTask.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
+        mTarget.saveTask(mTestTask);
+
+        mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_DIFFERENT_AFFINITY_WITH_SAME_UID;
+        mResult.mWindowingMode = WINDOWING_MODE_FULLSCREEN;
+        mTarget.getLaunchParams(mTaskWithDifferentComponent, null, mResult);
+
+        assertTrue("Result must be empty.", mResult.isEmpty());
+    }
+
+    @Test
+    public void testReturnsEmptyLaunchParamsWindowLayoutAffinityMismatch() {
+        mTestTask.affinity = TEST_WINDOW_LAYOUT_AFFINITY;
+        mTarget.saveTask(mTestTask);
+
+        mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_ALTERNATIVE_WINDOW_LAYOUT_AFFINITY;
+        mResult.mWindowingMode = WINDOWING_MODE_FULLSCREEN;
+        mTarget.getLaunchParams(mTaskWithDifferentComponent, null, mResult);
+
+        assertTrue("Result must be empty.", mResult.isEmpty());
+    }
+
+    @Test
     public void testSavesAndRestoresLaunchParamsAcrossInstances() {
         mTarget.saveTask(mTestTask);
         mPersisterQueue.flush();
@@ -227,6 +288,52 @@
     }
 
     @Test
+    public void testUsesRecordWithSameWindowLayoutAffinityAcrossInstances_NoPreviousRecord() {
+        mTestTask.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
+        mTarget.saveTask(mTestTask);
+        mPersisterQueue.flush();
+
+        final LaunchParamsPersister target = new LaunchParamsPersister(mPersisterQueue, mSupervisor,
+                mUserFolderGetter);
+        target.onSystemReady();
+        target.onUnlockUser(TEST_USER_ID);
+
+        mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
+        target.getLaunchParams(mTaskWithDifferentComponent, null, mResult);
+
+        assertEquals(mTestDisplay.mDisplayId, mResult.mPreferredDisplayId);
+        assertEquals(TEST_WINDOWING_MODE, mResult.mWindowingMode);
+        assertEquals(TEST_BOUNDS, mResult.mBounds);
+    }
+
+    @Test
+    public void testUsesRecordWithSameWindowLayoutAffinityAcrossInstances_HasOldPreviousRecord()
+            throws Exception {
+        mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
+        mTarget.saveTask(mTaskWithDifferentComponent);
+        mPersisterQueue.flush();
+
+        // Sleep 1s because many file systems only save last modified time as precise as 1s so we
+        // can for sure know the last modified time is different.
+        Thread.sleep(1000);
+
+        mTestTask.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
+        mTarget.saveTask(mTestTask);
+        mPersisterQueue.flush();
+
+        final LaunchParamsPersister target = new LaunchParamsPersister(mPersisterQueue, mSupervisor,
+                mUserFolderGetter);
+        target.onSystemReady();
+        target.onUnlockUser(TEST_USER_ID);
+
+        target.getLaunchParams(mTaskWithDifferentComponent, null, mResult);
+
+        assertEquals(mTestDisplay.mDisplayId, mResult.mPreferredDisplayId);
+        assertEquals(TEST_WINDOWING_MODE, mResult.mWindowingMode);
+        assertEquals(TEST_BOUNDS, mResult.mBounds);
+    }
+
+    @Test
     public void testClearsRecordsOfTheUserOnUserCleanUp() {
         mTarget.saveTask(mTestTask);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 55d12db..560d03f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -40,6 +40,7 @@
 import android.app.AppOpsManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.IntentFilter;
@@ -213,6 +214,9 @@
                 anyString(), anyInt());
         doReturn(null).when(packageManagerInternal).getDefaultHomeActivity(anyInt());
 
+        ComponentName systemServiceComponent = new ComponentName("android.test.system.service", "");
+        doReturn(systemServiceComponent).when(packageManagerInternal).getSystemUiServiceComponent();
+
         // PowerManagerInternal
         final PowerManagerInternal pmi = mock(PowerManagerInternal.class);
         final PowerSaveState state = new PowerSaveState.Builder().build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index 53a3682..19ed7a9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -213,6 +213,16 @@
     }
 
     @Test
+    public void testRegisterTaskOrganizerWithExistingTasks() throws RemoteException {
+        final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+        final Task task = createTaskInStack(stack, 0 /* userId */);
+        stack.setWindowingMode(WINDOWING_MODE_PINNED);
+
+        final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_PINNED);
+        verify(organizer, times(1)).taskAppeared(any());
+    }
+
+    @Test
     public void testTaskTransaction() {
         removeGlobalMinSizeRestriction();
         final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
@@ -241,6 +251,32 @@
     }
 
     @Test
+    public void testSetWindowingMode() {
+        final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
+            .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        final WindowContainerTransaction t = new WindowContainerTransaction();
+
+        t.setWindowingMode(stack.mRemoteToken, WINDOWING_MODE_FULLSCREEN);
+        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+
+        assertEquals(WINDOWING_MODE_FULLSCREEN, stack.getWindowingMode());
+    }
+
+    @Test
+    public void testSetActivityWindowingMode() {
+        final ActivityRecord record = makePipableActivity();
+        final ActivityStack stack = record.getStack();
+        final WindowContainerTransaction t = new WindowContainerTransaction();
+
+        t.setWindowingMode(stack.mRemoteToken, WINDOWING_MODE_PINNED);
+        t.setActivityWindowingMode(stack.mRemoteToken, WINDOWING_MODE_FULLSCREEN);
+        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+
+        assertEquals(WINDOWING_MODE_FULLSCREEN, record.getWindowingMode());
+        assertEquals(WINDOWING_MODE_PINNED, stack.getWindowingMode());
+    }
+
+    @Test
     public void testContainerChanges() {
         removeGlobalMinSizeRestriction();
         final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
index e1475a4..91c3c27 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
@@ -77,7 +77,8 @@
     }
 
     @Override
-    public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, boolean sync)
+    public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, float zoom,
+            boolean sync)
             throws RemoteException {
     }
 
@@ -85,7 +86,6 @@
     public void dispatchWallpaperCommand(String action, int x, int y, int z, Bundle extras,
             boolean sync) throws RemoteException {
     }
-
     @Override
     public void dispatchDragEvent(DragEvent event) throws RemoteException {
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index aa66524..900f014 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -17,19 +17,29 @@
 package com.android.server.wm;
 
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
 
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.IBinder;
+import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.view.DisplayInfo;
 import android.view.Gravity;
@@ -139,4 +149,124 @@
         assertEquals(Configuration.ORIENTATION_LANDSCAPE, dc.getConfiguration().orientation);
         assertEquals(portraitFrame, wallpaperWindow.getFrameLw());
     }
+
+    @Test
+    public void testWallpaperZoom() throws RemoteException {
+        final DisplayContent dc = mWm.mRoot.getDefaultDisplay();
+        final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
+                mock(IBinder.class), true,  dc, true /* ownerCanManageAppTokens */);
+        final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
+                "wallpaperWindow");
+        wallpaperWindow.getAttrs().privateFlags |=
+                WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
+
+        final WindowState homeWindow = createWallpaperTargetWindow(dc);
+
+        spyOn(dc.mWallpaperController);
+        doReturn(true).when(dc.mWallpaperController).isWallpaperVisible();
+
+        dc.mWallpaperController.adjustWallpaperWindows();
+
+        spyOn(wallpaperWindow.mClient);
+
+        float zoom = .5f;
+        dc.mWallpaperController.setWallpaperZoomOut(homeWindow, zoom);
+        assertEquals(zoom, wallpaperWindow.mWallpaperZoomOut, .01f);
+        verify(wallpaperWindow.mClient).dispatchWallpaperOffsets(anyFloat(), anyFloat(), anyFloat(),
+                anyFloat(), eq(zoom), anyBoolean());
+    }
+
+    @Test
+    public void testWallpaperZoom_shouldNotScaleWallpaper() throws RemoteException {
+        final DisplayContent dc = mWm.mRoot.getDefaultDisplay();
+        final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
+                mock(IBinder.class), true,  dc, true /* ownerCanManageAppTokens */);
+        final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
+                "wallpaperWindow");
+        wallpaperWindow.getAttrs().privateFlags |=
+                WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
+
+        final WindowState homeWindow = createWallpaperTargetWindow(dc);
+
+        spyOn(dc.mWallpaperController);
+        doReturn(true).when(dc.mWallpaperController).isWallpaperVisible();
+
+        dc.mWallpaperController.adjustWallpaperWindows();
+
+        spyOn(wallpaperWindow.mClient);
+
+        float newZoom = .5f;
+        wallpaperWindow.mShouldScaleWallpaper = false;
+        // Set zoom, and make sure the window animator scale didn't actually change, but the zoom
+        // value did, and we do dispatch the zoom to the wallpaper service
+        dc.mWallpaperController.setWallpaperZoomOut(homeWindow, newZoom);
+        assertEquals(newZoom, wallpaperWindow.mWallpaperZoomOut, .01f);
+        assertEquals(1f, wallpaperWindow.mWinAnimator.mWallpaperScale, .01f);
+        verify(wallpaperWindow.mClient).dispatchWallpaperOffsets(anyFloat(), anyFloat(), anyFloat(),
+                anyFloat(), eq(newZoom), anyBoolean());
+    }
+
+    @Test
+    public void testWallpaperZoom_multipleCallers() {
+        final DisplayContent dc = mWm.mRoot.getDefaultDisplay();
+        final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
+                mock(IBinder.class), true,  dc,
+                true /* ownerCanManageAppTokens */);
+        final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
+                "wallpaperWindow");
+        wallpaperWindow.getAttrs().privateFlags |=
+                WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
+
+
+        spyOn(dc.mWallpaperController);
+        doReturn(true).when(dc.mWallpaperController).isWallpaperVisible();
+
+        final WindowState homeWindow = createWallpaperTargetWindow(dc);
+
+        WindowState otherWindow = createWindow(null /* parent */, TYPE_APPLICATION, dc,
+                "otherWindow");
+
+        dc.mWallpaperController.adjustWallpaperWindows();
+
+        spyOn(wallpaperWindow.mClient);
+
+        // Set zoom from 2 windows
+        float homeWindowInitialZoom = .5f;
+        float otherWindowInitialZoom = .7f;
+        dc.mWallpaperController.setWallpaperZoomOut(homeWindow, homeWindowInitialZoom);
+        dc.mWallpaperController.setWallpaperZoomOut(otherWindow, otherWindowInitialZoom);
+        // Make sure the largest one wins
+        assertEquals(otherWindowInitialZoom, wallpaperWindow.mWallpaperZoomOut, .01f);
+
+        // Change zoom to a larger zoom from homeWindow
+        float homeWindowZoom2 = .8f;
+        dc.mWallpaperController.setWallpaperZoomOut(homeWindow, homeWindowZoom2);
+        // New zoom should be current
+        assertEquals(homeWindowZoom2, wallpaperWindow.mWallpaperZoomOut, .01f);
+
+        // Set homeWindow zoom to a lower zoom, but keep the one from otherWindow
+        dc.mWallpaperController.setWallpaperZoomOut(homeWindow, homeWindowInitialZoom);
+
+        // Zoom from otherWindow should be the current.
+        assertEquals(otherWindowInitialZoom, wallpaperWindow.mWallpaperZoomOut, .01f);
+    }
+
+
+    private WindowState createWallpaperTargetWindow(DisplayContent dc) {
+        final ActivityRecord homeActivity = new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
+                .setStack(dc.getRootHomeTask())
+                .setCreateTask(true)
+                .build();
+        homeActivity.setVisibility(true);
+
+        WindowState appWindow = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
+                homeActivity, "wallpaperTargetWindow");
+        appWindow.getAttrs().flags |= FLAG_SHOW_WALLPAPER;
+        appWindow.mHasSurface = true;
+        spyOn(appWindow);
+        doReturn(true).when(appWindow).isDrawFinishedLw();
+
+        homeActivity.addWindow(appWindow);
+        return appWindow;
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index 34e487b..07a6179 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -29,6 +29,7 @@
 import static org.mockito.Mockito.when;
 
 import android.app.IApplicationThread;
+import android.content.ComponentName;
 import android.content.pm.ApplicationInfo;
 import android.content.res.Configuration;
 import android.platform.test.annotations.Presubmit;
@@ -55,8 +56,11 @@
     @Before
     public void setUp() {
         mMockListener = mock(WindowProcessListener.class);
+
+        ApplicationInfo info = mock(ApplicationInfo.class);
+        info.packageName = "test.package.name";
         mWpc = new WindowProcessController(
-                mService, mock(ApplicationInfo.class), null, 0, -1, null, mMockListener);
+                mService, info, null, 0, -1, null, mMockListener);
         mWpc.setThread(mock(IApplicationThread.class));
     }
 
@@ -176,6 +180,26 @@
         assertEquals(mWpc.getLastReportedConfiguration(), newConfig);
     }
 
+    @Test
+    public void testActivityNotOverridingSystemUiProcessConfig() {
+        final ComponentName systemUiServiceComponent = mService.getSysUiServiceComponentLocked();
+        ApplicationInfo applicationInfo = mock(ApplicationInfo.class);
+        applicationInfo.packageName = systemUiServiceComponent.getPackageName();
+
+        WindowProcessController wpc = new WindowProcessController(
+                mService, applicationInfo, null, 0, -1, null, mMockListener);
+        wpc.setThread(mock(IApplicationThread.class));
+
+        final ActivityRecord activity = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .setUseProcess(wpc)
+                .build();
+
+        wpc.addActivityIfNeeded(activity);
+        // System UI owned processes should not be registered for activity config changes.
+        assertFalse(wpc.registeredForActivityConfigChanges());
+    }
+
     private TestDisplayContent createTestDisplayContentInContainer() {
         return new TestDisplayContent.Builder(mService, 1000, 1500).build();
     }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
index af81ab6..be0987d 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
@@ -257,6 +257,9 @@
             int userHandle) {
         SQLiteDatabase db = getReadableDatabase();
         Cursor c = db.rawQuery(selectQuery, null);
+        if (DBG) {
+            Slog.w(TAG, "querying database: " + selectQuery);
+        }
 
         try {
             if (c.moveToFirst()) {
@@ -334,7 +337,10 @@
                     return model;
                 } while (c.moveToNext());
             }
-            Slog.w(TAG, "No SoundModel available for the given keyphrase");
+
+            if (DBG) {
+                Slog.w(TAG, "No SoundModel available for the given keyphrase");
+            }
         } finally {
             c.close();
             db.close();
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 0b24dd2..0eba07b 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -41,6 +41,7 @@
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.hardware.soundtrigger.IRecognitionStatusCallback;
+import android.hardware.soundtrigger.KeyphraseEnrollmentInfo;
 import android.hardware.soundtrigger.KeyphraseMetadata;
 import android.hardware.soundtrigger.ModelParams;
 import android.hardware.soundtrigger.SoundTrigger;
@@ -223,6 +224,7 @@
     class VoiceInteractionManagerServiceStub extends IVoiceInteractionManagerService.Stub {
 
         VoiceInteractionManagerServiceImpl mImpl;
+        KeyphraseEnrollmentInfo mEnrollmentApplicationInfo;
 
         private boolean mSafeMode;
         private int mCurUser;
@@ -447,6 +449,15 @@
             }
         }
 
+        private void getOrCreateEnrollmentApplicationInfo() {
+            synchronized (this) {
+                if (mEnrollmentApplicationInfo == null) {
+                    mEnrollmentApplicationInfo = new KeyphraseEnrollmentInfo(
+                            mContext.getPackageManager());
+                }
+            }
+        }
+
         private void setCurrentUserLocked(@UserIdInt int userHandle) {
             mCurUser = userHandle;
             final UserInfo userInfo = mUserManagerInternal.getUserInfo(mCurUser);
@@ -1380,12 +1391,17 @@
                 pw.println("  mCurUserUnlocked: " + mCurUserUnlocked);
                 pw.println("  mCurUserSupported: " + mCurUserSupported);
                 dumpSupportedUsers(pw, "  ");
+                if (mEnrollmentApplicationInfo == null) {
+                    pw.println("  (No enrollment application info)");
+                } else {
+                    pw.println("  " + mEnrollmentApplicationInfo.toString());
+                }
                 mDbHelper.dump(pw);
                 if (mImpl == null) {
                     pw.println("  (No active implementation)");
-                    return;
+                } else {
+                    mImpl.dumpLocked(fd, pw, args);
                 }
-                mImpl.dumpLocked(fd, pw, args);
             }
             mSoundTriggerInternal.dump(fd, pw, args);
         }
@@ -1438,8 +1454,9 @@
         }
 
         private boolean isCallerTrustedEnrollmentApplication() {
-            return mImpl.mEnrollmentApplicationInfo.isUidSupportedEnrollmentApplication(
-                    Binder.getCallingUid());
+            getOrCreateEnrollmentApplicationInfo();
+            return mEnrollmentApplicationInfo.isUidSupportedEnrollmentApplication(
+                            Binder.getCallingUid());
         }
 
         private void setImplLocked(VoiceInteractionManagerServiceImpl impl) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index b813f87..a62b03c 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -36,7 +36,6 @@
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
-import android.hardware.soundtrigger.KeyphraseEnrollmentInfo;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -79,7 +78,6 @@
     final IActivityManager mAm;
     final IActivityTaskManager mAtm;
     final VoiceInteractionServiceInfo mInfo;
-    final KeyphraseEnrollmentInfo mEnrollmentApplicationInfo;
     final ComponentName mSessionComponentName;
     final IWindowManager mIWindowManager;
     boolean mBound = false;
@@ -135,7 +133,6 @@
         mComponent = service;
         mAm = ActivityManager.getService();
         mAtm = ActivityTaskManager.getService();
-        mEnrollmentApplicationInfo = new KeyphraseEnrollmentInfo(context.getPackageManager());
         VoiceInteractionServiceInfo info;
         try {
             info = new VoiceInteractionServiceInfo(context.getPackageManager(), service, mUser);
@@ -406,7 +403,6 @@
             pw.println("  Active session:");
             mActiveSession.dump("    ", pw);
         }
-        pw.println("  " + mEnrollmentApplicationInfo.toString());
     }
 
     void startLocked() {
diff --git a/telecomm/java/android/telecom/CallRedirectionService.java b/telecomm/java/android/telecom/CallRedirectionService.java
index 36c6377..c832f53 100644
--- a/telecomm/java/android/telecom/CallRedirectionService.java
+++ b/telecomm/java/android/telecom/CallRedirectionService.java
@@ -38,16 +38,14 @@
  *
  * <p>
  * Below is an example manifest registration for a {@code CallRedirectionService}.
- * <pre>
  * {@code
  * <service android:name="your.package.YourCallRedirectionServiceImplementation"
- *          android:permission="android.permission.BIND_REDIRECTION_SERVICE">
+ *          android:permission="android.permission.BIND_CALL_REDIRECTION_SERVICE">
  *      <intent-filter>
  *          <action android:name="android.telecom.CallRedirectionService"/>
  *      </intent-filter>
  * </service>
  * }
- * </pre>
  */
 public abstract class CallRedirectionService extends Service {
     /**
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 0093843..bebbbd0 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -80,17 +80,20 @@
      * Reason code (returned via {@link #getReason()}) which indicates that a call could not be
      * completed because the cellular radio is off or out of service, the device is connected to
      * a wifi network, but the user has not enabled wifi calling.
+     * @hide
      */
     public static final String REASON_WIFI_ON_BUT_WFC_OFF = "REASON_WIFI_ON_BUT_WFC_OFF";
 
     /**
      * Reason code (returned via {@link #getReason()}), which indicates that the video telephony
      * call was disconnected because IMS access is blocked.
+     * @hide
      */
     public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED";
 
     /**
      * Reason code, which indicates that the conference call is simulating single party conference.
+     * @hide
      */
     public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
 
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 4e6e1a5..768c8ee 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -53,7 +53,6 @@
      * {@link android.telecom.ConnectionService}.
      * @hide
      */
-    @SystemApi
     public static final String EXTRA_SORT_ORDER =
             "android.telecom.extra.SORT_ORDER";
 
@@ -89,7 +88,6 @@
      * rather than cellular calls.
      * @hide
      */
-    @SystemApi
     public static final String EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE =
             "android.telecom.extra.ALWAYS_USE_VOIP_AUDIO_MODE";
 
@@ -114,7 +112,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String EXTRA_SUPPORTS_VIDEO_CALLING_FALLBACK =
             "android.telecom.extra.SUPPORTS_VIDEO_CALLING_FALLBACK";
 
@@ -163,7 +160,6 @@
      * in progress.
      * @hide
      */
-    @SystemApi
     public static final String EXTRA_PLAY_CALL_RECORDING_TONE =
             "android.telecom.extra.PLAY_CALL_RECORDING_TONE";
 
@@ -258,7 +254,6 @@
      * See {@link #getCapabilities}
      * @hide
      */
-    @SystemApi
     public static final int CAPABILITY_EMERGENCY_CALLS_ONLY = 0x80;
 
     /**
@@ -282,7 +277,6 @@
      * convert all outgoing video calls to emergency numbers to audio-only.
      * @hide
      */
-    @SystemApi
     public static final int CAPABILITY_EMERGENCY_VIDEO_CALLING = 0x200;
 
     /**
@@ -340,7 +334,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final int CAPABILITY_EMERGENCY_PREFERRED = 0x2000;
 
     /**
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 7f4fcc0..1792256 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -318,13 +318,13 @@
      * the remote handle of the new call.
      * @hide
      */
-    @SystemApi
     public static final String EXTRA_UNKNOWN_CALL_HANDLE =
             "android.telecom.extra.UNKNOWN_CALL_HANDLE";
 
     /**
      * Optional extra for incoming and outgoing calls containing a long which specifies the time the
      * call was created. This value is in milliseconds since boot.
+     * @hide
      */
     public static final String EXTRA_CALL_CREATED_TIME_MILLIS =
             "android.telecom.extra.CALL_CREATED_TIME_MILLIS";
@@ -388,7 +388,6 @@
      * </ul>
      * @hide
      */
-    @SystemApi
     public static final String EXTRA_CALL_TECHNOLOGY_TYPE =
             "android.telecom.extra.CALL_TECHNOLOGY_TYPE";
 
@@ -731,7 +730,6 @@
      * @see #EXTRA_CURRENT_TTY_MODE
      * @hide
      */
-    @SystemApi
     public static final String ACTION_CURRENT_TTY_MODE_CHANGED =
             "android.telecom.action.CURRENT_TTY_MODE_CHANGED";
 
@@ -746,7 +744,6 @@
      * </ul>
      * @hide
      */
-    @SystemApi
     public static final String EXTRA_CURRENT_TTY_MODE =
             "android.telecom.extra.CURRENT_TTY_MODE";
 
@@ -757,7 +754,6 @@
      * @see #EXTRA_TTY_PREFERRED_MODE
      * @hide
      */
-    @SystemApi
     public static final String ACTION_TTY_PREFERRED_MODE_CHANGED =
             "android.telecom.action.TTY_PREFERRED_MODE_CHANGED";
 
@@ -768,7 +764,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String EXTRA_TTY_PREFERRED_MODE =
             "android.telecom.extra.TTY_PREFERRED_MODE";
 
@@ -846,7 +841,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String EXTRA_CALL_SOURCE = "android.telecom.extra.CALL_SOURCE";
 
     /**
@@ -943,8 +937,8 @@
      */
     public TelecomManager(Context context, ITelecomService telecomServiceImpl) {
         Context appContext = context.getApplicationContext();
-        if (appContext != null && Objects.equals(context.getFeatureId(),
-                appContext.getFeatureId())) {
+        if (appContext != null && Objects.equals(context.getAttributionTag(),
+                appContext.getAttributionTag())) {
             mContext = appContext;
         } else {
             mContext = context;
@@ -978,7 +972,7 @@
         try {
             if (isServiceConnected()) {
                 return getTelecomService().getDefaultOutgoingPhoneAccount(uriScheme,
-                        mContext.getOpPackageName(), mContext.getFeatureId());
+                        mContext.getOpPackageName(), mContext.getAttributionTag());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelecomService#getDefaultOutgoingPhoneAccount", e);
@@ -1176,7 +1170,7 @@
         try {
             if (isServiceConnected()) {
                 return getTelecomService().getSelfManagedPhoneAccounts(mContext.getOpPackageName(),
-                        mContext.getFeatureId());
+                        mContext.getAttributionTag());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelecomService#getSelfManagedPhoneAccounts()", e);
@@ -1202,7 +1196,7 @@
         try {
             if (isServiceConnected()) {
                 return getTelecomService().getCallCapablePhoneAccounts(includeDisabledAccounts,
-                        mContext.getOpPackageName(), mContext.getFeatureId());
+                        mContext.getOpPackageName(), mContext.getAttributionTag());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelecomService#getCallCapablePhoneAccounts(" +
@@ -1506,7 +1500,7 @@
         try {
             if (isServiceConnected()) {
                 return getTelecomService().isVoiceMailNumber(accountHandle, number,
-                        mContext.getOpPackageName(), mContext.getFeatureId());
+                        mContext.getOpPackageName(), mContext.getAttributionTag());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException calling ITelecomService#isVoiceMailNumber.", e);
@@ -1528,7 +1522,7 @@
         try {
             if (isServiceConnected()) {
                 return getTelecomService().getVoiceMailNumber(accountHandle,
-                        mContext.getOpPackageName(), mContext.getFeatureId());
+                        mContext.getOpPackageName(), mContext.getAttributionTag());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException calling ITelecomService#hasVoiceMailNumber.", e);
@@ -1549,7 +1543,7 @@
         try {
             if (isServiceConnected()) {
                 return getTelecomService().getLine1Number(accountHandle,
-                        mContext.getOpPackageName(), mContext.getFeatureId());
+                        mContext.getOpPackageName(), mContext.getAttributionTag());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException calling ITelecomService#getLine1Number.", e);
@@ -1571,7 +1565,7 @@
         try {
             if (isServiceConnected()) {
                 return getTelecomService().isInCall(mContext.getOpPackageName(),
-                        mContext.getFeatureId());
+                        mContext.getAttributionTag());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException calling isInCall().", e);
@@ -1597,7 +1591,7 @@
         try {
             if (isServiceConnected()) {
                 return getTelecomService().isInManagedCall(mContext.getOpPackageName(),
-                        mContext.getFeatureId());
+                        mContext.getAttributionTag());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException calling isInManagedCall().", e);
@@ -1778,7 +1772,7 @@
         try {
             if (isServiceConnected()) {
                 return getTelecomService().isTtySupported(mContext.getOpPackageName(),
-                        mContext.getFeatureId());
+                        mContext.getAttributionTag());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException attempting to get TTY supported state.", e);
@@ -1803,7 +1797,7 @@
         try {
             if (isServiceConnected()) {
                 return getTelecomService().getCurrentTtyMode(mContext.getOpPackageName(),
-                        mContext.getFeatureId());
+                        mContext.getAttributionTag());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException attempting to get the current TTY mode.", e);
@@ -2033,7 +2027,7 @@
         if (service != null) {
             try {
                 service.showInCallScreen(showDialpad, mContext.getOpPackageName(),
-                        mContext.getFeatureId());
+                        mContext.getAttributionTag());
             } catch (RemoteException e) {
                 Log.e(TAG, "Error calling ITelecomService#showCallScreen", e);
             }
@@ -2096,7 +2090,7 @@
             }
             try {
                 service.placeCall(address, extras == null ? new Bundle() : extras,
-                        mContext.getOpPackageName(), mContext.getFeatureId());
+                        mContext.getOpPackageName(), mContext.getAttributionTag());
             } catch (RemoteException e) {
                 Log.e(TAG, "Error calling ITelecomService#placeCall", e);
             }
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index 9ae86c8..dcd4eb5 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -1,7 +1,6 @@
 package android.telephony;
 
 import android.annotation.IntDef;
-import android.provider.Telephony;
 import android.telecom.Connection;
 import android.telephony.data.ApnSetting;
 
@@ -653,15 +652,6 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface UiccAppType{}
 
-    /** @hide */
-    @IntDef({
-            Telephony.Carriers.SKIP_464XLAT_DEFAULT,
-            Telephony.Carriers.SKIP_464XLAT_DISABLE,
-            Telephony.Carriers.SKIP_464XLAT_ENABLE,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Skip464XlatStatus {}
-
     /**
      * Override network type
      */
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 4415508..e2112a59 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2382,17 +2382,16 @@
      * <p> If a measure is not set, signal criteria reporting from modem will not be triggered and
      * not be used for calculating signal level. If multiple measures are set bit, the parameter
      * whose value is smallest is used to indicate the signal level.
+     * <UL>
+     *  <LI>RSRP = 1 << 0</LI>
+     *  <LI>RSRQ = 1 << 1</LI>
+     *  <LI>RSSNR = 1 << 2</LI>
+     * </UL>
+     * <p> The value of this key must be bitwise OR of {@link CellSignalStrengthLte#USE_RSRP},
+     * {@link CellSignalStrengthLte#USE_RSRQ}, {@link CellSignalStrengthLte#USE_RSSNR}.
      *
-     *  RSRP = 1 << 0,
-     *  RSRQ = 1 << 1,
-     *  RSSNR = 1 << 2,
-     *
-     *  The value of this key must be bitwise OR of {@link CellSignalStrengthLte#USE_RSRP},
-     *  {@link CellSignalStrengthLte#USE_RSRQ}, {@link CellSignalStrengthLte#USE_RSSNR}.
-     *
-     * For example, if both RSRP and RSRQ are used, the value of key is 3 (1 << 0 | 1 << 1).
-     * If the key is invalid or not configured, a default value (RSRP = 1 << 0)
-     * will apply.
+     * <p> For example, if both RSRP and RSRQ are used, the value of key is 3 (1 << 0 | 1 << 1).
+     * If the key is invalid or not configured, a default value (RSRP = 1 << 0) will apply.
      *
      * @hide
      */
@@ -2401,16 +2400,18 @@
 
     /**
      * List of 4 customized 5G SS reference signal received power (SSRSRP) thresholds.
-     *
+     * <p>
      * Reference: 3GPP TS 38.215
-     *
+     * <p>
      * 4 threshold integers must be within the boundaries [-140 dB, -44 dB], and the levels are:
-     *     "NONE: [-140, threshold1]"
-     *     "POOR: (threshold1, threshold2]"
-     *     "MODERATE: (threshold2, threshold3]"
-     *     "GOOD:  (threshold3, threshold4]"
-     *     "EXCELLENT:  (threshold4, -44]"
-     *
+     * <UL>
+     *     <LI>"NONE: [-140, threshold1]"</LI>
+     *     <LI>"POOR: (threshold1, threshold2]"</LI>
+     *     <LI>"MODERATE: (threshold2, threshold3]"</LI>
+     *     <LI>"GOOD:  (threshold3, threshold4]"</LI>
+     *     <LI>"EXCELLENT:  (threshold4, -44]"</LI>
+     * </UL>
+     * <p>
      * This key is considered invalid if the format is violated. If the key is invalid or
      * not configured, a default value set will apply.
      */
@@ -2419,16 +2420,18 @@
 
     /**
      * List of 4 customized 5G SS reference signal received quality (SSRSRQ) thresholds.
-     *
+     * <p>
      * Reference: 3GPP TS 38.215
-     *
+     * <p>
      * 4 threshold integers must be within the boundaries [-20 dB, -3 dB], and the levels are:
-     *     "NONE: [-20, threshold1]"
-     *     "POOR: (threshold1, threshold2]"
-     *     "MODERATE: (threshold2, threshold3]"
-     *     "GOOD:  (threshold3, threshold4]"
-     *     "EXCELLENT:  (threshold4, -3]"
-     *
+     * <UL>
+     *     <LI>"NONE: [-20, threshold1]"</LI>
+     *     <LI>"POOR: (threshold1, threshold2]"</LI>
+     *     <LI>"MODERATE: (threshold2, threshold3]"</LI>
+     *     <LI>"GOOD:  (threshold3, threshold4]"</LI>
+     *     <LI>"EXCELLENT:  (threshold4, -3]"</LI>
+     * </UL>
+     * <p>
      * This key is considered invalid if the format is violated. If the key is invalid or
      * not configured, a default value set will apply.
      */
@@ -2437,17 +2440,19 @@
 
     /**
      * List of 4 customized 5G SS signal-to-noise and interference ratio (SSSINR) thresholds.
-     *
+     * <p>
      * Reference: 3GPP TS 38.215,
      *            3GPP TS 38.133 10.1.16.1
-     *
+     * <p>
      * 4 threshold integers must be within the boundaries [-23 dB, 40 dB], and the levels are:
-     *     "NONE: [-23, threshold1]"
-     *     "POOR: (threshold1, threshold2]"
-     *     "MODERATE: (threshold2, threshold3]"
-     *     "GOOD:  (threshold3, threshold4]"
-     *     "EXCELLENT:  (threshold4, 40]"
-     *
+     * <UL>
+     *     <LI>"NONE: [-23, threshold1]"</LI>
+     *     <LI>"POOR: (threshold1, threshold2]"</LI>
+     *     <LI>"MODERATE: (threshold2, threshold3]"</LI>
+     *     <LI>"GOOD:  (threshold3, threshold4]"</LI>
+     *     <LI>"EXCELLENT:  (threshold4, 40]"</LI>
+     * </UL>
+     * <p>
      * This key is considered invalid if the format is violated. If the key is invalid or
      * not configured, a default value set will apply.
      */
@@ -2462,19 +2467,19 @@
      * <p> If a measure is not set, signal criteria reporting from modem will not be triggered and
      * not be used for calculating signal level. If multiple measures are set bit, the parameter
      * whose value is smallest is used to indicate the signal level.
-     *
-     *  SSRSRP = 1 << 0,
-     *  SSRSRQ = 1 << 1,
-     *  SSSINR = 1 << 2,
-     *
+     * <UL>
+     *  <LI>SSRSRP = 1 << 0</LI>
+     *  <LI>SSRSRQ = 1 << 1</LI>
+     *  <LI>SSSINR = 1 << 2</LI>
+     * </UL>
      *  The value of this key must be bitwise OR of {@link CellSignalStrengthNr#USE_SSRSRP},
      *  {@link CellSignalStrengthNr#USE_SSRSRQ}, {@link CellSignalStrengthNr#USE_SSSINR}.
      *
-     * For example, if both SSRSRP and SSSINR are used, the value of key is 5 (1 << 0 | 1 << 2).
+     * <p> For example, if both SSRSRP and SSSINR are used, the value of key is 5 (1 << 0 | 1 << 2).
      * If the key is invalid or not configured, a default value (SSRSRP = 1 << 0) will apply.
      *
-     *  Reference: 3GPP TS 38.215,
-     *             3GPP TS 38.133 10.1.16.1
+     * <p> Reference: 3GPP TS 38.215,
+     *                3GPP TS 38.133 10.1.16.1
      *
      * @hide
      */
@@ -3530,6 +3535,15 @@
             "support_wps_over_ims_bool";
 
     /**
+     * The two digital number pattern of MMI code which is defined by carrier.
+     * If the dial number matches this pattern, it will be dialed out normally not USSD.
+     *
+     * @hide
+     */
+    public static final String KEY_MMI_TWO_DIGIT_NUMBER_PATTERN_STRING_ARRAY =
+            "mmi_two_digit_number_pattern_string_array";
+
+    /**
      * Holds the list of carrier certificate hashes.
      * Note that each carrier has its own certificates.
      */
@@ -4086,6 +4100,7 @@
                 new int[] {4 /* BUSY */});
         sDefaults.putBoolean(KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL, false);
         sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG, 2000);
+        sDefaults.putStringArray(KEY_MMI_TWO_DIGIT_NUMBER_PATTERN_STRING_ARRAY, new String[0]);
         sDefaults.putInt(KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT,
                 CellSignalStrengthLte.USE_RSRP);
         // Default wifi configurations.
@@ -4147,7 +4162,7 @@
                 return null;
             }
             return loader.getConfigForSubIdWithFeature(subId, mContext.getOpPackageName(),
-                    mContext.getFeatureId());
+                    mContext.getAttributionTag());
         } catch (RemoteException ex) {
             Rlog.e(TAG, "Error getting config for subId " + subId + ": "
                     + ex.toString());
diff --git a/telephony/java/android/telephony/PinResult.java b/telephony/java/android/telephony/PinResult.java
index c14bd91..98d6448 100644
--- a/telephony/java/android/telephony/PinResult.java
+++ b/telephony/java/android/telephony/PinResult.java
@@ -19,7 +19,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -32,7 +31,6 @@
  *
  * @hide
  */
-@SystemApi
 public final class PinResult implements Parcelable {
     /** @hide */
     @IntDef({
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 45cba51..dd20d06 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -580,7 +580,6 @@
      *
      * @hide
      */
-    @SystemApi
     public @RegState int getDataRegistrationState() {
         return getDataRegState();
     }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 8ac9023b..01a40f5 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1144,7 +1144,7 @@
             ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 subInfo = iSub.getActiveSubscriptionInfo(subId, mContext.getOpPackageName(),
-                        mContext.getFeatureId());
+                        mContext.getAttributionTag());
             }
         } catch (RemoteException ex) {
             // ignore it
@@ -1178,7 +1178,7 @@
             ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getActiveSubscriptionInfoForIccId(iccId, mContext.getOpPackageName(),
-                        mContext.getFeatureId());
+                        mContext.getAttributionTag());
             }
         } catch (RemoteException ex) {
             // ignore it
@@ -1212,7 +1212,7 @@
             ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getActiveSubscriptionInfoForSimSlotIndex(slotIndex,
-                        mContext.getOpPackageName(), mContext.getFeatureId());
+                        mContext.getOpPackageName(), mContext.getAttributionTag());
             }
         } catch (RemoteException ex) {
             // ignore it
@@ -1236,7 +1236,7 @@
             ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getAllSubInfoList(mContext.getOpPackageName(),
-                        mContext.getFeatureId());
+                        mContext.getAttributionTag());
             }
         } catch (RemoteException ex) {
             // ignore it
@@ -1300,8 +1300,13 @@
      * both active and hidden SubscriptionInfos.
      *
      */
-    public @Nullable List<SubscriptionInfo> getActiveAndHiddenSubscriptionInfoList() {
-        return getActiveSubscriptionInfoList(/* userVisibleonly */false);
+    public @NonNull List<SubscriptionInfo> getCompleteActiveSubscriptionInfoList() {
+        List<SubscriptionInfo> completeList = getActiveSubscriptionInfoList(
+                /* userVisibleonly */false);
+        if (completeList == null) {
+            completeList = new ArrayList<>();
+        }
+        return completeList;
     }
 
     /**
@@ -1317,7 +1322,7 @@
             ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName(),
-                        mContext.getFeatureId());
+                        mContext.getAttributionTag());
             }
         } catch (RemoteException ex) {
             // ignore it
@@ -1368,7 +1373,7 @@
             ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getAvailableSubscriptionInfoList(mContext.getOpPackageName(),
-                        mContext.getFeatureId());
+                        mContext.getAttributionTag());
             }
         } catch (RemoteException ex) {
             // ignore it
@@ -1486,7 +1491,7 @@
             ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getAllSubInfoCount(mContext.getOpPackageName(),
-                        mContext.getFeatureId());
+                        mContext.getAttributionTag());
             }
         } catch (RemoteException ex) {
             // ignore it
@@ -1515,7 +1520,7 @@
             ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getActiveSubInfoCount(mContext.getOpPackageName(),
-                        mContext.getFeatureId());
+                        mContext.getAttributionTag());
             }
         } catch (RemoteException ex) {
             // ignore it
@@ -2270,7 +2275,7 @@
             ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 resultValue = iSub.getSubscriptionProperty(subId, propKey,
-                        context.getOpPackageName(), context.getFeatureId());
+                        context.getOpPackageName(), context.getAttributionTag());
             }
         } catch (RemoteException ex) {
             // ignore it
@@ -2434,7 +2439,7 @@
             ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 return iSub.isActiveSubId(subId, mContext.getOpPackageName(),
-                        mContext.getFeatureId());
+                        mContext.getAttributionTag());
             }
         } catch (RemoteException ex) {
         }
@@ -2697,13 +2702,14 @@
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public @NonNull List<SubscriptionInfo> getOpportunisticSubscriptions() {
         String contextPkg = mContext != null ? mContext.getOpPackageName() : "<unknown>";
-        String contextFeature = mContext != null ? mContext.getFeatureId() : null;
+        String contextAttributionTag = mContext != null ? mContext.getAttributionTag() : null;
         List<SubscriptionInfo> subInfoList = null;
 
         try {
             ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
-                subInfoList = iSub.getOpportunisticSubscriptions(contextPkg, contextFeature);
+                subInfoList = iSub.getOpportunisticSubscriptions(contextPkg,
+                        contextAttributionTag);
             }
         } catch (RemoteException ex) {
             // ignore it
@@ -2942,7 +2948,7 @@
     public @NonNull List<SubscriptionInfo> getSubscriptionsInGroup(@NonNull ParcelUuid groupUuid) {
         Preconditions.checkNotNull(groupUuid, "groupUuid can't be null");
         String contextPkg = mContext != null ? mContext.getOpPackageName() : "<unknown>";
-        String contextFeature = mContext != null ? mContext.getFeatureId() : null;
+        String contextAttributionTag = mContext != null ? mContext.getAttributionTag() : null;
         if (VDBG) {
             logd("[getSubscriptionsInGroup]+ groupUuid:" + groupUuid);
         }
@@ -2951,7 +2957,8 @@
         try {
             ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
-                result = iSub.getSubscriptionsInGroup(groupUuid, contextPkg, contextFeature);
+                result = iSub.getSubscriptionsInGroup(groupUuid, contextPkg,
+                        contextAttributionTag);
             } else {
                 if (!isSystemProcess()) {
                     throw new IllegalStateException("telephony service is null.");
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 23a2b9c..610ec5e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -345,10 +345,10 @@
         mSubId = subId;
         Context appContext = context.getApplicationContext();
         if (appContext != null) {
-            if (Objects.equals(context.getFeatureId(), appContext.getFeatureId())) {
+            if (Objects.equals(context.getAttributionTag(), appContext.getAttributionTag())) {
                 mContext = appContext;
             } else {
-                mContext = appContext.createFeatureContext(context.getFeatureId());
+                mContext = appContext.createAttributionContext(context.getAttributionTag());
             }
         } else {
             mContext = context;
@@ -393,12 +393,12 @@
         }
     }
 
-    private String getFeatureId() {
+    private String getAttributionTag() {
         // For legacy reasons the TelephonyManager has API for getting
         // a static instance with no context set preventing us from
-        // getting the feature Id.
+        // getting the attribution tag.
         if (mContext != null) {
-            return mContext.getFeatureId();
+            return mContext.getAttributionTag();
         }
         return null;
     }
@@ -1896,7 +1896,7 @@
 
         try {
             return telephony.getDeviceSoftwareVersionForSlot(slotIndex, getOpPackageName(),
-                    getFeatureId());
+                    getAttributionTag());
         } catch (RemoteException ex) {
             return null;
         } catch (NullPointerException ex) {
@@ -1938,7 +1938,7 @@
             if (telephony == null)
                 return null;
             return telephony.getDeviceIdWithFeature(mContext.getOpPackageName(),
-                    mContext.getFeatureId());
+                    mContext.getAttributionTag());
         } catch (RemoteException ex) {
             return null;
         } catch (NullPointerException ex) {
@@ -1983,7 +1983,7 @@
             if (info == null)
                 return null;
             return info.getDeviceIdForPhone(slotIndex, mContext.getOpPackageName(),
-                    mContext.getFeatureId());
+                    mContext.getAttributionTag());
         } catch (RemoteException ex) {
             return null;
         } catch (NullPointerException ex) {
@@ -2041,7 +2041,7 @@
         if (telephony == null) return null;
 
         try {
-            return telephony.getImeiForSlot(slotIndex, getOpPackageName(), getFeatureId());
+            return telephony.getImeiForSlot(slotIndex, getOpPackageName(), getAttributionTag());
         } catch (RemoteException ex) {
             return null;
         } catch (NullPointerException ex) {
@@ -2135,7 +2135,8 @@
         if (telephony == null) return null;
 
         try {
-            String meid = telephony.getMeidForSlot(slotIndex, getOpPackageName(), getFeatureId());
+            String meid = telephony.getMeidForSlot(slotIndex, getOpPackageName(),
+                    getAttributionTag());
             if (TextUtils.isEmpty(meid)) {
                 Log.d(TAG, "getMeid: return null because MEID is not available");
                 return null;
@@ -2237,7 +2238,7 @@
             if (info == null)
                 return null;
             String nai = info.getNaiForSubscriber(subId, mContext.getOpPackageName(),
-                    mContext.getFeatureId());
+                    mContext.getAttributionTag());
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
                 Rlog.v(TAG, "Nai = " + nai);
             }
@@ -2271,7 +2272,7 @@
             }
 
             CellIdentity cellIdentity = telephony.getCellLocation(mContext.getOpPackageName(),
-                    mContext.getFeatureId());
+                    mContext.getAttributionTag());
             CellLocation cl = cellIdentity.asCellLocation();
             if (cl == null || cl.isEmpty()) {
                 Rlog.d(TAG, "getCellLocation returning null because CellLocation is empty or"
@@ -2355,7 +2356,7 @@
             if (telephony == null)
                 return null;
             return telephony.getNeighboringCellInfo(mContext.getOpPackageName(),
-                    mContext.getFeatureId());
+                    mContext.getAttributionTag());
         } catch (RemoteException ex) {
             return null;
         } catch (NullPointerException ex) {
@@ -2371,7 +2372,12 @@
     public static final int PHONE_TYPE_CDMA = PhoneConstants.PHONE_TYPE_CDMA;
     /** Phone is via SIP. */
     public static final int PHONE_TYPE_SIP = PhoneConstants.PHONE_TYPE_SIP;
-    /** Phone is via IMS. */
+
+    /**
+     * Phone is via IMS.
+     *
+     * @hide
+     */
     public static final int PHONE_TYPE_IMS = PhoneConstants.PHONE_TYPE_IMS;
 
     /**
@@ -2379,7 +2385,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final int PHONE_TYPE_THIRD_PARTY = PhoneConstants.PHONE_TYPE_THIRD_PARTY;
 
     /**
@@ -2957,7 +2962,7 @@
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 return telephony.getNetworkTypeForSubscriber(subId, getOpPackageName(),
-                        getFeatureId());
+                        getAttributionTag());
             } else {
                 // This can happen when the ITelephony interface is not up yet.
                 return NETWORK_TYPE_UNKNOWN;
@@ -3022,7 +3027,7 @@
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 return telephony.getDataNetworkTypeForSubscriber(subId, getOpPackageName(),
-                        getFeatureId());
+                        getAttributionTag());
             } else {
                 // This can happen when the ITelephony interface is not up yet.
                 return NETWORK_TYPE_UNKNOWN;
@@ -3059,7 +3064,7 @@
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 return telephony.getVoiceNetworkTypeForSubscriber(subId, getOpPackageName(),
-                        getFeatureId());
+                        getAttributionTag());
             } else {
                 // This can happen when the ITelephony interface is not up yet.
                 return NETWORK_TYPE_UNKNOWN;
@@ -3763,29 +3768,6 @@
     }
 
     /**
-     * Returns the ISO-3166 country code equivalent for the SIM provider's country code
-     * of the default subscription
-     * <p>
-     * The ISO-3166 country code is provided in lowercase 2 character format.
-     * @return the lowercase 2 character ISO-3166 country code, or empty string is not available.
-     * <p>
-     * Note: This API is introduced to unblock mainlining work as the following APIs in
-     * Linkify.java invokes getSimCountryIso() without a context. TODO(Bug 144576376): remove
-     * this API once the following APIs are redesigned to access telephonymanager with a context.
-     *
-     * {@link Linkify#addLinks(@NonNull Spannable text, @LinkifyMask int mask)}
-     * {@link Linkify#addLinks(@NonNull Spannable text, @LinkifyMask int mask,
-               @Nullable Function<String, URLSpan> urlSpanFactory)}
-     *
-     * @hide
-     */
-    @SystemApi
-    @NonNull
-    public static String getDefaultSimCountryIso() {
-        return getSimCountryIso(SubscriptionManager.getDefaultSubscriptionId());
-    }
-
-    /**
      * Returns the ISO country code equivalent for the SIM provider's country code.
      *
      * @param subId for which SimCountryIso is returned
@@ -3868,7 +3850,7 @@
             if (info == null)
                 return null;
             return info.getIccSerialNumberForSubscriber(subId, mContext.getOpPackageName(),
-                    mContext.getFeatureId());
+                    mContext.getAttributionTag());
         } catch (RemoteException ex) {
             return null;
         } catch (NullPointerException ex) {
@@ -3912,7 +3894,7 @@
             if (telephony == null)
                 return PhoneConstants.LTE_ON_CDMA_UNKNOWN;
             return telephony.getLteOnCdmaModeForSubscriber(subId, getOpPackageName(),
-                    getFeatureId());
+                    getAttributionTag());
         } catch (RemoteException ex) {
             // Assume no ICC card if remote exception which shouldn't happen
             return PhoneConstants.LTE_ON_CDMA_UNKNOWN;
@@ -4141,7 +4123,7 @@
             if (info == null)
                 return null;
             return info.getSubscriberIdForSubscriber(subId, mContext.getOpPackageName(),
-                    mContext.getFeatureId());
+                    mContext.getAttributionTag());
         } catch (RemoteException ex) {
             return null;
         } catch (NullPointerException ex) {
@@ -4309,7 +4291,7 @@
             if (info == null)
                 return null;
             return info.getGroupIdLevel1ForSubscriber(getSubId(), mContext.getOpPackageName(),
-                    mContext.getFeatureId());
+                    mContext.getAttributionTag());
         } catch (RemoteException ex) {
             return null;
         } catch (NullPointerException ex) {
@@ -4333,7 +4315,7 @@
             if (info == null)
                 return null;
             return info.getGroupIdLevel1ForSubscriber(subId, mContext.getOpPackageName(),
-                    mContext.getFeatureId());
+                    mContext.getAttributionTag());
         } catch (RemoteException ex) {
             return null;
         } catch (NullPointerException ex) {
@@ -4384,7 +4366,7 @@
             ITelephony telephony = getITelephony();
             if (telephony != null)
                 number = telephony.getLine1NumberForDisplay(subId, mContext.getOpPackageName(),
-                         mContext.getFeatureId());
+                         mContext.getAttributionTag());
         } catch (RemoteException ex) {
         } catch (NullPointerException ex) {
         }
@@ -4396,7 +4378,7 @@
             if (info == null)
                 return null;
             return info.getLine1NumberForSubscriber(subId, mContext.getOpPackageName(),
-                    mContext.getFeatureId());
+                    mContext.getAttributionTag());
         } catch (RemoteException ex) {
             return null;
         } catch (NullPointerException ex) {
@@ -4475,7 +4457,7 @@
             ITelephony telephony = getITelephony();
             if (telephony != null)
                 alphaTag = telephony.getLine1AlphaTagForDisplay(subId,
-                        getOpPackageName(), getFeatureId());
+                        getOpPackageName(), getAttributionTag());
         } catch (RemoteException ex) {
         } catch (NullPointerException ex) {
         }
@@ -4487,7 +4469,7 @@
             if (info == null)
                 return null;
             return info.getLine1AlphaTagForSubscriber(subId, getOpPackageName(),
-                    getFeatureId());
+                    getAttributionTag());
         } catch (RemoteException ex) {
             return null;
         } catch (NullPointerException ex) {
@@ -4517,7 +4499,7 @@
             ITelephony telephony = getITelephony();
             if (telephony != null)
                 return telephony.getMergedSubscriberIds(getSubId(), getOpPackageName(),
-                        getFeatureId());
+                        getAttributionTag());
         } catch (RemoteException ex) {
         } catch (NullPointerException ex) {
         }
@@ -4574,7 +4556,7 @@
             IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
-            return info.getMsisdnForSubscriber(subId, getOpPackageName(), getFeatureId());
+            return info.getMsisdnForSubscriber(subId, getOpPackageName(), getAttributionTag());
         } catch (RemoteException ex) {
             return null;
         } catch (NullPointerException ex) {
@@ -4609,7 +4591,7 @@
             if (info == null)
                 return null;
             return info.getVoiceMailNumberForSubscriber(subId, getOpPackageName(),
-                    getFeatureId());
+                    getAttributionTag());
         } catch (RemoteException ex) {
             return null;
         } catch (NullPointerException ex) {
@@ -4734,7 +4716,7 @@
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 return telephony.getVisualVoicemailPackageName(mContext.getOpPackageName(),
-                        getFeatureId(), getSubId());
+                        getAttributionTag(), getSubId());
             }
         } catch (RemoteException ex) {
         } catch (NullPointerException ex) {
@@ -5171,7 +5153,7 @@
             if (telephony == null)
                 return 0;
             return telephony.getVoiceMessageCountForSubscriber(subId, getOpPackageName(),
-                    getFeatureId());
+                    getAttributionTag());
         } catch (RemoteException ex) {
             return 0;
         } catch (NullPointerException ex) {
@@ -5208,7 +5190,7 @@
             if (info == null)
                 return null;
             return info.getVoiceMailAlphaTagForSubscriber(subId, getOpPackageName(),
-                    getFeatureId());
+                    getAttributionTag());
         } catch (RemoteException ex) {
             return null;
         } catch (NullPointerException ex) {
@@ -5591,7 +5573,7 @@
                 (TelephonyRegistryManager)
                         mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
         if (telephonyRegistry != null) {
-            telephonyRegistry.listenForSubscriber(mSubId, getOpPackageName(), getFeatureId(),
+            telephonyRegistry.listenForSubscriber(mSubId, getOpPackageName(), getAttributionTag(),
                     listener, events, notifyNow);
         } else {
             Rlog.w(TAG, "telephony registry not ready.");
@@ -5624,7 +5606,7 @@
             if (telephony == null)
                 return -1;
             return telephony.getCdmaEriIconIndexForSubscriber(subId, getOpPackageName(),
-                    getFeatureId());
+                    getAttributionTag());
         } catch (RemoteException ex) {
             // the phone process is restarting.
             return -1;
@@ -5648,7 +5630,7 @@
             if (telephony == null)
                 return -1;
             return telephony.getCdmaEriIconModeForSubscriber(subId, getOpPackageName(),
-                    getFeatureId());
+                    getAttributionTag());
         } catch (RemoteException ex) {
             // the phone process is restarting.
             return -1;
@@ -5680,7 +5662,7 @@
             if (telephony == null)
                 return null;
             return telephony.getCdmaEriTextForSubscriber(subId, getOpPackageName(),
-                    getFeatureId());
+                    getAttributionTag());
         } catch (RemoteException ex) {
             // the phone process is restarting.
             return null;
@@ -5772,7 +5754,7 @@
             ITelephony telephony = getITelephony();
             if (telephony == null)
                 return null;
-            return telephony.getAllCellInfo(getOpPackageName(), getFeatureId());
+            return telephony.getAllCellInfo(getOpPackageName(), getAttributionTag());
         } catch (RemoteException ex) {
         } catch (NullPointerException ex) {
         }
@@ -5872,7 +5854,7 @@
                                 Binder.restoreCallingIdentity(identity);
                             }
                         }
-                    }, getOpPackageName(), getFeatureId());
+                    }, getOpPackageName(), getAttributionTag());
         } catch (RemoteException ex) {
         }
     }
@@ -5923,7 +5905,7 @@
                                 Binder.restoreCallingIdentity(identity);
                             }
                         }
-                    }, getOpPackageName(), getFeatureId(), workSource);
+                    }, getOpPackageName(), getAttributionTag(), workSource);
         } catch (RemoteException ex) {
         }
     }
@@ -7209,7 +7191,7 @@
             if (telephony == null)
                 return null;
             return telephony.getForbiddenPlmns(subId, appType, mContext.getOpPackageName(),
-                    getFeatureId());
+                    getAttributionTag());
         } catch (RemoteException ex) {
             return null;
         } catch (NullPointerException ex) {
@@ -7243,7 +7225,7 @@
             ITelephony telephony = getITelephony();
             if (telephony == null) return -1;
             return telephony.setForbiddenPlmns(
-                    getSubId(), APPTYPE_USIM, fplmns, getOpPackageName(), getFeatureId());
+                    getSubId(), APPTYPE_USIM, fplmns, getOpPackageName(), getAttributionTag());
         } catch (RemoteException ex) {
             Rlog.e(TAG, "setForbiddenPlmns RemoteException: " + ex.getMessage());
         } catch (NullPointerException ex) {
@@ -7264,7 +7246,7 @@
             ITelephony telephony = getITelephony();
             if (telephony == null)
                 return new String[0];
-            return telephony.getPcscfAddress(apnType, getOpPackageName(), getFeatureId());
+            return telephony.getPcscfAddress(apnType, getOpPackageName(), getAttributionTag());
         } catch (RemoteException e) {
             return new String[0];
         }
@@ -7837,7 +7819,7 @@
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 return telephony.getCellNetworkScanResults(getSubId(), getOpPackageName(),
-                        getFeatureId());
+                        getAttributionTag());
             }
         } catch (RemoteException ex) {
             Rlog.e(TAG, "getAvailableNetworks RemoteException", ex);
@@ -7892,7 +7874,7 @@
             }
         }
         return mTelephonyScanManager.requestNetworkScan(getSubId(), request, executor, callback,
-                getOpPackageName(), getFeatureId());
+                getOpPackageName(), getAttributionTag());
     }
 
     /**
@@ -8646,7 +8628,7 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null)
-                return telephony.isRadioOnWithFeature(getOpPackageName(), getFeatureId());
+                return telephony.isRadioOnWithFeature(getOpPackageName(), getAttributionTag());
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#isRadioOn", e);
         }
@@ -8725,7 +8707,6 @@
      *
      * @hide
      */
-    @SystemApi
     @Nullable
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public PinResult supplyPinReportPinResult(@NonNull String pin) {
@@ -8750,7 +8731,6 @@
      *
      * @hide
      */
-    @SystemApi
     @Nullable
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public PinResult supplyPukReportPinResult(@NonNull String puk, @NonNull String pin) {
@@ -8942,7 +8922,10 @@
     }
 
     /**
-     * Shut down all the live radios over all the slot index.
+     * Shut down all the live radios over all the slot indexes.
+     *
+     * <p>To know when the radio has completed powering off, use
+     * {@link PhoneStateListener#LISTEN_SERVICE_STATE LISTEN_SERVICE_STATE}.
      *
      * @hide
      */
@@ -8955,7 +8938,8 @@
                 telephony.shutdownMobileRadios();
             }
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#shutdownMobileRadios", e);
+            Log.e(TAG, "Error calling ITelephony#shutdownAllRadios", e);
+            e.rethrowAsRuntimeException();
         }
     }
 
@@ -8974,7 +8958,8 @@
                 return telephony.needMobileRadioShutdown();
             }
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#needMobileRadioShutdown", e);
+            Log.e(TAG, "Error calling ITelephony#isAnyRadioPoweredOn", e);
+            e.rethrowAsRuntimeException();
         }
         return false;
     }
@@ -9017,7 +9002,7 @@
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 return telephony.getRadioPowerState(getSlotIndex(), mContext.getOpPackageName(),
-                        mContext.getFeatureId());
+                        mContext.getAttributionTag());
             }
         } catch (RemoteException ex) {
             // This could happen if binder process crashes.
@@ -9408,7 +9393,7 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null)
-                return telephony.isVideoCallingEnabled(getOpPackageName(), getFeatureId());
+                return telephony.isVideoCallingEnabled(getOpPackageName(), getAttributionTag());
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#isVideoCallingEnabled", e);
         }
@@ -9425,7 +9410,7 @@
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 return telephony.canChangeDtmfToneLength(mSubId, getOpPackageName(),
-                        getFeatureId());
+                        getAttributionTag());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#canChangeDtmfToneLength", e);
@@ -9444,7 +9429,7 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                return telephony.isWorldPhone(mSubId, getOpPackageName(), getFeatureId());
+                return telephony.isWorldPhone(mSubId, getOpPackageName(), getAttributionTag());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#isWorldPhone", e);
@@ -10172,7 +10157,8 @@
             ITelephony service = getITelephony();
             if (service != null) {
                 retval = service.getSubIdForPhoneAccountHandle(
-                        phoneAccountHandle, mContext.getOpPackageName(), mContext.getFeatureId());
+                        phoneAccountHandle, mContext.getOpPackageName(),
+                        mContext.getAttributionTag());
             }
         } catch (RemoteException ex) {
             Log.e(TAG, "getSubscriptionId RemoteException", ex);
@@ -10313,7 +10299,7 @@
             ITelephony service = getITelephony();
             if (service != null) {
                 return service.getServiceStateForSubscriber(subId, getOpPackageName(),
-                        getFeatureId());
+                        getAttributionTag());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#getServiceStateForSubscriber", e);
@@ -11034,7 +11020,8 @@
         try {
             ITelephony service = getITelephony();
             if (service != null) {
-                return service.getClientRequestStats(getOpPackageName(), getFeatureId(), subId);
+                return service.getClientRequestStats(getOpPackageName(), getAttributionTag(),
+                        subId);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#getClientRequestStats", e);
@@ -11145,21 +11132,21 @@
     }
 
     /**
-     * Checks whether cellular data connection is enabled in the device.
+     * Checks whether cellular data connection is allowed in the device.
      *
-     * Whether cellular data connection is enabled, meaning upon request whether will try to setup
-     * metered data connection considering all factors below:
-     * 1) User turned on data setting {@link #isDataEnabled}.
-     * 2) Carrier allows data to be on.
-     * 3) Network policy.
-     * And possibly others.
-     *
-     * @return {@code true} if the overall data connection is capable; {@code false} if not.
+     * <p>Whether cellular data connection is allowed considers all factors below:
+     * <UL>
+     *   <LI>User turned on data setting {@link #isDataEnabled}.</LI>
+     *   <LI>Carrier allows data to be on.</LI>
+     *   <LI>Network policy.</LI>
+     *   <LI>And possibly others.</LI>
+     * </UL>
+     * @return {@code true} if the overall data connection is allowed; {@code false} if not.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public boolean isDataConnectionEnabled() {
+    public boolean isDataConnectionAllowed() {
         boolean retVal = false;
         try {
             int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
@@ -11167,8 +11154,9 @@
             if (telephony != null)
                 retVal = telephony.isDataEnabled(subId);
         } catch (RemoteException e) {
-            Log.e(TAG, "Error isDataConnectionEnabled", e);
+            Log.e(TAG, "Error isDataConnectionAllowed", e);
         } catch (NullPointerException e) {
+            return false;
         }
         return retVal;
     }
@@ -11319,7 +11307,7 @@
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 return telephony.getNumberOfModemsWithSimultaneousDataConnections(
-                        getSubId(), getOpPackageName(), getFeatureId());
+                        getSubId(), getOpPackageName(), getAttributionTag());
             }
         } catch (RemoteException ex) {
             // This could happen if binder process crashes.
@@ -11749,7 +11737,7 @@
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 return telephony.getEmergencyNumberList(mContext.getOpPackageName(),
-                        mContext.getFeatureId());
+                        mContext.getAttributionTag());
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
@@ -11804,7 +11792,7 @@
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 emergencyNumberList = telephony.getEmergencyNumberList(
-                        mContext.getOpPackageName(), mContext.getFeatureId());
+                        mContext.getOpPackageName(), mContext.getAttributionTag());
                 if (emergencyNumberList != null) {
                     for (Integer subscriptionId : emergencyNumberList.keySet()) {
                         List<EmergencyNumber> numberList = emergencyNumberList.get(subscriptionId);
@@ -11897,7 +11885,7 @@
     }
 
     /**
-     * A test API to return the emergency number db version.
+     * Returns the emergency number database version.
      *
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
@@ -11906,6 +11894,7 @@
      */
     @TestApi
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public int getEmergencyNumberDbVersion() {
         try {
             ITelephony telephony = getITelephony();
@@ -12112,13 +12101,13 @@
     })
     public int getPreferredOpportunisticDataSubscription() {
         String packageName = mContext != null ? mContext.getOpPackageName() : "<unknown>";
-        String featureId = mContext != null ? mContext.getFeatureId() : null;
+        String attributionTag = mContext != null ? mContext.getAttributionTag() : null;
         int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
         try {
             IOns iOpportunisticNetworkService = getIOns();
             if (iOpportunisticNetworkService != null) {
                 subId = iOpportunisticNetworkService.getPreferredDataSubscriptionId(
-                        packageName, featureId);
+                        packageName, attributionTag);
             }
         } catch (RemoteException ex) {
             Rlog.e(TAG, "getPreferredDataSubscriptionId RemoteException", ex);
@@ -12235,18 +12224,20 @@
 
     /**
      * It indicates whether modem is enabled or not per slot.
-     * It's the corresponding status of {@link #enableModemForSlot}.
+     * It's the corresponding status of TelephonyManager.enableModemForSlot.
      *
+     * <p>Requires Permission:
+     * READ_PRIVILEGED_PHONE_STATE or
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      * @param slotIndex which slot it's checking.
-     * @hide
      */
-    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public boolean isModemEnabledForSlot(int slotIndex) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 return telephony.isModemEnabledForSlot(slotIndex, mContext.getOpPackageName(),
-                        mContext.getFeatureId());
+                        mContext.getAttributionTag());
             }
         } catch (RemoteException ex) {
             Log.e(TAG, "enableModem RemoteException", ex);
@@ -12351,7 +12342,7 @@
         try {
             ITelephony service = getITelephony();
             if (service != null) {
-                return service.isMultiSimSupported(getOpPackageName(), getFeatureId());
+                return service.isMultiSimSupported(getOpPackageName(), getAttributionTag());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "isMultiSimSupported RemoteException", e);
@@ -12403,7 +12394,7 @@
             ITelephony service = getITelephony();
             if (service != null) {
                 return service.doesSwitchMultiSimConfigTriggerReboot(getSubId(),
-                        getOpPackageName(), getFeatureId());
+                        getOpPackageName(), getAttributionTag());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "doesSwitchMultiSimConfigTriggerReboot RemoteException", e);
@@ -12472,7 +12463,6 @@
      * @throws {@link SecurityException} if the caller is not the system or phone process.
      * @hide
      */
-    @SystemApi
     @TestApi
     // TODO: add new permission tag indicating that this is system-only.
     public @NonNull List<ApnSetting> getDevicePolicyOverrideApns(@NonNull Context context) {
@@ -12503,7 +12493,6 @@
      * @throws {@link SecurityException} if the caller is not the system or phone process.
      * @hide
      */
-    @SystemApi
     @TestApi
     // TODO: add new permission tag indicating that this is system-only.
     public int addDevicePolicyOverrideApn(@NonNull Context context,
@@ -12534,7 +12523,6 @@
      * @throws {@link SecurityException} if the caller is not the system or phone process.
      * @hide
      */
-    @SystemApi
     @TestApi
     // TODO: add new permission tag indicating that this is system-only.
     public boolean modifyDevicePolicyOverrideApn(@NonNull Context context, int apnId,
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index f5dfacc6..bfb54b0 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -18,7 +18,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.hardware.radio.V1_5.ApnTypes;
@@ -27,7 +26,6 @@
 import android.os.Parcelable;
 import android.provider.Telephony;
 import android.provider.Telephony.Carriers;
-import android.telephony.Annotation;
 import android.telephony.Annotation.ApnType;
 import android.telephony.Annotation.NetworkType;
 import android.telephony.ServiceState;
@@ -126,6 +124,15 @@
     /** Authentication type for PAP or CHAP. */
     public static final int AUTH_TYPE_PAP_OR_CHAP = 3;
 
+    /** @hide */
+    @IntDef({
+            Telephony.Carriers.SKIP_464XLAT_DEFAULT,
+            Telephony.Carriers.SKIP_464XLAT_DISABLE,
+            Telephony.Carriers.SKIP_464XLAT_ENABLE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Skip464XlatStatus {}
+
     /**
      * APN types for data connections.  These are usage categories for an APN
      * entry.  One APN entry may support multiple APN types, eg, a single APN
@@ -139,7 +146,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_ALL_STRING = "*";
 
     /**
@@ -147,7 +153,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_DEFAULT_STRING = "default";
 
 
@@ -156,7 +161,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_MMS_STRING = "mms";
 
 
@@ -165,7 +169,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_SUPL_STRING = "supl";
 
     /**
@@ -173,7 +176,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_DUN_STRING = "dun";
 
     /**
@@ -181,7 +183,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_HIPRI_STRING = "hipri";
 
     /**
@@ -189,7 +190,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_FOTA_STRING = "fota";
 
     /**
@@ -197,7 +197,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_IMS_STRING = "ims";
 
     /**
@@ -205,7 +204,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_CBS_STRING = "cbs";
 
     /**
@@ -213,7 +211,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_IA_STRING = "ia";
 
     /**
@@ -222,7 +219,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_EMERGENCY_STRING = "emergency";
 
     /**
@@ -230,7 +226,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_MCX_STRING = "mcx";
 
     /**
@@ -238,7 +233,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_XCAP_STRING = "xcap";
 
 
@@ -745,7 +739,7 @@
      * @return SKIP_464XLAT_DEFAULT, SKIP_464XLAT_DISABLE or SKIP_464XLAT_ENABLE
      * @hide
      */
-    @Annotation.Skip464XlatStatus
+    @Skip464XlatStatus
     public int getSkip464Xlat() {
         return mSkip464Xlat;
     }
@@ -1416,7 +1410,6 @@
      * @return comma delimited list of APN types.
      * @hide
      */
-    @SystemApi
     @NonNull
     public static String getApnTypesStringFromBitmask(int apnTypeBitmask) {
         List<String> types = new ArrayList<>();
@@ -2065,7 +2058,7 @@
          * @param skip464xlat skip464xlat for this APN.
          * @hide
          */
-        public Builder setSkip464Xlat(@Annotation.Skip464XlatStatus int skip464xlat) {
+        public Builder setSkip464Xlat(@Skip464XlatStatus int skip464xlat) {
             this.mSkip464Xlat = skip464xlat;
             return this;
         }
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 3341fa7..4b5303f 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -294,8 +294,15 @@
             throw new IllegalArgumentException("Must include a non-null Executor.");
         }
         c.setExecutor(executor);
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new ImsException("Could not find Telephony Service.",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
         try {
-            getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder());
+            iTelephony.registerImsRegistrationCallback(mSubId, c.getBinder());
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -331,8 +338,15 @@
             throw new IllegalArgumentException("Must include a non-null Executor.");
         }
         c.setExecutor(executor);
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new ImsException("Could not find Telephony Service.",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
         try {
-            getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder());
+            iTelephony.registerImsRegistrationCallback(mSubId, c.getBinder());
         } catch (ServiceSpecificException e) {
             throw new ImsException(e.getMessage(), e.errorCode);
         } catch (RemoteException | IllegalStateException e) {
@@ -361,8 +375,14 @@
         if (c == null) {
             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
         }
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().unregisterImsRegistrationCallback(mSubId, c.getBinder());
+            iTelephony.unregisterImsRegistrationCallback(mSubId, c.getBinder());
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -387,8 +407,14 @@
         if (c == null) {
             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
         }
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().unregisterImsRegistrationCallback(mSubId, c.getBinder());
+            iTelephony.unregisterImsRegistrationCallback(mSubId, c.getBinder());
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -409,8 +435,14 @@
         if (executor == null) {
             throw new IllegalArgumentException("Must include a non-null Executor.");
         }
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().getImsMmTelRegistrationState(mSubId, new IIntegerConsumer.Stub() {
+            iTelephony.getImsMmTelRegistrationState(mSubId, new IIntegerConsumer.Stub() {
                 @Override
                 public void accept(int result) {
                     executor.execute(() -> stateCallback.accept(result));
@@ -443,8 +475,14 @@
         if (executor == null) {
             throw new IllegalArgumentException("Must include a non-null Executor.");
         }
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().getImsMmTelRegistrationTransportType(mSubId,
+            iTelephony.getImsMmTelRegistrationTransportType(mSubId,
                     new IIntegerConsumer.Stub() {
                         @Override
                         public void accept(int result) {
@@ -506,8 +544,15 @@
             throw new IllegalArgumentException("Must include a non-null Executor.");
         }
         c.setExecutor(executor);
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new ImsException("Could not find Telephony Service.",
+                    ImsException.CODE_ERROR_INVALID_SUBSCRIPTION);
+        }
+
         try {
-            getITelephony().registerMmTelCapabilityCallback(mSubId, c.getBinder());
+            iTelephony.registerMmTelCapabilityCallback(mSubId, c.getBinder());
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -553,8 +598,14 @@
         if (c == null) {
             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
         }
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().unregisterMmTelCapabilityCallback(mSubId, c.getBinder());
+            iTelephony.unregisterMmTelCapabilityCallback(mSubId, c.getBinder());
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -599,8 +650,13 @@
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             android.Manifest.permission.READ_PRECISE_PHONE_STATE})
     public boolean isAdvancedCallingSettingEnabled() {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            return getITelephony().isAdvancedCallingSettingEnabled(mSubId);
+            return iTelephony.isAdvancedCallingSettingEnabled(mSubId);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -640,8 +696,13 @@
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     @SystemApi @TestApi
     public void setAdvancedCallingSettingEnabled(boolean isEnabled) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().setAdvancedCallingSettingEnabled(mSubId, isEnabled);
+            iTelephony.setAdvancedCallingSettingEnabled(mSubId, isEnabled);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -680,8 +741,13 @@
     @SystemApi @TestApi
     public boolean isCapable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            return getITelephony().isCapable(mSubId, capability, imsRegTech);
+            return iTelephony.isCapable(mSubId, capability, imsRegTech);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -709,8 +775,13 @@
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isAvailable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            return getITelephony().isAvailable(mSubId, capability, imsRegTech);
+            return iTelephony.isAvailable(mSubId, capability, imsRegTech);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -744,6 +815,13 @@
         if (executor == null) {
             throw new IllegalArgumentException("Must include a non-null Executor.");
         }
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new ImsException("Could not find Telephony Service.",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
         try {
             getITelephony().isMmTelCapabilitySupported(mSubId, new IIntegerConsumer.Stub() {
                 @Override
@@ -788,8 +866,13 @@
             android.Manifest.permission.READ_PRECISE_PHONE_STATE})
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     public boolean isVtSettingEnabled() {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            return getITelephony().isVtSettingEnabled(mSubId);
+            return iTelephony.isVtSettingEnabled(mSubId);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -813,8 +896,13 @@
     @SystemApi @TestApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVtSettingEnabled(boolean isEnabled) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().setVtSettingEnabled(mSubId, isEnabled);
+            iTelephony.setVtSettingEnabled(mSubId, isEnabled);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -853,8 +941,13 @@
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             android.Manifest.permission.READ_PRECISE_PHONE_STATE})
     public boolean isVoWiFiSettingEnabled() {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            return getITelephony().isVoWiFiSettingEnabled(mSubId);
+            return iTelephony.isVoWiFiSettingEnabled(mSubId);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -879,8 +972,13 @@
     @SystemApi @TestApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiSettingEnabled(boolean isEnabled) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().setVoWiFiSettingEnabled(mSubId, isEnabled);
+            iTelephony.setVoWiFiSettingEnabled(mSubId, isEnabled);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -921,8 +1019,13 @@
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             android.Manifest.permission.READ_PRECISE_PHONE_STATE})
     public boolean isVoWiFiRoamingSettingEnabled() {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            return getITelephony().isVoWiFiRoamingSettingEnabled(mSubId);
+            return iTelephony.isVoWiFiRoamingSettingEnabled(mSubId);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -948,8 +1051,13 @@
     @SystemApi @TestApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiRoamingSettingEnabled(boolean isEnabled) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().setVoWiFiRoamingSettingEnabled(mSubId, isEnabled);
+            iTelephony.setVoWiFiRoamingSettingEnabled(mSubId, isEnabled);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -980,8 +1088,13 @@
     @SystemApi @TestApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiNonPersistent(boolean isCapable, int mode) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().setVoWiFiNonPersistent(mSubId, isCapable, mode);
+            iTelephony.setVoWiFiNonPersistent(mSubId, isCapable, mode);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -1025,8 +1138,13 @@
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             android.Manifest.permission.READ_PRECISE_PHONE_STATE})
     public @WiFiCallingMode int getVoWiFiModeSetting() {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            return getITelephony().getVoWiFiModeSetting(mSubId);
+            return iTelephony.getVoWiFiModeSetting(mSubId);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -1054,8 +1172,13 @@
     @SystemApi @TestApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiModeSetting(@WiFiCallingMode int mode) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().setVoWiFiModeSetting(mSubId, mode);
+            iTelephony.setVoWiFiModeSetting(mSubId, mode);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -1085,8 +1208,13 @@
     @SystemApi @TestApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public @WiFiCallingMode int getVoWiFiRoamingModeSetting() {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            return getITelephony().getVoWiFiRoamingModeSetting(mSubId);
+            return iTelephony.getVoWiFiRoamingModeSetting(mSubId);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -1116,8 +1244,13 @@
     @SystemApi @TestApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().setVoWiFiRoamingModeSetting(mSubId, mode);
+            iTelephony.setVoWiFiRoamingModeSetting(mSubId, mode);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -1145,8 +1278,13 @@
     @SystemApi @TestApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setRttCapabilitySetting(boolean isEnabled) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().setRttCapabilitySetting(mSubId, isEnabled);
+            iTelephony.setRttCapabilitySetting(mSubId, isEnabled);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -1186,8 +1324,13 @@
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             android.Manifest.permission.READ_PRECISE_PHONE_STATE})
     public boolean isTtyOverVolteEnabled() {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            return getITelephony().isTtyOverVolteEnabled(mSubId);
+            return iTelephony.isTtyOverVolteEnabled(mSubId);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -1223,8 +1366,15 @@
         if (callback == null) {
             throw new IllegalArgumentException("Must include a non-null Consumer.");
         }
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new ImsException("Could not find Telephony Service.",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
         try {
-            getITelephony().getImsMmTelFeatureState(mSubId, new IIntegerConsumer.Stub() {
+            iTelephony.getImsMmTelFeatureState(mSubId, new IIntegerConsumer.Stub() {
                 @Override
                 public void accept(int result) {
                     executor.execute(() -> callback.accept(result));
@@ -1243,9 +1393,6 @@
                         .getTelephonyServiceManager()
                         .getTelephonyServiceRegisterer()
                         .get());
-        if (binder == null) {
-            throw new RuntimeException("Could not find Telephony Service.");
-        }
         return binder;
     }
 }
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 30306c7..05ab6bd 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -291,7 +291,7 @@
 
         try {
             imsRcsController.requestCapabilities(mSubId, mContext.getOpPackageName(),
-                    mContext.getFeatureId(), contactNumbers, internalCallback);
+                    mContext.getAttributionTag(), contactNumbers, internalCallback);
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling IImsRcsController#requestCapabilities", e);
             throw new ImsException("Remote IMS Service is not available",
@@ -352,7 +352,7 @@
         try {
             // Telephony.SimInfo#IMS_RCS_UCE_ENABLED can also be used to listen to changes to this.
             return imsRcsController.isUceSettingEnabled(mSubId, mContext.getOpPackageName(),
-                    mContext.getFeatureId());
+                    mContext.getAttributionTag());
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling IImsRcsController#isUceSettingEnabled", e);
             throw new ImsException("Remote IMS Service is not available",
diff --git a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
index 5904916..c7e5a5e 100644
--- a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
+++ b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
@@ -322,7 +322,7 @@
 
                 mAtm.startActivityAndWait(null,
                         getInstrumentation().getContext().getBasePackageName(),
-                        getInstrumentation().getContext().getFeatureId(), mLaunchIntent,
+                        getInstrumentation().getContext().getAttributionTag(), mLaunchIntent,
                         mimeType, null, null, 0, mLaunchIntent.getFlags(), null, null,
                         UserHandle.USER_CURRENT_OR_SELF);
             } catch (RemoteException e) {
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 819fc02..8cc8cf4 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -1063,52 +1063,6 @@
         assertThat(bootObserver2.mitigatedBootLoop()).isFalse();
     }
 
-    /**
-     * Test to verify that Package Watchdog syncs health check requests with the controller
-     * correctly, and that the requests are only synced when the set of observed packages
-     * changes.
-     */
-    @Test
-    public void testSyncHealthCheckRequests() {
-        TestController testController = spy(TestController.class);
-        testController.setSupportedPackages(List.of(APP_A, APP_B, APP_C));
-        PackageWatchdog watchdog = createWatchdog(testController, true);
-
-        TestObserver testObserver1 = new TestObserver(OBSERVER_NAME_1);
-        watchdog.registerHealthObserver(testObserver1);
-        watchdog.startObservingHealth(testObserver1, List.of(APP_A), LONG_DURATION);
-        mTestLooper.dispatchAll();
-
-        TestObserver testObserver2 = new TestObserver(OBSERVER_NAME_2);
-        watchdog.registerHealthObserver(testObserver2);
-        watchdog.startObservingHealth(testObserver2, List.of(APP_B), LONG_DURATION);
-        mTestLooper.dispatchAll();
-
-        TestObserver testObserver3 = new TestObserver(OBSERVER_NAME_3);
-        watchdog.registerHealthObserver(testObserver3);
-        watchdog.startObservingHealth(testObserver3, List.of(APP_C), LONG_DURATION);
-        mTestLooper.dispatchAll();
-
-        watchdog.unregisterHealthObserver(testObserver1);
-        mTestLooper.dispatchAll();
-
-        watchdog.unregisterHealthObserver(testObserver2);
-        mTestLooper.dispatchAll();
-
-        watchdog.unregisterHealthObserver(testObserver3);
-        mTestLooper.dispatchAll();
-
-        List<Set> expectedSyncRequests = List.of(
-                Set.of(APP_A),
-                Set.of(APP_A, APP_B),
-                Set.of(APP_A, APP_B, APP_C),
-                Set.of(APP_B, APP_C),
-                Set.of(APP_C),
-                Set.of()
-        );
-        assertThat(testController.getSyncRequests()).isEqualTo(expectedSyncRequests);
-    }
-
     private void adoptShellPermissions(String... permissions) {
         InstrumentationRegistry
                 .getInstrumentation()
@@ -1265,7 +1219,6 @@
         private Consumer<String> mPassedConsumer;
         private Consumer<List<PackageConfig>> mSupportedConsumer;
         private Runnable mNotifySyncRunnable;
-        private List<Set> mSyncRequests = new ArrayList<>();
 
         @Override
         public void setEnabled(boolean enabled) {
@@ -1285,7 +1238,6 @@
 
         @Override
         public void syncRequests(Set<String> packages) {
-            mSyncRequests.add(packages);
             mRequestedPackages.clear();
             if (mIsEnabled) {
                 packages.retainAll(mSupportedPackages);
@@ -1316,10 +1268,6 @@
                 return Collections.emptyList();
             }
         }
-
-        public List<Set> getSyncRequests() {
-            return mSyncRequests;
-        }
     }
 
     private static class TestClock implements PackageWatchdog.SystemClock {
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 8e6f198..e415170 100644
--- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
@@ -16,11 +16,15 @@
 
 package com.google.android.test.windowinsetstests;
 
+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;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -37,6 +41,8 @@
 import android.view.WindowInsetsAnimation.Callback;
 import android.view.WindowInsetsAnimationControlListener;
 import android.view.WindowInsetsAnimationController;
+import android.view.WindowInsetsController;
+import android.view.WindowInsetsController.OnControllableInsetsChangedListener;
 import android.view.animation.LinearInterpolator;
 import android.widget.LinearLayout;
 
@@ -82,8 +88,8 @@
                 switch (event.getAction()) {
                     case MotionEvent.ACTION_DOWN:
                         mDown = event.getY();
-                        mDownInsets = v.getRootWindowInsets().getInsets(Type.ime());
-                        mShownAtDown = v.getRootWindowInsets().isVisible(Type.ime());
+                        mDownInsets = v.getRootWindowInsets().getInsets(ime());
+                        mShownAtDown = v.getRootWindowInsets().isVisible(ime());
                         mRequestedController = false;
                         mCurrentRequest = null;
                         break;
@@ -94,7 +100,7 @@
                                 > mViewConfiguration.getScaledTouchSlop()
                                 && !mRequestedController) {
                             mRequestedController = true;
-                            v.getWindowInsetsController().controlWindowInsetsAnimation(Type.ime(),
+                            v.getWindowInsetsController().controlWindowInsetsAnimation(ime(),
                                     1000, new LinearInterpolator(),
                                     mCurrentRequest = new WindowInsetsAnimationControlListener() {
                                         @Override
@@ -189,6 +195,51 @@
         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(),
+                                    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() {
+                                        }
+                                    });
+                        }
+                    }
+                });
+    }
+
     static class Transition {
         private int mEndBottom;
         private int mStartBottom;
@@ -200,7 +251,7 @@
         }
 
         void onPrepare(WindowInsetsAnimation animation) {
-            if ((animation.getTypeMask() & Type.ime()) != 0) {
+            if ((animation.getTypeMask() & ime()) != 0) {
                 mInsetsAnimation = animation;
             }
             mStartBottom = mView.getBottom();
diff --git a/tests/net/common/java/android/net/RouteInfoTest.java b/tests/net/common/java/android/net/RouteInfoTest.java
index fe51b3a..1658262 100644
--- a/tests/net/common/java/android/net/RouteInfoTest.java
+++ b/tests/net/common/java/android/net/RouteInfoTest.java
@@ -19,19 +19,40 @@
 import static android.net.RouteInfo.RTN_UNREACHABLE;
 
 import static com.android.testutils.MiscAssertsKt.assertEqualBothWays;
+import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals;
 import static com.android.testutils.MiscAssertsKt.assertNotEqualEitherWay;
-import static com.android.testutils.ParcelUtilsKt.assertParcelSane;
 import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless;
 
-import android.test.suitebuilder.annotation.SmallTest;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
-import junit.framework.TestCase;
+import android.os.Build;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
 
-public class RouteInfoTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class RouteInfoTest {
+    @Rule
+    public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
+
+    private static final int INVALID_ROUTE_TYPE = -1;
 
     private InetAddress Address(String addr) {
         return InetAddress.parseNumericAddress(addr);
@@ -41,15 +62,32 @@
         return new IpPrefix(prefix);
     }
 
-    @SmallTest
+    @Test
     public void testConstructor() {
         RouteInfo r;
-
         // Invalid input.
         try {
             r = new RouteInfo((IpPrefix) null, null, "rmnet0");
             fail("Expected RuntimeException:  destination and gateway null");
-        } catch(RuntimeException e) {}
+        } catch (RuntimeException e) { }
+
+        try {
+            r = new RouteInfo(Prefix("2001:db8:ace::/49"), Address("2001:db8::1"), "rmnet0",
+                    INVALID_ROUTE_TYPE);
+            fail("Invalid route type should cause exception");
+        } catch (IllegalArgumentException e) { }
+
+        try {
+            r = new RouteInfo(Prefix("2001:db8:ace::/49"), Address("192.0.2.1"), "rmnet0",
+                    RTN_UNREACHABLE);
+            fail("Address family mismatch should cause exception");
+        } catch (IllegalArgumentException e) { }
+
+        try {
+            r = new RouteInfo(Prefix("0.0.0.0/0"), Address("2001:db8::1"), "rmnet0",
+                    RTN_UNREACHABLE);
+            fail("Address family mismatch should cause exception");
+        } catch (IllegalArgumentException e) { }
 
         // Null destination is default route.
         r = new RouteInfo((IpPrefix) null, Address("2001:db8::1"), null);
@@ -74,6 +112,7 @@
         assertNull(r.getInterface());
     }
 
+    @Test
     public void testMatches() {
         class PatchedRouteInfo {
             private final RouteInfo mRouteInfo;
@@ -113,6 +152,7 @@
         assertFalse(ipv4Default.matches(Address("2001:db8::f00")));
     }
 
+    @Test
     public void testEquals() {
         // IPv4
         RouteInfo r1 = new RouteInfo(Prefix("2001:db8:ace::/48"), Address("2001:db8::1"), "wlan0");
@@ -146,6 +186,7 @@
         assertNotEqualEitherWay(r1, r3);
     }
 
+    @Test
     public void testHostAndDefaultRoutes() {
         RouteInfo r;
 
@@ -228,6 +269,7 @@
         assertFalse(r.isIPv6Default());
     }
 
+    @Test
     public void testTruncation() {
       LinkAddress l;
       RouteInfo r;
@@ -244,6 +286,7 @@
     // Make sure that creating routes to multicast addresses doesn't throw an exception. Even though
     // there's nothing we can do with them, we don't want to crash if, e.g., someone calls
     // requestRouteToHostAddress("230.0.0.0", MOBILE_HIPRI);
+    @Test
     public void testMulticastRoute() {
       RouteInfo r;
       r = new RouteInfo(Prefix("230.0.0.0/32"), Address("192.0.2.1"), "wlan0");
@@ -251,16 +294,36 @@
       // No exceptions? Good.
     }
 
+    @Test
     public void testParceling() {
         RouteInfo r;
-
-        r = new RouteInfo(Prefix("::/0"), Address("2001:db8::"), null);
+        r = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), null);
         assertParcelingIsLossless(r);
-
         r = new RouteInfo(Prefix("192.0.2.0/24"), null, "wlan0");
-        assertParcelSane(r, 7);
+        assertParcelingIsLossless(r);
+        r = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), "wlan0", RTN_UNREACHABLE);
+        assertParcelingIsLossless(r);
     }
 
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testMtuParceling() {
+        final RouteInfo r = new RouteInfo(Prefix("ff02::1/128"), Address("2001:db8::"), "testiface",
+                RTN_UNREACHABLE, 1450 /* mtu */);
+        assertParcelingIsLossless(r);
+    }
+
+    @Test @IgnoreAfter(Build.VERSION_CODES.Q)
+    public void testFieldCount_Q() {
+        assertFieldCountEquals(6, RouteInfo.class);
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testFieldCount() {
+        // Make sure any new field is covered by the above parceling tests when changing this number
+        assertFieldCountEquals(7, RouteInfo.class);
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
     public void testMtu() {
         RouteInfo r;
         r = new RouteInfo(Prefix("0.0.0.0/0"), Address("0.0.0.0"), "wlan0",
diff --git a/tests/net/common/java/android/net/util/SocketUtilsTest.kt b/tests/net/common/java/android/net/util/SocketUtilsTest.kt
index 9c7cfb0..aaf97f3 100644
--- a/tests/net/common/java/android/net/util/SocketUtilsTest.kt
+++ b/tests/net/common/java/android/net/util/SocketUtilsTest.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package android.net.util;
+package android.net.util
 
+import android.os.Build
 import android.system.NetlinkSocketAddress
 import android.system.Os
 import android.system.OsConstants.AF_INET
@@ -26,18 +27,26 @@
 import android.system.PacketSocketAddress
 import androidx.test.filters.SmallTest
 import androidx.test.runner.AndroidJUnit4
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Assert.fail
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 
 private const val TEST_INDEX = 123
 private const val TEST_PORT = 555
+private const val FF_BYTE = 0xff.toByte()
+
 @RunWith(AndroidJUnit4::class)
 @SmallTest
 class SocketUtilsTest {
+    @Rule @JvmField
+    val ignoreRule = DevSdkIgnoreRule()
+
     @Test
     fun testMakeNetlinkSocketAddress() {
         val nlAddress = SocketUtils.makeNetlinkSocketAddress(TEST_PORT, RTMGRP_NEIGH)
@@ -50,16 +59,21 @@
     }
 
     @Test
-    fun testMakePacketSocketAddress() {
+    fun testMakePacketSocketAddress_Q() {
         val pkAddress = SocketUtils.makePacketSocketAddress(ETH_P_ALL, TEST_INDEX)
         assertTrue("Not PacketSocketAddress object", pkAddress is PacketSocketAddress)
 
-        val ff = 0xff.toByte()
-        val pkAddress2 = SocketUtils.makePacketSocketAddress(TEST_INDEX,
-                byteArrayOf(ff, ff, ff, ff, ff, ff))
+        val pkAddress2 = SocketUtils.makePacketSocketAddress(TEST_INDEX, ByteArray(6) { FF_BYTE })
         assertTrue("Not PacketSocketAddress object", pkAddress2 is PacketSocketAddress)
     }
 
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    fun testMakePacketSocketAddress() {
+        val pkAddress = SocketUtils.makePacketSocketAddress(
+                ETH_P_ALL, TEST_INDEX, ByteArray(6) { FF_BYTE })
+        assertTrue("Not PacketSocketAddress object", pkAddress is PacketSocketAddress)
+    }
+
     @Test
     fun testCloseSocket() {
         // Expect no exception happening with null object.
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 8c0c36b..6985415 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -23,8 +23,6 @@
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
-import static android.net.ConnectivityDiagnosticsManager.DataStallReport;
 import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_SUPL;
@@ -100,6 +98,7 @@
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.startsWith;
 import static org.mockito.Matchers.anyInt;
@@ -6870,8 +6869,13 @@
         HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
 
         // Verify onConnectivityReport fired
-        verify(mConnectivityDiagnosticsCallback)
-                .onConnectivityReport(any(ConnectivityReport.class));
+        verify(mConnectivityDiagnosticsCallback).onConnectivityReport(
+                argThat(report -> {
+                    final NetworkCapabilities nc = report.getNetworkCapabilities();
+                    return nc.getUids() == null
+                            && nc.getAdministratorUids().isEmpty()
+                            && nc.getOwnerUid() == Process.INVALID_UID;
+                }));
     }
 
     @Test
@@ -6886,7 +6890,13 @@
         HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
 
         // Verify onDataStallSuspected fired
-        verify(mConnectivityDiagnosticsCallback).onDataStallSuspected(any(DataStallReport.class));
+        verify(mConnectivityDiagnosticsCallback).onDataStallSuspected(
+                argThat(report -> {
+                    final NetworkCapabilities nc = report.getNetworkCapabilities();
+                    return nc.getUids() == null
+                            && nc.getAdministratorUids().isEmpty()
+                            && nc.getOwnerUid() == Process.INVALID_UID;
+                }));
     }
 
     @Test
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index a9e0b9a..36deca3 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -64,6 +64,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.annotation.NonNull;
 import android.app.AlarmManager;
 import android.app.usage.NetworkStatsManager;
 import android.content.Context;
@@ -163,7 +164,6 @@
     private @Mock IBinder mBinder;
     private @Mock AlarmManager mAlarmManager;
     private HandlerThread mHandlerThread;
-    private Handler mHandler;
 
     private NetworkStatsService mService;
     private INetworkStatsSession mSession;
@@ -192,15 +192,11 @@
         PowerManager.WakeLock wakeLock =
                 powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
 
-        mService = new NetworkStatsService(
-                mServiceContext, mNetManager, mAlarmManager, wakeLock, mClock,
-                mServiceContext.getSystemService(TelephonyManager.class), mSettings,
-                mStatsFactory, new NetworkStatsObservers(),  mStatsDir, getBaseDir(mStatsDir));
         mHandlerThread = new HandlerThread("HandlerThread");
-        mHandlerThread.start();
-        Handler.Callback callback = new NetworkStatsService.HandlerCallback(mService);
-        mHandler = new Handler(mHandlerThread.getLooper(), callback);
-        mService.setHandler(mHandler, callback);
+        final NetworkStatsService.Dependencies deps = makeDependencies();
+        mService = new NetworkStatsService(mServiceContext, mNetManager, mAlarmManager, wakeLock,
+                mClock, mServiceContext.getSystemService(TelephonyManager.class), mSettings,
+                mStatsFactory, new NetworkStatsObservers(), mStatsDir, getBaseDir(mStatsDir), deps);
 
         mElapsedRealtime = 0L;
 
@@ -217,11 +213,21 @@
 
         // catch INetworkManagementEventObserver during systemReady()
         ArgumentCaptor<INetworkManagementEventObserver> networkObserver =
-              ArgumentCaptor.forClass(INetworkManagementEventObserver.class);
+                ArgumentCaptor.forClass(INetworkManagementEventObserver.class);
         verify(mNetManager).registerObserver(networkObserver.capture());
         mNetworkObserver = networkObserver.getValue();
     }
 
+    @NonNull
+    private NetworkStatsService.Dependencies makeDependencies() {
+        return new NetworkStatsService.Dependencies() {
+            @Override
+            public HandlerThread makeHandlerThread() {
+                return mHandlerThread;
+            }
+        };
+    }
+
     @After
     public void tearDown() throws Exception {
         IoUtils.deleteContents(mStatsDir);
@@ -234,6 +240,8 @@
 
         mSession.close();
         mService = null;
+
+        mHandlerThread.quitSafely();
     }
 
     @Test
@@ -939,9 +947,7 @@
         long minThresholdInBytes = 2 * 1024 * 1024; // 2 MB
         assertEquals(minThresholdInBytes, request.thresholdInBytes);
 
-        // Send dummy message to make sure that any previous message has been handled
-        mHandler.sendMessage(mHandler.obtainMessage(-1));
-        HandlerUtilsKt.waitForIdle(mHandler, WAIT_TIMEOUT);
+        HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT);
 
         // Make sure that the caller binder gets connected
         verify(mBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt());
@@ -1077,7 +1083,7 @@
 
         // Simulates alert quota of the provider has been reached.
         cb.onAlertReached();
-        HandlerUtilsKt.waitForIdle(mHandler, WAIT_TIMEOUT);
+        HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT);
 
         // Verifies that polling is triggered by alert reached.
         provider.expectStatsUpdate(0 /* unused */);
@@ -1294,9 +1300,7 @@
 
     private void forcePollAndWaitForIdle() {
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
-        // Send dummy message to make sure that any previous message has been handled
-        mHandler.sendMessage(mHandler.obtainMessage(-1));
-        HandlerUtilsKt.waitForIdle(mHandler, WAIT_TIMEOUT);
+        HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT);
     }
 
     static class LatchedHandler extends Handler {
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 5aa32f8..bcfce66 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -366,8 +366,12 @@
   });
   manifest_action["instrumentation"]["meta-data"] = meta_data_action;
 
+  // TODO moltmann: Remove
   manifest_action["feature"];
   manifest_action["feature"]["inherit-from"];
+
+  manifest_action["attribution"];
+  manifest_action["attribution"]["inherit-from"];
   manifest_action["original-package"];
   manifest_action["overlay"];
   manifest_action["protected-broadcast"];
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index cbf6fe8..b1e2487 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -120,12 +120,6 @@
         "liblog",
         "libcutils",
     ],
-    apex_available: [
-        "//apex_available:platform",
-        //TODO(b/149781190): Remove this once statsd no longer depends on libstatslog
-        "com.android.os.statsd",
-        "test_com.android.os.statsd",
-    ],
     target: {
         android: {
             shared_libs: ["libstatssocket"],
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 91174d3..f4d2881 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -177,14 +177,14 @@
 java_library {
     name: "framework-wifi-stubs-publicapi",
     srcs: [":framework-wifi-stubs-srcs-publicapi"],
-    sdk_version: "module_current",
+    sdk_version: "current",
     installable: false,
 }
 
 java_library {
     name: "framework-wifi-stubs-systemapi",
     srcs: [":framework-wifi-stubs-srcs-systemapi"],
-    sdk_version: "module_current",
+    sdk_version: "system_current",
     libs: ["framework-annotations-lib"],
     installable: false,
 }
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index 2b47623..a269e17 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -35,7 +35,6 @@
 import java.nio.charset.CharsetEncoder;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
 
@@ -211,7 +210,7 @@
      * Delay in milliseconds before shutting down soft AP when
      * there are no connected devices.
      */
-    private final int mShutdownTimeoutMillis;
+    private final long mShutdownTimeoutMillis;
 
     /**
      * THe definition of security type OPEN.
@@ -247,7 +246,7 @@
     private SoftApConfiguration(@Nullable String ssid, @Nullable MacAddress bssid,
             @Nullable String passphrase, boolean hiddenSsid, @BandType int band, int channel,
             @SecurityType int securityType, int maxNumberOfClients, boolean shutdownTimeoutEnabled,
-            int shutdownTimeoutMillis, boolean clientControlByUser,
+            long shutdownTimeoutMillis, boolean clientControlByUser,
             @NonNull List<MacAddress> blockedList, @NonNull List<MacAddress> allowedList) {
         mSsid = ssid;
         mBssid = bssid;
@@ -327,7 +326,7 @@
         dest.writeInt(mSecurityType);
         dest.writeInt(mMaxNumberOfClients);
         dest.writeBoolean(mAutoShutdownEnabled);
-        dest.writeInt(mShutdownTimeoutMillis);
+        dest.writeLong(mShutdownTimeoutMillis);
         dest.writeBoolean(mClientControlByUser);
         dest.writeTypedList(mBlockedClientList);
         dest.writeTypedList(mAllowedClientList);
@@ -346,7 +345,7 @@
                     in.readString(),
                     in.readParcelable(MacAddress.class.getClassLoader()),
                     in.readString(), in.readBoolean(), in.readInt(), in.readInt(), in.readInt(),
-                    in.readInt(), in.readBoolean(), in.readInt(), in.readBoolean(),
+                    in.readInt(), in.readBoolean(), in.readLong(), in.readBoolean(),
                     in.createTypedArrayList(MacAddress.CREATOR),
                     in.createTypedArrayList(MacAddress.CREATOR));
         }
@@ -454,19 +453,19 @@
     /**
      * Returns the shutdown timeout in milliseconds.
      * The Soft AP will shutdown when there are no devices associated to it for
-     * the timeout duration. See {@link Builder#setShutdownTimeoutMillis(int)}.
+     * the timeout duration. See {@link Builder#setShutdownTimeoutMillis(long)}.
      *
      * @hide
      */
     @SystemApi
-    public int getShutdownTimeoutMillis() {
+    public long getShutdownTimeoutMillis() {
         return mShutdownTimeoutMillis;
     }
 
     /**
      * Returns a flag indicating whether clients need to be pre-approved by the user.
      * (true: authorization required) or not (false: not required).
-     * {@link Builder#enableClientControlByUser(Boolean)}.
+     * {@link Builder#setClientControlByUserEnabled(Boolean)}.
      *
      * @hide
      */
@@ -478,7 +477,7 @@
     /**
      * Returns List of clients which aren't allowed to associate to the AP.
      *
-     * Clients are configured using {@link Builder#setClientList(List, List)}
+     * Clients are configured using {@link Builder#setBlockedClientList(List)}
      *
      * @hide
      */
@@ -490,7 +489,7 @@
 
     /**
      * List of clients which are allowed to associate to the AP.
-     * Clients are configured using {@link Builder#setClientList(List, List)}
+     * Clients are configured using {@link Builder#setAllowedClientList(List)}
      *
      * @hide
      */
@@ -575,7 +574,7 @@
         private int mMaxNumberOfClients;
         private int mSecurityType;
         private boolean mAutoShutdownEnabled;
-        private int mShutdownTimeoutMillis;
+        private long mShutdownTimeoutMillis;
         private boolean mClientControlByUser;
         private List<MacAddress> mBlockedClientList;
         private List<MacAddress> mAllowedClientList;
@@ -627,6 +626,11 @@
          */
         @NonNull
         public SoftApConfiguration build() {
+            for (MacAddress client : mAllowedClientList) {
+                if (mBlockedClientList.contains(client)) {
+                    throw new IllegalArgumentException("A MacAddress exist in both client list");
+                }
+            }
             return new SoftApConfiguration(mSsid, mBssid, mPassphrase,
                     mHiddenSsid, mBand, mChannel, mSecurityType, mMaxNumberOfClients,
                     mAutoShutdownEnabled, mShutdownTimeoutMillis, mClientControlByUser,
@@ -835,7 +839,7 @@
          * @param enable true to enable, false to disable.
          * @return Builder for chaining.
          *
-         * @see #setShutdownTimeoutMillis(int)
+         * @see #setShutdownTimeoutMillis(long)
          */
         @NonNull
         public Builder setAutoShutdownEnabled(boolean enable) {
@@ -862,7 +866,7 @@
          * @see #setAutoShutdownEnabled(boolean)
          */
         @NonNull
-        public Builder setShutdownTimeoutMillis(@IntRange(from = 0) int timeoutMillis) {
+        public Builder setShutdownTimeoutMillis(@IntRange(from = 0) long timeoutMillis) {
             if (timeoutMillis < 0) {
                 throw new IllegalArgumentException("Invalid timeout value");
             }
@@ -878,7 +882,7 @@
          *
          * If manual user control is enabled then clients will be accepted, rejected, or require
          * a user approval based on the configuration provided by
-         * {@link #setClientList(List, List)}.
+         * {@link #setBlockedClientList(List)} and {@link #setAllowedClientList(List)}.
          *
          * <p>
          * This method requires hardware support. Hardware support can be determined using
@@ -898,26 +902,48 @@
          * @return Builder for chaining.
          */
         @NonNull
-        public Builder enableClientControlByUser(boolean enabled) {
+        public Builder setClientControlByUserEnabled(boolean enabled) {
             mClientControlByUser = enabled;
             return this;
         }
 
 
         /**
-         * This method together with {@link enableClientControlByUser(boolean)} control client
-         * connections to the AP. If {@link enableClientControlByUser(false)} is configured than
+         * This method together with {@link setClientControlByUserEnabled(boolean)} control client
+         * connections to the AP. If client control by user is disabled using the above method then
          * this API has no effect and clients are allowed to associate to the AP (within limit of
          * max number of clients).
          *
-         * If {@link enableClientControlByUser(true)} is configured then this API configures
-         * 2 lists:
+         * If client control by user is enabled then this API configures the list of clients
+         * which are explicitly allowed. These are auto-accepted.
+         *
+         * All other clients which attempt to associate, whose MAC addresses are on neither list,
+         * are:
          * <ul>
-         * <li>List of clients which are blocked. These are rejected.</li>
-         * <li>List of clients which are explicitly allowed. These are auto-accepted.</li>
+         * <li>Rejected</li>
+         * <li>A callback {@link WifiManager.SoftApCallback#onBlockedClientConnecting(WifiClient)}
+         * is issued (which allows the user to add them to the allowed client list if desired).<li>
          * </ul>
          *
-         * <p>
+         * @param allowedClientList list of clients which are allowed to associate to the AP
+         *                          without user pre-approval.
+         * @return Builder for chaining.
+         */
+        @NonNull
+        public Builder setAllowedClientList(@NonNull List<MacAddress> allowedClientList) {
+            mAllowedClientList = new ArrayList<>(allowedClientList);
+            return this;
+        }
+
+        /**
+         * This method together with {@link setClientControlByUserEnabled(boolean)} control client
+         * connections to the AP. If client control by user is disabled using the above method then
+         * this API has no effect and clients are allowed to associate to the AP (within limit of
+         * max number of clients).
+         *
+         * If client control by user is enabled then this API this API configures the list of
+         * clients which are blocked. These are rejected.
+         *
          * All other clients which attempt to associate, whose MAC addresses are on neither list,
          * are:
          * <ul>
@@ -927,23 +953,11 @@
          * </ul>
          *
          * @param blockedClientList list of clients which are not allowed to associate to the AP.
-         * @param allowedClientList list of clients which are allowed to associate to the AP
-         *                          without user pre-approval.
          * @return Builder for chaining.
          */
         @NonNull
-        public Builder setClientList(@NonNull List<MacAddress> blockedClientList,
-                @NonNull List<MacAddress> allowedClientList) {
+        public Builder setBlockedClientList(@NonNull List<MacAddress> blockedClientList) {
             mBlockedClientList = new ArrayList<>(blockedClientList);
-            mAllowedClientList = new ArrayList<>(allowedClientList);
-            Iterator<MacAddress> iterator = mAllowedClientList.iterator();
-            while (iterator.hasNext()) {
-                MacAddress client = iterator.next();
-                int index = mBlockedClientList.indexOf(client);
-                if (index != -1) {
-                    throw new IllegalArgumentException("A MacAddress exist in both list");
-                }
-            }
             return this;
         }
     }
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index e0b433d..ba68d17 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -635,13 +635,6 @@
     public String preSharedKey;
 
     /**
-     * Optional SAE Password Id for use with WPA3-SAE. It is an ASCII string.
-     * @hide
-     */
-    @SystemApi
-    public @Nullable String saePasswordId;
-
-    /**
      * Four WEP keys. For each of the four values, provide either an ASCII
      * string enclosed in double quotation marks (e.g., {@code "abcdef"}),
      * a string of hex digits (e.g., {@code 0102030405}), or an empty string
@@ -2334,9 +2327,6 @@
             sbuf.append('*');
         }
 
-        sbuf.append('\n').append(" SAE Password Id: ");
-        sbuf.append(this.saePasswordId);
-
         sbuf.append("\nEnterprise config:\n");
         sbuf.append(enterpriseConfig);
 
@@ -2731,7 +2721,6 @@
             providerFriendlyName = source.providerFriendlyName;
             isHomeProviderNetwork = source.isHomeProviderNetwork;
             preSharedKey = source.preSharedKey;
-            saePasswordId = source.saePasswordId;
 
             mNetworkSelectionStatus.copy(source.getNetworkSelectionStatus());
             apBand = source.apBand;
@@ -2819,7 +2808,6 @@
             dest.writeLong(roamingConsortiumId);
         }
         dest.writeString(preSharedKey);
-        dest.writeString(saePasswordId);
         for (String wepKey : wepKeys) {
             dest.writeString(wepKey);
         }
@@ -2895,7 +2883,6 @@
                     config.roamingConsortiumIds[i] = in.readLong();
                 }
                 config.preSharedKey = in.readString();
-                config.saePasswordId = in.readString();
                 for (int i = 0; i < config.wepKeys.length; i++) {
                     config.wepKeys[i] = in.readString();
                 }
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 9e29a7a..e1acaf8 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -727,8 +727,9 @@
 
     /**
      *  If Soft Ap client is blocked, this reason code means that client doesn't exist in the
-     *  specified configuration {@link SoftApConfiguration.Builder#setClientList(List, List)}
-     *  and the {@link SoftApConfiguration.Builder#enableClientControlByUser(true)}
+     *  specified configuration {@link SoftApConfiguration.Builder#setBlockedClientList(List)}
+     *  and {@link SoftApConfiguration.Builder#setAllowedClientList(List)}
+     *  and the {@link SoftApConfiguration.Builder#setClientControlByUserEnabled(boolean)}
      *  is configured as well.
      *  @hide
      */
@@ -1352,7 +1353,7 @@
         try {
             ParceledListSlice<WifiConfiguration> parceledList =
                     mService.getConfiguredNetworks(mContext.getOpPackageName(),
-                            mContext.getFeatureId());
+                            mContext.getAttributionTag());
             if (parceledList == null) {
                 return Collections.emptyList();
             }
@@ -1369,7 +1370,7 @@
         try {
             ParceledListSlice<WifiConfiguration> parceledList =
                     mService.getPrivilegedConfiguredNetworks(mContext.getOpPackageName(),
-                            mContext.getFeatureId());
+                            mContext.getAttributionTag());
             if (parceledList == null) {
                 return Collections.emptyList();
             }
@@ -1898,7 +1899,7 @@
             @NonNull List<WifiNetworkSuggestion> networkSuggestions) {
         try {
             return mService.addNetworkSuggestions(
-                    networkSuggestions, mContext.getOpPackageName(), mContext.getFeatureId());
+                    networkSuggestions, mContext.getOpPackageName(), mContext.getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2680,8 +2681,8 @@
     public boolean startScan(WorkSource workSource) {
         try {
             String packageName = mContext.getOpPackageName();
-            String featureId = mContext.getFeatureId();
-            return mService.startScan(packageName, featureId);
+            String attributionTag = mContext.getAttributionTag();
+            return mService.startScan(packageName, attributionTag);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2713,7 +2714,7 @@
     public WifiInfo getConnectionInfo() {
         try {
             return mService.getConnectionInfo(mContext.getOpPackageName(),
-                    mContext.getFeatureId());
+                    mContext.getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2728,35 +2729,38 @@
     public List<ScanResult> getScanResults() {
         try {
             return mService.getScanResults(mContext.getOpPackageName(),
-                    mContext.getFeatureId());
+                    mContext.getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Return the filtered ScanResults which may be authenticated by the suggested network
-     * configurations.
-     * @param networkSuggestions The list of {@link WifiNetworkSuggestion}
-     * @param scanResults The scan results to be filtered, this is optional, if it is null or
-     * empty, wifi system would use the recent scan results in the system.
-     * @return The map of {@link WifiNetworkSuggestion} and the list of {@link ScanResult} which
-     * may be authenticated by the corresponding network configuration.
+     * Get the filtered ScanResults which match the network configurations specified by the
+     * {@code networkSuggestionsToMatch}. Suggestions which use {@link WifiConfiguration} use
+     * SSID and the security type to match. Suggestions which use {@link PasspointConfigration}
+     * use the matching rules of Hotspot 2.0.
+     * @param networkSuggestionsToMatch The list of {@link WifiNetworkSuggestion} to match against.
+     * These may or may not be suggestions which are installed on the device.
+     * @param scanResults The scan results to be filtered. Optional - if not provided(empty list),
+     * the Wi-Fi service will use the most recent scan results which the system has.
+     * @return The map of {@link WifiNetworkSuggestion} to the list of {@link ScanResult}
+     * corresponding to networks which match them.
      * @hide
      */
     @SystemApi
     @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE})
     @NonNull
     public Map<WifiNetworkSuggestion, List<ScanResult>> getMatchingScanResults(
-            @NonNull List<WifiNetworkSuggestion> networkSuggestions,
+            @NonNull List<WifiNetworkSuggestion> networkSuggestionsToMatch,
             @Nullable List<ScanResult> scanResults) {
-        if (networkSuggestions == null) {
+        if (networkSuggestionsToMatch == null) {
             throw new IllegalArgumentException("networkSuggestions must not be null.");
         }
         try {
             return mService.getMatchingScanResults(
-                    networkSuggestions, scanResults,
-                    mContext.getOpPackageName(), mContext.getFeatureId());
+                    networkSuggestionsToMatch, scanResults,
+                    mContext.getOpPackageName(), mContext.getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3195,7 +3199,7 @@
                     new LocalOnlyHotspotCallbackProxy(this, executor, callback);
             try {
                 String packageName = mContext.getOpPackageName();
-                String featureId = mContext.getFeatureId();
+                String featureId = mContext.getAttributionTag();
                 int returnCode = mService.startLocalOnlyHotspot(proxy, packageName, featureId,
                         config);
                 if (returnCode != LocalOnlyHotspotCallback.REQUEST_REGISTERED) {
@@ -3372,7 +3376,7 @@
      */
     @NonNull
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE)
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public SoftApConfiguration getSoftApConfiguration() {
         try {
             return mService.getSoftApConfiguration();
@@ -3406,9 +3410,10 @@
      * If the API is called while the tethered soft AP is enabled, the configuration will apply to
      * the current soft AP if the new configuration only includes
      * {@link SoftApConfiguration.Builder#setMaxNumberOfClients(int)}
-     * or {@link SoftApConfiguration.Builder#setShutdownTimeoutMillis(int)}
-     * or {@link SoftApConfiguration.Builder#enableClientControlByUser(boolean)}
-     * or {@link SoftApConfiguration.Builder#setClientList(List, List)}.
+     * or {@link SoftApConfiguration.Builder#setShutdownTimeoutMillis(long)}
+     * or {@link SoftApConfiguration.Builder#setClientControlByUserEnabled(boolean)}
+     * or {@link SoftApConfiguration.Builder#setBlockedClientList(List)}
+     * or {@link SoftApConfiguration.Builder#setAllowedClientList(List)}
      *
      * Otherwise, the configuration changes will be applied when the Soft AP is next started
      * (the framework will not stop/start the AP).
@@ -5886,7 +5891,7 @@
         try {
             mService.registerSuggestionConnectionStatusListener(new Binder(),
                     new SuggestionConnectionStatusListenerProxy(executor, listener),
-                    listener.hashCode(), mContext.getOpPackageName(), mContext.getFeatureId());
+                    listener.hashCode(), mContext.getOpPackageName(), mContext.getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 5e48919..d299cdc 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -182,7 +182,7 @@
     public List<Integer> getAvailableChannels(int band) {
         try {
             Bundle bundle = mService.getAvailableChannels(band, mContext.getOpPackageName(),
-                    mContext.getFeatureId());
+                    mContext.getAttributionTag());
             List<Integer> channels = bundle.getIntegerArrayList(GET_AVAILABLE_CHANNELS_EXTRA);
             return channels == null ? new ArrayList<>() : channels;
         } catch (RemoteException e) {
@@ -963,7 +963,7 @@
         scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings);
         scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource);
         scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
-        scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getFeatureId());
+        scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag());
         mAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, key, scanParams);
     }
 
@@ -984,7 +984,7 @@
         validateChannel();
         Bundle scanParams = new Bundle();
         scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
-        scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getFeatureId());
+        scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag());
         mAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, key, scanParams);
     }
 
@@ -1001,7 +1001,7 @@
         validateChannel();
         Bundle scanParams = new Bundle();
         scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
-        scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getFeatureId());
+        scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag());
         Message reply =
                 mAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0, 0, scanParams);
         return reply.what == CMD_OP_SUCCEEDED;
@@ -1056,7 +1056,7 @@
         scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings);
         scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource);
         scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
-        scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getFeatureId());
+        scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag());
         mAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, scanParams);
     }
 
@@ -1073,7 +1073,7 @@
         validateChannel();
         Bundle scanParams = new Bundle();
         scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
-        scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getFeatureId());
+        scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag());
         mAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key, scanParams);
     }
 
@@ -1086,7 +1086,7 @@
         validateChannel();
         Bundle scanParams = new Bundle();
         scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
-        scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getFeatureId());
+        scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag());
         Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SINGLE_SCAN_RESULTS, 0, 0,
                 scanParams);
         if (reply.what == WifiScanner.CMD_OP_SUCCEEDED) {
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index 2ebaa18..c2ae17c 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -267,7 +267,7 @@
 
             try {
                 Binder binder = new Binder();
-                mService.connect(binder, mContext.getOpPackageName(), mContext.getFeatureId(),
+                mService.connect(binder, mContext.getOpPackageName(), mContext.getAttributionTag(),
                         new WifiAwareEventCallbackProxy(this, looper, binder, attachCallback,
                                 identityChangedListener), configRequest,
                         identityChangedListener != null);
@@ -298,7 +298,7 @@
         }
 
         try {
-            mService.publish(mContext.getOpPackageName(), mContext.getFeatureId(), clientId,
+            mService.publish(mContext.getOpPackageName(), mContext.getAttributionTag(), clientId,
                     publishConfig,
                     new WifiAwareDiscoverySessionCallbackProxy(this, looper, true, callback,
                             clientId));
@@ -336,7 +336,7 @@
         }
 
         try {
-            mService.subscribe(mContext.getOpPackageName(), mContext.getFeatureId(), clientId,
+            mService.subscribe(mContext.getOpPackageName(), mContext.getAttributionTag(), clientId,
                     subscribeConfig,
                     new WifiAwareDiscoverySessionCallbackProxy(this, looper, false, callback,
                             clientId));
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index a310ff6..724ccf0 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -1180,7 +1180,7 @@
                 == AsyncChannel.STATUS_SUCCESSFUL) {
             Bundle bundle = new Bundle();
             bundle.putString(CALLING_PACKAGE, c.mContext.getOpPackageName());
-            bundle.putString(CALLING_FEATURE_ID, c.mContext.getFeatureId());
+            bundle.putString(CALLING_FEATURE_ID, c.mContext.getAttributionTag());
             bundle.putBinder(CALLING_BINDER, binder);
             c.mAsyncChannel.sendMessage(UPDATE_CHANNEL_INFO, 0,
                     c.putListener(null), bundle);
diff --git a/wifi/java/android/net/wifi/rtt/WifiRttManager.java b/wifi/java/android/net/wifi/rtt/WifiRttManager.java
index cb0c5d4..865702a 100644
--- a/wifi/java/android/net/wifi/rtt/WifiRttManager.java
+++ b/wifi/java/android/net/wifi/rtt/WifiRttManager.java
@@ -146,8 +146,8 @@
 
         Binder binder = new Binder();
         try {
-            mService.startRanging(binder, mContext.getOpPackageName(), mContext.getFeatureId(),
-                    workSource, request, new IRttCallback.Stub() {
+            mService.startRanging(binder, mContext.getOpPackageName(),
+                    mContext.getAttributionTag(), workSource, request, new IRttCallback.Stub() {
                         @Override
                         public void onRangingFailure(int status) throws RemoteException {
                             clearCallingIdentity();
diff --git a/wifi/tests/AndroidTest.xml b/wifi/tests/AndroidTest.xml
index 987fee7..34e2e3a 100644
--- a/wifi/tests/AndroidTest.xml
+++ b/wifi/tests/AndroidTest.xml
@@ -25,4 +25,10 @@
         <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
         <option name="hidden-api-checks" value="false"/>
     </test>
+
+    <!-- Only run FrameworksWifiApiTests in MTS if the Wifi Mainline module is installed. -->
+    <object type="module_controller"
+            class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+        <option name="mainline-module-package-name" value="com.google.android.wifi" />
+    </object>
 </configuration>
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
index 060ddf0..1a44270 100644
--- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
@@ -127,8 +127,9 @@
                 .setMaxNumberOfClients(10)
                 .setAutoShutdownEnabled(true)
                 .setShutdownTimeoutMillis(500000)
-                .enableClientControlByUser(true)
-                .setClientList(testBlockedClientList, testAllowedClientList)
+                .setClientControlByUserEnabled(true)
+                .setBlockedClientList(testBlockedClientList)
+                .setAllowedClientList(testAllowedClientList)
                 .build();
         assertThat(original.getPassphrase()).isEqualTo("secretsecret");
         assertThat(original.getSecurityType()).isEqualTo(
@@ -264,7 +265,9 @@
         ArrayList<MacAddress> testBlockedClientList = new ArrayList<>();
         testBlockedClientList.add(testMacAddress_1);
         SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
-        configBuilder.setClientList(testBlockedClientList, testAllowedClientList);
+        configBuilder.setBlockedClientList(testBlockedClientList)
+                .setAllowedClientList(testAllowedClientList)
+                .build();
     }
 
     @Test
diff --git a/wifi/tests/src/android/net/wifi/WifiScannerTest.java b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
index 0cc76b6..4881200 100644
--- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
@@ -267,7 +267,7 @@
         assertNull(messageBundle.getParcelable(WifiScanner.SCAN_PARAMS_WORK_SOURCE_KEY));
         assertEquals(mContext.getOpPackageName(),
                 messageBundle.getParcelable(WifiScanner.REQUEST_PACKAGE_NAME_KEY));
-        assertEquals(mContext.getFeatureId(),
+        assertEquals(mContext.getAttributionTag(),
                 messageBundle.getParcelable(WifiScanner.REQUEST_FEATURE_ID_KEY));
 
     }
@@ -297,7 +297,7 @@
         Bundle messageBundle = (Bundle) message.obj;
         assertEquals(mContext.getOpPackageName(),
                 messageBundle.getParcelable(WifiScanner.REQUEST_PACKAGE_NAME_KEY));
-        assertEquals(mContext.getFeatureId(),
+        assertEquals(mContext.getAttributionTag(),
                 messageBundle.getParcelable(WifiScanner.REQUEST_FEATURE_ID_KEY));
     }
 
diff --git a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
index a9dcde0..e6eae416 100644
--- a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
@@ -71,7 +71,7 @@
         mMockLooperExecutor = mMockLooper.getNewExecutor();
 
         when(mockContext.getOpPackageName()).thenReturn(packageName);
-        when(mockContext.getFeatureId()).thenReturn(featureId);
+        when(mockContext.getAttributionTag()).thenReturn(featureId);
     }
 
     /**