Merge "Call setAdapter from handleLayoutChange for the other profile" into rvc-dev
diff --git a/Android.bp b/Android.bp
index 78d38c5..71513b7 100644
--- a/Android.bp
+++ b/Android.bp
@@ -470,7 +470,7 @@
         "framework-statsd-stubs-module_libs_api",
         "framework-permission-stubs-systemapi",
         "framework-wifi-stubs-systemapi",
-        "framework-tethering-stubs",
+        "framework-tethering-stubs-module_libs_api",
     ],
     installable: true,
     javac_shard_size: 150,
@@ -518,7 +518,7 @@
         "framework-sdkextensions-stubs-systemapi",
         "framework-statsd-stubs-module_libs_api",
         "framework-wifi-stubs-systemapi",
-        "framework-tethering-stubs",
+        "framework-tethering-stubs-module_libs_api",
         // TODO (b/147688669) should be framework-telephony-stubs
         "framework-telephony",
         // TODO(jiyong): add stubs for APEXes here
@@ -540,7 +540,6 @@
     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/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/blobstore/framework/java/android/app/blob/AccessorInfo.java b/apex/blobstore/framework/java/android/app/blob/AccessorInfo.java
new file mode 100644
index 0000000..3725ad4
--- /dev/null
+++ b/apex/blobstore/framework/java/android/app/blob/AccessorInfo.java
@@ -0,0 +1,119 @@
+/*
+ * 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.app.blob;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.List;
+
+/**
+ * Class to provide information about an accessor of a shared blob.
+ *
+ * @hide
+ */
+public final class AccessorInfo implements Parcelable {
+    private final String mPackageName;
+    private final long mExpiryTimeMs;
+    private final int mDescriptionResId;
+    private final CharSequence mDescription;
+
+    public AccessorInfo(String packageName, long expiryTimeMs,
+            int descriptionResId, CharSequence description) {
+        mPackageName = packageName;
+        mExpiryTimeMs = expiryTimeMs;
+        mDescriptionResId = descriptionResId;
+        mDescription = description;
+    }
+
+    private AccessorInfo(Parcel in) {
+        mPackageName = in.readString();
+        mExpiryTimeMs = in.readLong();
+        mDescriptionResId = in.readInt();
+        mDescription = in.readCharSequence();
+    }
+
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    public long getExpiryTimeMs() {
+        return mExpiryTimeMs;
+    }
+
+    public int getDescriptionResId() {
+        return mDescriptionResId;
+    }
+
+    public CharSequence getDescription() {
+        return mDescription;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mPackageName);
+        dest.writeLong(mExpiryTimeMs);
+        dest.writeInt(mDescriptionResId);
+        dest.writeCharSequence(mDescription);
+    }
+
+    @Override
+    public String toString() {
+        return "AccessorInfo {"
+                + "package: " + mPackageName + ","
+                + "expiryMs: " + mExpiryTimeMs + ","
+                + "descriptionResId: " + mDescriptionResId + ","
+                + "description: " + mDescription + ","
+                + "}";
+    }
+
+    private String toShortString() {
+        return mPackageName;
+    }
+
+    public static String toShortString(List<AccessorInfo> accessors) {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("[");
+        for (int i = 0, size = accessors.size(); i < size; ++i) {
+            sb.append(accessors.get(i).toShortString());
+            sb.append(",");
+        }
+        sb.append("]");
+        return sb.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull
+    public static final Creator<AccessorInfo> CREATOR = new Creator<AccessorInfo>() {
+        @Override
+        @NonNull
+        public AccessorInfo createFromParcel(Parcel source) {
+            return new AccessorInfo(source);
+        }
+
+        @Override
+        @NonNull
+        public AccessorInfo[] newArray(int size) {
+            return new AccessorInfo[size];
+        }
+    };
+}
diff --git a/wifi/java/android/net/wifi/IScoreChangeCallback.aidl b/apex/blobstore/framework/java/android/app/blob/BlobInfo.aidl
similarity index 65%
copy from wifi/java/android/net/wifi/IScoreChangeCallback.aidl
copy to apex/blobstore/framework/java/android/app/blob/BlobInfo.aidl
index d691f41..2549773 100644
--- a/wifi/java/android/net/wifi/IScoreChangeCallback.aidl
+++ b/apex/blobstore/framework/java/android/app/blob/BlobInfo.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -13,17 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package android.app.blob;
 
-package android.net.wifi;
-
-/**
- * Interface for Wi-Fi score callback.
- *
- * @hide
- */
-oneway interface IScoreChangeCallback
-{
-    void onScoreChange(int sessionId, int score);
-
-    void onTriggerUpdateOfWifiUsabilityStats(int sessionId);
-}
+/** {@hide} */
+parcelable BlobInfo;
\ No newline at end of file
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobInfo.java b/apex/blobstore/framework/java/android/app/blob/BlobInfo.java
new file mode 100644
index 0000000..9746dd0
--- /dev/null
+++ b/apex/blobstore/framework/java/android/app/blob/BlobInfo.java
@@ -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.
+ */
+
+package android.app.blob;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Class to provide information about a shared blob.
+ *
+ * @hide
+ */
+public final class BlobInfo implements Parcelable {
+    private final long mId;
+    private final long mExpiryTimeMs;
+    private final CharSequence mLabel;
+    private final List<AccessorInfo> mAccessors;
+
+    public BlobInfo(long id, long expiryTimeMs, CharSequence label,
+            List<AccessorInfo> accessors) {
+        mId = id;
+        mExpiryTimeMs = expiryTimeMs;
+        mLabel = label;
+        mAccessors = accessors;
+    }
+
+    private BlobInfo(Parcel in) {
+        mId = in.readLong();
+        mExpiryTimeMs = in.readLong();
+        mLabel = in.readCharSequence();
+        mAccessors = in.readArrayList(null /* classloader */);
+    }
+
+    public long getId() {
+        return mId;
+    }
+
+    public long getExpiryTimeMs() {
+        return mExpiryTimeMs;
+    }
+
+    public CharSequence getLabel() {
+        return mLabel;
+    }
+
+    public List<AccessorInfo> getAccessors() {
+        return Collections.unmodifiableList(mAccessors);
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeLong(mId);
+        dest.writeLong(mExpiryTimeMs);
+        dest.writeCharSequence(mLabel);
+        dest.writeList(mAccessors);
+    }
+
+    @Override
+    public String toString() {
+        return toShortString();
+    }
+
+    private String toShortString() {
+        return "BlobInfo {"
+                + "id: " + mId + ","
+                + "expiryMs: " + mExpiryTimeMs + ","
+                + "label: " + mLabel + ","
+                + "accessors: " + AccessorInfo.toShortString(mAccessors) + ","
+                + "}";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull
+    public static final Creator<BlobInfo> CREATOR = new Creator<BlobInfo>() {
+        @Override
+        @NonNull
+        public BlobInfo createFromParcel(Parcel source) {
+            return new BlobInfo(source);
+        }
+
+        @Override
+        @NonNull
+        public BlobInfo[] newArray(int size) {
+            return new BlobInfo[size];
+        }
+    };
+}
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
index d1e28e9..814ab6d 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
@@ -22,16 +22,19 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.content.Context;
 import android.os.ParcelFileDescriptor;
 import android.os.ParcelableException;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
+import android.os.UserHandle;
 
 import com.android.internal.util.function.pooled.PooledLambda;
 
 import java.io.Closeable;
 import java.io.IOException;
+import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
@@ -145,9 +148,6 @@
     /** @hide */
     public static final int INVALID_RES_ID = -1;
 
-    /** @hide */
-    public static final String DESC_RES_TYPE_STRING = "string";
-
     private final Context mContext;
     private final IBlobStoreManager mService;
 
@@ -215,7 +215,7 @@
     }
 
     /**
-     * Delete an existing session and any data that was written to that session so far.
+     * Abandons an existing session and deletes any data that was written to that session so far.
      *
      * @param sessionId a unique id obtained via {@link #createSession(BlobHandle)} that
      *                  represents a particular session.
@@ -224,9 +224,9 @@
      * @throws SecurityException when the caller does not own the session, or
      *                           the session does not exist or is invalid.
      */
-    public void deleteSession(@IntRange(from = 1) long sessionId) throws IOException {
+    public void abandonSession(@IntRange(from = 1) long sessionId) throws IOException {
         try {
-            mService.deleteSession(sessionId, mContext.getOpPackageName());
+            mService.abandonSession(sessionId, mContext.getOpPackageName());
         } catch (ParcelableException e) {
             e.maybeRethrow(IOException.class);
             throw new RuntimeException(e);
@@ -454,13 +454,13 @@
     }
 
     /**
-     * Release all active leases to the blob represented by {@code blobHandle} which are
+     * Release any active lease to the blob represented by {@code blobHandle} which is
      * currently held by the caller.
      *
      * @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
-     *                   release the leases for.
+     *                   release the lease for.
      *
-     * @throws IOException when there is an I/O error while releasing the releases to the blob.
+     * @throws IOException when there is an I/O error while releasing the release to the blob.
      * @throws SecurityException when the blob represented by the {@code blobHandle} does not
      *                           exist or the caller does not have access to it.
      * @throws IllegalArgumentException when {@code blobHandle} is invalid.
@@ -481,6 +481,7 @@
      *
      * @hide
      */
+    @TestApi
     public void waitForIdle(long timeoutMillis) throws InterruptedException, TimeoutException {
         try {
             final CountDownLatch countDownLatch = new CountDownLatch(1);
@@ -495,6 +496,31 @@
         }
     }
 
+    /** @hide */
+    @NonNull
+    public List<BlobInfo> queryBlobsForUser(@NonNull UserHandle user) throws IOException {
+        try {
+            return mService.queryBlobsForUser(user.getIdentifier());
+        } catch (ParcelableException e) {
+            e.maybeRethrow(IOException.class);
+            throw new RuntimeException(e);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** @hide */
+    public void deleteBlob(@NonNull BlobInfo blobInfo) throws IOException {
+        try {
+            mService.deleteBlob(blobInfo.getId());
+        } catch (ParcelableException e) {
+            e.maybeRethrow(IOException.class);
+            throw new RuntimeException(e);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * Represents an ongoing session of a blob's contribution to the blob store managed by the
      * system.
@@ -599,7 +625,7 @@
 
         /**
          * Close this session. It can be re-opened for writing/reading if it has not been
-         * abandoned (using {@link #abandon}) or closed (using {@link #commit}).
+         * abandoned (using {@link #abandon}) or committed (using {@link #commit}).
          *
          * @throws IOException when there is an I/O error while closing the session.
          * @throws SecurityException when the caller is not the owner of the session.
diff --git a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
index a85a25c..e783813 100644
--- a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
+++ b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
@@ -16,6 +16,7 @@
 package android.app.blob;
 
 import android.app.blob.BlobHandle;
+import android.app.blob.BlobInfo;
 import android.app.blob.IBlobStoreSession;
 import android.os.RemoteCallback;
 
@@ -24,11 +25,14 @@
     long createSession(in BlobHandle handle, in String packageName);
     IBlobStoreSession openSession(long sessionId, in String packageName);
     ParcelFileDescriptor openBlob(in BlobHandle handle, in String packageName);
-    void deleteSession(long sessionId, in String packageName);
+    void abandonSession(long sessionId, in String packageName);
 
     void acquireLease(in BlobHandle handle, int descriptionResId, in CharSequence description,
             long leaseTimeoutMillis, in String packageName);
     void releaseLease(in BlobHandle handle, in String packageName);
 
     void waitForIdle(in RemoteCallback callback);
+
+    List<BlobInfo> queryBlobsForUser(int userId);
+    void deleteBlob(long blobId);
 }
\ No newline at end of file
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
index dab4797..970766d 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -15,7 +15,6 @@
  */
 package com.android.server.blob;
 
-import static android.app.blob.BlobStoreManager.DESC_RES_TYPE_STRING;
 import static android.app.blob.XmlTags.ATTR_DESCRIPTION;
 import static android.app.blob.XmlTags.ATTR_DESCRIPTION_RES_NAME;
 import static android.app.blob.XmlTags.ATTR_EXPIRY_TIME;
@@ -30,16 +29,16 @@
 import static android.os.Process.INVALID_UID;
 import static android.system.OsConstants.O_RDONLY;
 
-import static com.android.server.blob.BlobStoreConfig.LOGV;
 import static com.android.server.blob.BlobStoreConfig.TAG;
 import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_DESC_RES_NAME;
 import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_STRING_DESC;
+import static com.android.server.blob.BlobStoreUtils.getDescriptionResourceId;
+import static com.android.server.blob.BlobStoreUtils.getPackageResources;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.blob.BlobHandle;
 import android.content.Context;
-import android.content.pm.PackageManager;
 import android.content.res.ResourceId;
 import android.content.res.Resources;
 import android.os.ParcelFileDescriptor;
@@ -65,6 +64,7 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.util.Objects;
+import java.util.function.Consumer;
 
 class BlobMetadata {
     private final Object mMetadataLock = new Object();
@@ -281,6 +281,10 @@
         return false;
     }
 
+    void forEachLeasee(Consumer<Leasee> consumer) {
+        mLeasees.forEach(consumer);
+    }
+
     File getBlobFile() {
         if (mBlobFile == null) {
             mBlobFile = BlobStoreConfig.getBlobFile(mBlobId);
@@ -506,16 +510,9 @@
             if (resources == null) {
                 return null;
             }
-            try {
-                final int resId = resources.getIdentifier(descriptionResEntryName,
-                        DESC_RES_TYPE_STRING, packageName);
-                return resId <= 0 ? null : resources.getString(resId);
-            } catch (Resources.NotFoundException e) {
-                if (LOGV) {
-                    Slog.w(TAG, "Description resource not found", e);
-                }
-                return null;
-            }
+            final int resId = getDescriptionResourceId(resources, descriptionResEntryName,
+                    packageName);
+            return resId == Resources.ID_NULL ? null : resources.getString(resId);
         }
 
         @Nullable
@@ -546,19 +543,6 @@
             return desc == null ? "<none>" : desc;
         }
 
-        @Nullable
-        private static Resources getPackageResources(@NonNull Context context,
-                @NonNull String packageName, int userId) {
-            try {
-                return context.getPackageManager()
-                        .getResourcesForApplicationAsUser(packageName, userId);
-            } catch (PackageManager.NameNotFoundException e) {
-                Slog.d(TAG, "Unknown package in user " + userId + ": "
-                        + packageName, e);
-                return null;
-            }
-        }
-
         void writeToXml(@NonNull XmlSerializer out) throws IOException {
             XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, packageName);
             XmlUtils.writeIntAttribute(out, ATTR_UID, uid);
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index 96f7b7a..53a97ce 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -36,6 +36,8 @@
 import static com.android.server.blob.BlobStoreSession.STATE_VERIFIED_INVALID;
 import static com.android.server.blob.BlobStoreSession.STATE_VERIFIED_VALID;
 import static com.android.server.blob.BlobStoreSession.stateToString;
+import static com.android.server.blob.BlobStoreUtils.getDescriptionResourceId;
+import static com.android.server.blob.BlobStoreUtils.getPackageResources;
 
 import android.annotation.CurrentTimeSecondsLong;
 import android.annotation.IdRes;
@@ -43,7 +45,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.app.blob.AccessorInfo;
 import android.app.blob.BlobHandle;
+import android.app.blob.BlobInfo;
 import android.app.blob.IBlobStoreManager;
 import android.app.blob.IBlobStoreSession;
 import android.content.BroadcastReceiver;
@@ -100,6 +104,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
 import java.nio.charset.StandardCharsets;
 import java.security.SecureRandom;
 import java.util.ArrayList;
@@ -110,6 +115,7 @@
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Consumer;
+import java.util.function.Function;
 
 /**
  * Service responsible for maintaining and facilitating access to data blobs published by apps.
@@ -343,7 +349,7 @@
         return session;
     }
 
-    private void deleteSessionInternal(long sessionId,
+    private void abandonSessionInternal(long sessionId,
             int callingUid, String callingPackage) {
         synchronized (mBlobsLock) {
             final BlobStoreSession session = openSessionInternal(sessionId,
@@ -351,7 +357,7 @@
             session.open();
             session.abandon();
             if (LOGV) {
-                Slog.v(TAG, "Deleted session with id " + sessionId
+                Slog.v(TAG, "Abandoned session with id " + sessionId
                         + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
             }
             writeBlobSessionsAsync();
@@ -434,6 +440,48 @@
         }
     }
 
+    private List<BlobInfo> queryBlobsForUserInternal(int userId) {
+        final ArrayList<BlobInfo> blobInfos = new ArrayList<>();
+        synchronized (mBlobsLock) {
+            final ArrayMap<String, WeakReference<Resources>> resources = new ArrayMap<>();
+            final Function<String, Resources> resourcesGetter = (packageName) -> {
+                final WeakReference<Resources> resourcesRef = resources.get(packageName);
+                Resources packageResources = resourcesRef == null ? null : resourcesRef.get();
+                if (packageResources == null) {
+                    packageResources = getPackageResources(mContext, packageName, userId);
+                    resources.put(packageName, new WeakReference<>(packageResources));
+                }
+                return packageResources;
+            };
+            getUserBlobsLocked(userId).forEach((blobHandle, blobMetadata) -> {
+                final ArrayList<AccessorInfo> accessorInfos = new ArrayList<>();
+                blobMetadata.forEachLeasee(leasee -> {
+                    final int descriptionResId = leasee.descriptionResEntryName == null
+                            ? Resources.ID_NULL
+                            : getDescriptionResourceId(resourcesGetter.apply(leasee.packageName),
+                                    leasee.descriptionResEntryName, leasee.packageName);
+                    accessorInfos.add(new AccessorInfo(leasee.packageName, leasee.expiryTimeMillis,
+                            descriptionResId, leasee.description));
+                });
+                blobInfos.add(new BlobInfo(blobMetadata.getBlobId(),
+                        blobHandle.getExpiryTimeMillis(), blobHandle.getLabel(), accessorInfos));
+            });
+        }
+        return blobInfos;
+    }
+
+    private void deleteBlobInternal(long blobId, int callingUid) {
+        synchronized (mBlobsLock) {
+            final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(
+                    UserHandle.getUserId(callingUid));
+            userBlobs.entrySet().removeIf(entry -> {
+                final BlobMetadata blobMetadata = entry.getValue();
+                return blobMetadata.getBlobId() == blobId;
+            });
+            writeBlobsInfoAsync();
+        }
+    }
+
     private void verifyCallingPackage(int callingUid, String callingPackage) {
         if (mPackageManagerInternal.getPackageUid(
                 callingPackage, 0, UserHandle.getUserId(callingUid)) != callingUid) {
@@ -1167,7 +1215,7 @@
         }
 
         @Override
-        public void deleteSession(@IntRange(from = 1) long sessionId,
+        public void abandonSession(@IntRange(from = 1) long sessionId,
                 @NonNull String packageName) {
             Preconditions.checkArgumentPositive(sessionId,
                     "sessionId must be positive: " + sessionId);
@@ -1176,7 +1224,7 @@
             final int callingUid = Binder.getCallingUid();
             verifyCallingPackage(callingUid, packageName);
 
-            deleteSessionInternal(sessionId, callingUid, packageName);
+            abandonSessionInternal(sessionId, callingUid, packageName);
         }
 
         @Override
@@ -1250,6 +1298,28 @@
         }
 
         @Override
+        @NonNull
+        public List<BlobInfo> queryBlobsForUser(@UserIdInt int userId) {
+            if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+                throw new SecurityException("Only system uid is allowed to call "
+                        + "queryBlobsForUser()");
+            }
+
+            return queryBlobsForUserInternal(userId);
+        }
+
+        @Override
+        public void deleteBlob(long blobId) {
+            final int callingUid = Binder.getCallingUid();
+            if (callingUid != Process.SYSTEM_UID) {
+                throw new SecurityException("Only system uid is allowed to call "
+                        + "deleteBlob()");
+            }
+
+            deleteBlobInternal(blobId, callingUid);
+        }
+
+        @Override
         public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
                 @Nullable String[] args) {
             // TODO: add proto-based version of this.
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
new file mode 100644
index 0000000..6af540a
--- /dev/null
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.blob;
+
+import static com.android.server.blob.BlobStoreConfig.TAG;
+
+import android.annotation.IdRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.util.Slog;
+
+class BlobStoreUtils {
+    private static final String DESC_RES_TYPE_STRING = "string";
+
+    @Nullable
+    static Resources getPackageResources(@NonNull Context context,
+            @NonNull String packageName, int userId) {
+        try {
+            return context.getPackageManager()
+                    .getResourcesForApplicationAsUser(packageName, userId);
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.d(TAG, "Unknown package in user " + userId + ": "
+                    + packageName, e);
+            return null;
+        }
+    }
+
+    @IdRes
+    static int getDescriptionResourceId(@NonNull Resources resources,
+            @NonNull String resourceEntryName, @NonNull String packageName) {
+        return resources.getIdentifier(resourceEntryName, DESC_RES_TYPE_STRING, packageName);
+    }
+}
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 30a8b45..e43f59a 100644
--- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
+++ b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.ApexContext;
+import android.content.ApexEnvironment;
 import android.content.pm.PackageManager;
 import android.os.UserHandle;
 import android.util.ArrayMap;
@@ -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,14 +252,14 @@
     }
 
     @Override
-    public void deleteAsUser(@NonNull UserHandle user) {
+    public void deleteForUser(@NonNull UserHandle user) {
         getFile(user).delete();
     }
 
     @NonNull
     private static File getFile(@NonNull UserHandle user) {
-        ApexContext apexContext = ApexContext.getApexContext(APEX_MODULE_NAME);
-        File dataDirectory = apexContext.getDeviceProtectedDataDirForUser(user);
+        ApexEnvironment apexEnvironment = ApexEnvironment.getApexEnvironment(APEX_MODULE_NAME);
+        File dataDirectory = apexEnvironment.getDeviceProtectedDataDirForUser(user);
         return new File(dataDirectory, RUNTIME_PERMISSIONS_FILE_NAME);
     }
 }
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 3031c82..f66257f 100644
--- a/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java
+++ b/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.ApexContext;
+import android.content.ApexEnvironment;
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -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,14 +205,14 @@
     }
 
     @Override
-    public void deleteAsUser(@NonNull UserHandle user) {
+    public void deleteForUser(@NonNull UserHandle user) {
         getFile(user).delete();
     }
 
     @NonNull
     private static File getFile(@NonNull UserHandle user) {
-        ApexContext apexContext = ApexContext.getApexContext(APEX_MODULE_NAME);
-        File dataDirectory = apexContext.getDeviceProtectedDataDirForUser(user);
+        ApexEnvironment apexEnvironment = ApexEnvironment.getApexEnvironment(APEX_MODULE_NAME);
+        File dataDirectory = apexEnvironment.getDeviceProtectedDataDirForUser(user);
         return new File(dataDirectory, ROLES_FILE_NAME);
     }
 }
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/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
index dc61f2ae..c84627d 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -54,6 +54,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -102,9 +103,6 @@
     private final OnAlarmListener mAnomalyAlarmListener = new AnomalyAlarmListener();
     private final OnAlarmListener mPullingAlarmListener = new PullingAlarmListener();
     private final OnAlarmListener mPeriodicAlarmListener = new PeriodicAlarmListener();
-    private final BroadcastReceiver mAppUpdateReceiver;
-    private final BroadcastReceiver mUserUpdateReceiver;
-    private final ShutdownEventReceiver mShutdownEventReceiver;
 
     private StatsManagerService mStatsManagerService;
 
@@ -118,27 +116,6 @@
         super();
         mContext = context;
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
-        mAppUpdateReceiver = new AppUpdateReceiver();
-        mUserUpdateReceiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                synchronized (sStatsdLock) {
-                    if (sStatsd == null) {
-                        Log.w(TAG, "Could not access statsd for UserUpdateReceiver");
-                        return;
-                    }
-                    try {
-                        // Pull the latest state of UID->app name, version mapping.
-                        // Needed since the new user basically has a version of every app.
-                        informAllUidsLocked(context);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Failed to inform statsd latest update of all apps", e);
-                        forgetEverythingLocked();
-                    }
-                }
-            }
-        };
-        mShutdownEventReceiver = new ShutdownEventReceiver();
         if (DEBUG) Log.d(TAG, "Registered receiver for ACTION_PACKAGE_REPLACED and ADDED.");
         HandlerThread handlerThread = new HandlerThread(TAG);
         handlerThread.start();
@@ -162,9 +139,18 @@
         return ret;
     }
 
-    // Assumes that sStatsdLock is held.
-    @GuardedBy("sStatsdLock")
-    private void informAllUidsLocked(Context context) throws RemoteException {
+    /**
+     * Non-blocking call to retrieve a reference to statsd
+     *
+     * @return IStatsd object if statsd is ready, null otherwise.
+     */
+    private static IStatsd getStatsdNonblocking() {
+        synchronized (sStatsdLock) {
+            return sStatsd;
+        }
+    }
+
+    private static void informAllUidsLocked(Context context) throws RemoteException {
         UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
         PackageManager pm = context.getPackageManager();
         final List<UserHandle> users = um.getUserHandles(true);
@@ -273,7 +259,6 @@
                         if (!replacing) {
                             // Don't bother sending an update if we're right about to get another
                             // intent for the new version that's added.
-                            PackageManager pm = context.getPackageManager();
                             String app = intent.getData().getSchemeSpecificPart();
                             sStatsd.informOnePackageRemoved(app, uid);
                         }
@@ -303,23 +288,43 @@
         }
     }
 
-    public final static class AnomalyAlarmListener implements OnAlarmListener {
+    private static final class UserUpdateReceiver extends BroadcastReceiver {
         @Override
-        public void onAlarm() {
-            Log.i(TAG, "StatsCompanionService believes an anomaly has occurred at time "
-                    + System.currentTimeMillis() + "ms.");
+        public void onReceive(Context context, Intent intent) {
             synchronized (sStatsdLock) {
                 if (sStatsd == null) {
-                    Log.w(TAG, "Could not access statsd to inform it of anomaly alarm firing");
+                    Log.w(TAG, "Could not access statsd for UserUpdateReceiver");
                     return;
                 }
                 try {
-                    // Two-way call to statsd to retain AlarmManager wakelock
-                    sStatsd.informAnomalyAlarmFired();
+                    // Pull the latest state of UID->app name, version mapping.
+                    // Needed since the new user basically has a version of every app.
+                    informAllUidsLocked(context);
                 } catch (RemoteException e) {
-                    Log.w(TAG, "Failed to inform statsd of anomaly alarm firing", e);
+                    Log.e(TAG, "Failed to inform statsd latest update of all apps", e);
                 }
             }
+        }
+    }
+
+    public static final class AnomalyAlarmListener implements OnAlarmListener {
+        @Override
+        public void onAlarm() {
+            if (DEBUG) {
+                Log.i(TAG, "StatsCompanionService believes an anomaly has occurred at time "
+                        + System.currentTimeMillis() + "ms.");
+            }
+            IStatsd statsd = getStatsdNonblocking();
+            if (statsd == null) {
+                Log.w(TAG, "Could not access statsd to inform it of anomaly alarm firing");
+                return;
+            }
+            try {
+                // Two-way call to statsd to retain AlarmManager wakelock
+                statsd.informAnomalyAlarmFired();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to inform statsd of anomaly alarm firing", e);
+            }
             // AlarmManager releases its own wakelock here.
         }
     }
@@ -330,17 +335,16 @@
             if (DEBUG) {
                 Log.d(TAG, "Time to poll something.");
             }
-            synchronized (sStatsdLock) {
-                if (sStatsd == null) {
-                    Log.w(TAG, "Could not access statsd to inform it of pulling alarm firing.");
-                    return;
-                }
-                try {
-                    // Two-way call to statsd to retain AlarmManager wakelock
-                    sStatsd.informPollAlarmFired();
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Failed to inform statsd of pulling alarm firing.", e);
-                }
+            IStatsd statsd = getStatsdNonblocking();
+            if (statsd == null) {
+                Log.w(TAG, "Could not access statsd to inform it of pulling alarm firing.");
+                return;
+            }
+            try {
+                // Two-way call to statsd to retain AlarmManager wakelock
+                statsd.informPollAlarmFired();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to inform statsd of pulling alarm firing.", e);
             }
         }
     }
@@ -351,17 +355,16 @@
             if (DEBUG) {
                 Log.d(TAG, "Time to trigger periodic alarm.");
             }
-            synchronized (sStatsdLock) {
-                if (sStatsd == null) {
-                    Log.w(TAG, "Could not access statsd to inform it of periodic alarm firing.");
-                    return;
-                }
-                try {
-                    // Two-way call to statsd to retain AlarmManager wakelock
-                    sStatsd.informAlarmForSubscriberTriggeringFired();
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Failed to inform statsd of periodic alarm firing.", e);
-                }
+            IStatsd statsd = getStatsdNonblocking();
+            if (statsd == null) {
+                Log.w(TAG, "Could not access statsd to inform it of periodic alarm firing.");
+                return;
+            }
+            try {
+                // Two-way call to statsd to retain AlarmManager wakelock
+                statsd.informAlarmForSubscriberTriggeringFired();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to inform statsd of periodic alarm firing.", e);
             }
             // AlarmManager releases its own wakelock here.
         }
@@ -379,17 +382,19 @@
                 return;
             }
 
-            Log.i(TAG, "StatsCompanionService noticed a shutdown.");
-            synchronized (sStatsdLock) {
-                if (sStatsd == null) {
-                    Log.w(TAG, "Could not access statsd to inform it of a shutdown event.");
-                    return;
-                }
-                try {
-                    sStatsd.informDeviceShutdown();
-                } catch (Exception e) {
-                    Log.w(TAG, "Failed to inform statsd of a shutdown event.", e);
-                }
+            if (DEBUG) {
+                Log.i(TAG, "StatsCompanionService noticed a shutdown.");
+            }
+            IStatsd statsd = getStatsdNonblocking();
+            if (statsd == null) {
+                Log.w(TAG, "Could not access statsd to inform it of a shutdown event.");
+                return;
+            }
+            try {
+                // two way binder call
+                statsd.informDeviceShutdown();
+            } catch (Exception e) {
+                Log.w(TAG, "Failed to inform statsd of a shutdown event.", e);
             }
         }
     }
@@ -515,7 +520,7 @@
         }
     }
 
-    @Override
+    @Override // Binder call
     public void triggerUidSnapshot() {
         StatsCompanion.enforceStatsdCallingUid();
         synchronized (sStatsdLock) {
@@ -525,7 +530,7 @@
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to trigger uid snapshot.", e);
             } finally {
-                restoreCallingIdentity(token);
+                Binder.restoreCallingIdentity(token);
             }
         }
     }
@@ -539,15 +544,28 @@
     // Statsd related code
 
     /**
-     * Fetches the statsd IBinder service.
-     * Note: This should only be called from sayHiToStatsd. All other clients should use the cached
-     * sStatsd with a null check.
+     * Fetches the statsd IBinder service. This is a blocking call.
+     * Note: This should only be called from {@link #sayHiToStatsd()}. All other clients should use
+     * the cached sStatsd via {@link #getStatsdNonblocking()}.
      */
-    private static IStatsd fetchStatsdService() {
-        return IStatsd.Stub.asInterface(StatsFrameworkInitializer
-            .getStatsServiceManager()
-            .getStatsdServiceRegisterer()
-            .get());
+    private IStatsd fetchStatsdService(StatsdDeathRecipient deathRecipient) {
+        synchronized (sStatsdLock) {
+            if (sStatsd == null) {
+                sStatsd = IStatsd.Stub.asInterface(StatsFrameworkInitializer
+                        .getStatsServiceManager()
+                        .getStatsdServiceRegisterer()
+                        .get());
+                if (sStatsd != null) {
+                    try {
+                        sStatsd.asBinder().linkToDeath(deathRecipient, /* flags */ 0);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "linkToDeath(StatsdDeathRecipient) failed");
+                        statsdNotReadyLocked();
+                    }
+                }
+            }
+            return sStatsd;
+        }
     }
 
     /**
@@ -567,67 +585,84 @@
      * statsd.
      */
     private void sayHiToStatsd() {
-        synchronized (sStatsdLock) {
-            if (sStatsd != null) {
-                Log.e(TAG, "Trying to fetch statsd, but it was already fetched",
-                        new IllegalStateException(
-                                "sStatsd is not null when being fetched"));
-                return;
-            }
-            sStatsd = fetchStatsdService();
-            if (sStatsd == null) {
-                Log.i(TAG,
-                        "Could not yet find statsd to tell it that StatsCompanion is "
-                                + "alive.");
-                return;
-            }
-            mStatsManagerService.statsdReady(sStatsd);
-            if (DEBUG) Log.d(TAG, "Saying hi to statsd");
+        if (getStatsdNonblocking() != null) {
+            Log.e(TAG, "Trying to fetch statsd, but it was already fetched",
+                    new IllegalStateException(
+                            "sStatsd is not null when being fetched"));
+            return;
+        }
+        StatsdDeathRecipient deathRecipient = new StatsdDeathRecipient();
+        IStatsd statsd = fetchStatsdService(deathRecipient);
+        if (statsd == null) {
+            Log.i(TAG,
+                    "Could not yet find statsd to tell it that StatsCompanion is "
+                            + "alive.");
+            return;
+        }
+        mStatsManagerService.statsdReady(statsd);
+        if (DEBUG) Log.d(TAG, "Saying hi to statsd");
+        try {
+            statsd.statsCompanionReady();
+
+            cancelAnomalyAlarm();
+            cancelPullingAlarm();
+
+            BroadcastReceiver appUpdateReceiver = new AppUpdateReceiver();
+            BroadcastReceiver userUpdateReceiver = new UserUpdateReceiver();
+            BroadcastReceiver shutdownEventReceiver = new ShutdownEventReceiver();
+
+            // Setup broadcast receiver for updates.
+            IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REPLACED);
+            filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+            filter.addDataScheme("package");
+            mContext.registerReceiverForAllUsers(appUpdateReceiver, filter, null, null);
+
+            // Setup receiver for user initialize (which happens once for a new user)
+            // and
+            // if a user is removed.
+            filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE);
+            filter.addAction(Intent.ACTION_USER_REMOVED);
+            mContext.registerReceiverForAllUsers(userUpdateReceiver, filter, null, null);
+
+            // Setup receiver for device reboots or shutdowns.
+            filter = new IntentFilter(Intent.ACTION_REBOOT);
+            filter.addAction(Intent.ACTION_SHUTDOWN);
+            mContext.registerReceiverForAllUsers(
+                    shutdownEventReceiver, filter, null, null);
+
+            // Only add the receivers if the registration is successful.
+            deathRecipient.addRegisteredBroadcastReceivers(
+                    List.of(appUpdateReceiver, userUpdateReceiver, shutdownEventReceiver));
+
+            final long token = Binder.clearCallingIdentity();
             try {
-                sStatsd.statsCompanionReady();
-                // If the statsCompanionReady two-way binder call returns, link to statsd.
-                try {
-                    sStatsd.asBinder().linkToDeath(new StatsdDeathRecipient(), 0);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "linkToDeath(StatsdDeathRecipient) failed", e);
-                    forgetEverythingLocked();
-                }
-                // Setup broadcast receiver for updates.
-                IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REPLACED);
-                filter.addAction(Intent.ACTION_PACKAGE_ADDED);
-                filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-                filter.addDataScheme("package");
-                mContext.registerReceiverForAllUsers(mAppUpdateReceiver, filter, null, null);
-
-                // Setup receiver for user initialize (which happens once for a new user)
-                // and
-                // if a user is removed.
-                filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE);
-                filter.addAction(Intent.ACTION_USER_REMOVED);
-                mContext.registerReceiverForAllUsers(mUserUpdateReceiver, filter, null, null);
-
-                // Setup receiver for device reboots or shutdowns.
-                filter = new IntentFilter(Intent.ACTION_REBOOT);
-                filter.addAction(Intent.ACTION_SHUTDOWN);
-                mContext.registerReceiverForAllUsers(
-                        mShutdownEventReceiver, filter, null, null);
-                final long token = Binder.clearCallingIdentity();
-                try {
-                    // Pull the latest state of UID->app name, version mapping when
-                    // statsd starts.
-                    informAllUidsLocked(mContext);
-                } finally {
-                    restoreCallingIdentity(token);
-                }
-                Log.i(TAG, "Told statsd that StatsCompanionService is alive.");
-            } catch (RemoteException e) {
-                Log.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
-                forgetEverythingLocked();
+                // Pull the latest state of UID->app name, version mapping when
+                // statsd starts.
+                informAllUidsLocked(mContext);
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
+            Log.i(TAG, "Told statsd that StatsCompanionService is alive.");
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
         }
     }
 
     private class StatsdDeathRecipient implements IBinder.DeathRecipient {
+
+        private List<BroadcastReceiver> mReceiversToUnregister;
+
+        StatsdDeathRecipient() {
+            mReceiversToUnregister = new ArrayList<>();
+        }
+
+        public void addRegisteredBroadcastReceivers(List<BroadcastReceiver> receivers) {
+            synchronized (sStatsdLock) {
+                mReceiversToUnregister.addAll(receivers);
+            }
+        }
+
         @Override
         public void binderDied() {
             Log.i(TAG, "Statsd is dead - erase all my knowledge, except pullers");
@@ -656,20 +691,18 @@
                         }
                     }
                 }
-                forgetEverythingLocked();
+                // We only unregister in binder death becaseu receivers can only be unregistered
+                // once, or an IllegalArgumentException is thrown.
+                for (BroadcastReceiver receiver: mReceiversToUnregister) {
+                    mContext.unregisterReceiver(receiver);
+                }
+                statsdNotReadyLocked();
             }
         }
     }
 
-    @GuardedBy("StatsCompanionService.sStatsdLock")
-    private void forgetEverythingLocked() {
+    private void statsdNotReadyLocked() {
         sStatsd = null;
-        mContext.unregisterReceiver(mAppUpdateReceiver);
-        mContext.unregisterReceiver(mUserUpdateReceiver);
-        mContext.unregisterReceiver(mShutdownEventReceiver);
-        cancelAnomalyAlarm();
-        cancelPullingAlarm();
-
         mStatsManagerService.statsdNotReady();
     }
 
diff --git a/api/current.txt b/api/current.txt
index 59afab2..d944d24 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4368,7 +4368,7 @@
     method @Deprecated public int noteProxyOpNoThrow(@NonNull String, @Nullable String, int);
     method public int noteProxyOpNoThrow(@NonNull String, @Nullable String, int, @Nullable String, @Nullable String);
     method @Nullable public static String permissionToOp(@NonNull String);
-    method public void setNotedAppOpsCollector(@Nullable android.app.AppOpsManager.AppOpsCollector);
+    method public void setOnOpNotedCallback(@Nullable java.util.concurrent.Executor, @Nullable android.app.AppOpsManager.OnOpNotedCallback);
     method @Deprecated public int startOp(@NonNull String, int, @NonNull String);
     method public int startOp(@NonNull String, int, @Nullable String, @Nullable String, @Nullable String);
     method @Deprecated public int startOpNoThrow(@NonNull String, int, @NonNull String);
@@ -4424,14 +4424,6 @@
     field public static final int WATCH_FOREGROUND_CHANGES = 1; // 0x1
   }
 
-  public abstract static class AppOpsManager.AppOpsCollector {
-    ctor public AppOpsManager.AppOpsCollector();
-    method @NonNull public java.util.concurrent.Executor getAsyncNotedExecutor();
-    method public abstract void onAsyncNoted(@NonNull android.app.AsyncNotedAppOp);
-    method public abstract void onNoted(@NonNull android.app.SyncNotedAppOp);
-    method public abstract void onSelfNoted(@NonNull android.app.SyncNotedAppOp);
-  }
-
   public static interface AppOpsManager.OnOpActiveChangedListener {
     method public void onOpActiveChanged(@NonNull String, int, @NonNull String, boolean);
   }
@@ -4440,6 +4432,13 @@
     method public void onOpChanged(String, String);
   }
 
+  public abstract static class AppOpsManager.OnOpNotedCallback {
+    ctor public AppOpsManager.OnOpNotedCallback();
+    method public abstract void onAsyncNoted(@NonNull android.app.AsyncNotedAppOp);
+    method public abstract void onNoted(@NonNull android.app.SyncNotedAppOp);
+    method public abstract void onSelfNoted(@NonNull android.app.SyncNotedAppOp);
+  }
+
   public class Application extends android.content.ContextWrapper implements android.content.ComponentCallbacks2 {
     ctor public Application();
     method public static String getProcessName();
@@ -4591,7 +4590,7 @@
     method @NonNull public String getMessage();
     method @IntRange(from=0) public int getNotingUid();
     method @NonNull public String getOp();
-    method @IntRange(from=0) public long getTime();
+    method public long getTime();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.AsyncNotedAppOp> CREATOR;
   }
@@ -7583,12 +7582,12 @@
   }
 
   public class BlobStoreManager {
+    method public void abandonSession(@IntRange(from=1) long) throws java.io.IOException;
     method public void acquireLease(@NonNull android.app.blob.BlobHandle, @IdRes int, long) throws java.io.IOException;
     method public void acquireLease(@NonNull android.app.blob.BlobHandle, @NonNull CharSequence, long) throws java.io.IOException;
     method public void acquireLease(@NonNull android.app.blob.BlobHandle, @IdRes int) throws java.io.IOException;
     method public void acquireLease(@NonNull android.app.blob.BlobHandle, @NonNull CharSequence) throws java.io.IOException;
     method @IntRange(from=1) public long createSession(@NonNull android.app.blob.BlobHandle) throws java.io.IOException;
-    method public void deleteSession(@IntRange(from=1) long) throws java.io.IOException;
     method @NonNull public android.os.ParcelFileDescriptor openBlob(@NonNull android.app.blob.BlobHandle) throws java.io.IOException;
     method @NonNull public android.app.blob.BlobStoreManager.Session openSession(@IntRange(from=1) long) throws java.io.IOException;
     method public void releaseLease(@NonNull android.app.blob.BlobHandle) throws java.io.IOException;
@@ -9467,7 +9466,7 @@
   public static final class WifiDeviceFilter.Builder {
     ctor public WifiDeviceFilter.Builder();
     method @NonNull public android.companion.WifiDeviceFilter build();
-    method @NonNull public android.companion.WifiDeviceFilter.Builder setBssid(@Nullable android.net.MacAddress);
+    method @NonNull public android.companion.WifiDeviceFilter.Builder setBssid(@NonNull android.net.MacAddress);
     method @NonNull public android.companion.WifiDeviceFilter.Builder setBssidMask(@NonNull android.net.MacAddress);
     method @NonNull public android.companion.WifiDeviceFilter.Builder setNamePattern(@Nullable java.util.regex.Pattern);
   }
@@ -9882,7 +9881,7 @@
     method public void notifyChange(@NonNull android.net.Uri, @Nullable android.database.ContentObserver);
     method @Deprecated public void notifyChange(@NonNull android.net.Uri, @Nullable android.database.ContentObserver, boolean);
     method public void notifyChange(@NonNull android.net.Uri, @Nullable android.database.ContentObserver, int);
-    method public void notifyChange(@NonNull Iterable<android.net.Uri>, @Nullable android.database.ContentObserver, int);
+    method public void notifyChange(@NonNull java.util.Collection<android.net.Uri>, @Nullable android.database.ContentObserver, int);
     method @Nullable public final android.content.res.AssetFileDescriptor openAssetFile(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException;
     method @Nullable public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(@NonNull android.net.Uri, @NonNull String) throws java.io.FileNotFoundException;
     method @Nullable public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException;
@@ -10566,6 +10565,7 @@
     field public static final String ACTION_APP_ERROR = "android.intent.action.APP_ERROR";
     field public static final String ACTION_ASSIST = "android.intent.action.ASSIST";
     field public static final String ACTION_ATTACH_DATA = "android.intent.action.ATTACH_DATA";
+    field public static final String ACTION_AUTO_REVOKE_PERMISSIONS = "android.intent.action.AUTO_REVOKE_PERMISSIONS";
     field public static final String ACTION_BATTERY_CHANGED = "android.intent.action.BATTERY_CHANGED";
     field public static final String ACTION_BATTERY_LOW = "android.intent.action.BATTERY_LOW";
     field public static final String ACTION_BATTERY_OKAY = "android.intent.action.BATTERY_OKAY";
@@ -12972,11 +12972,11 @@
     method @Deprecated public final void dispatchChange(boolean);
     method public final void dispatchChange(boolean, @Nullable android.net.Uri);
     method public final void dispatchChange(boolean, @Nullable android.net.Uri, int);
-    method public final void dispatchChange(boolean, @NonNull Iterable<android.net.Uri>, int);
+    method public final void dispatchChange(boolean, @NonNull java.util.Collection<android.net.Uri>, int);
     method public void onChange(boolean);
     method public void onChange(boolean, @Nullable android.net.Uri);
     method public void onChange(boolean, @Nullable android.net.Uri, int);
-    method public void onChange(boolean, @NonNull Iterable<android.net.Uri>, int);
+    method public void onChange(boolean, @NonNull java.util.Collection<android.net.Uri>, int);
   }
 
   public interface CrossProcessCursor extends android.database.Cursor {
@@ -18118,9 +18118,7 @@
     ctor @Deprecated public FingerprintManager.CryptoObject(@NonNull java.security.Signature);
     ctor @Deprecated public FingerprintManager.CryptoObject(@NonNull javax.crypto.Cipher);
     ctor @Deprecated public FingerprintManager.CryptoObject(@NonNull javax.crypto.Mac);
-    ctor @Deprecated public FingerprintManager.CryptoObject(@NonNull android.security.identity.IdentityCredential);
     method @Deprecated public javax.crypto.Cipher getCipher();
-    method @Deprecated @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
     method @Deprecated public javax.crypto.Mac getMac();
     method @Deprecated public java.security.Signature getSignature();
   }
@@ -27055,7 +27053,8 @@
     method public void registerRouteCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.RouteCallback, @NonNull android.media.RouteDiscoveryPreference);
     method public void registerTransferCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.TransferCallback);
     method public void setOnGetControllerHintsListener(@Nullable android.media.MediaRouter2.OnGetControllerHintsListener);
-    method public void transferTo(@Nullable android.media.MediaRoute2Info);
+    method public void stop();
+    method public void transferTo(@NonNull android.media.MediaRoute2Info);
     method public void unregisterControllerCallback(@NonNull android.media.MediaRouter2.ControllerCallback);
     method public void unregisterRouteCallback(@NonNull android.media.MediaRouter2.RouteCallback);
     method public void unregisterTransferCallback(@NonNull android.media.MediaRouter2.TransferCallback);
@@ -27095,8 +27094,9 @@
 
   public abstract static class MediaRouter2.TransferCallback {
     ctor public MediaRouter2.TransferCallback();
+    method public void onStopped(@NonNull android.media.MediaRouter2.RoutingController);
     method public void onTransferFailed(@NonNull android.media.MediaRoute2Info);
-    method public void onTransferred(@NonNull android.media.MediaRouter2.RoutingController, @Nullable android.media.MediaRouter2.RoutingController);
+    method public void onTransferred(@NonNull android.media.MediaRouter2.RoutingController, @NonNull android.media.MediaRouter2.RoutingController);
   }
 
   public class MediaScannerConnection implements android.content.ServiceConnection {
@@ -36154,6 +36154,8 @@
     method public static boolean isExternalStorageEmulated(@NonNull java.io.File);
     method public static boolean isExternalStorageLegacy();
     method public static boolean isExternalStorageLegacy(@NonNull java.io.File);
+    method public static boolean isExternalStorageManager();
+    method public static boolean isExternalStorageManager(@NonNull java.io.File);
     method public static boolean isExternalStorageRemovable();
     method public static boolean isExternalStorageRemovable(@NonNull java.io.File);
     field public static String DIRECTORY_ALARMS;
@@ -42690,7 +42692,7 @@
     ctor public PersonalizationData.Builder();
     method @NonNull public android.security.identity.PersonalizationData.Builder addAccessControlProfile(@NonNull android.security.identity.AccessControlProfile);
     method @NonNull public android.security.identity.PersonalizationData build();
-    method @NonNull public android.security.identity.PersonalizationData.Builder setEntry(@NonNull String, @NonNull String, @NonNull java.util.Collection<android.security.identity.AccessControlProfileId>, @NonNull byte[]);
+    method @NonNull public android.security.identity.PersonalizationData.Builder putEntry(@NonNull String, @NonNull String, @NonNull java.util.Collection<android.security.identity.AccessControlProfileId>, @NonNull byte[]);
   }
 
   public abstract class ResultData {
@@ -42698,7 +42700,7 @@
     method @Nullable public abstract byte[] getEntry(@NonNull String, @NonNull String);
     method @Nullable public abstract java.util.Collection<java.lang.String> getEntryNames(@NonNull String);
     method @Nullable public abstract byte[] getMessageAuthenticationCode();
-    method @NonNull public abstract java.util.Collection<java.lang.String> getNamespaceNames();
+    method @NonNull public abstract java.util.Collection<java.lang.String> getNamespaces();
     method @Nullable public abstract java.util.Collection<java.lang.String> getRetrievedEntryNames(@NonNull String);
     method @NonNull public abstract byte[] getStaticAuthenticationData();
     method public abstract int getStatus(@NonNull String, @NonNull String);
@@ -43110,7 +43112,7 @@
   public static final class FillResponse.Builder {
     ctor public FillResponse.Builder();
     method @NonNull public android.service.autofill.FillResponse.Builder addDataset(@Nullable android.service.autofill.Dataset);
-    method @NonNull public android.service.autofill.FillResponse.Builder addInlineAction(@NonNull android.service.autofill.InlinePresentation);
+    method @NonNull public android.service.autofill.FillResponse.Builder addInlineAction(@NonNull android.service.autofill.InlineAction);
     method @NonNull public android.service.autofill.FillResponse build();
     method @NonNull public android.service.autofill.FillResponse.Builder disableAutofill(long);
     method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews);
@@ -43140,6 +43142,15 @@
     method public android.service.autofill.ImageTransformation build();
   }
 
+  public final class InlineAction implements android.os.Parcelable {
+    ctor public InlineAction(@NonNull android.service.autofill.InlinePresentation, @NonNull android.content.IntentSender);
+    method public int describeContents();
+    method @NonNull public android.content.IntentSender getAction();
+    method @NonNull public android.service.autofill.InlinePresentation getInlinePresentation();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.service.autofill.InlineAction> CREATOR;
+  }
+
   public final class InlinePresentation implements android.os.Parcelable {
     ctor public InlinePresentation(@NonNull android.app.slice.Slice, @NonNull android.view.inline.InlinePresentationSpec, boolean);
     method public int describeContents();
@@ -46585,12 +46596,9 @@
     field public static final String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX";
     field public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
     field public static final String IMSI_KEY_AVAILABILITY_INT = "imsi_key_availability_int";
-    field public static final String KEY_5G_ICON_CONFIGURATION_STRING = "5g_icon_configuration_string";
-    field public static final String KEY_5G_ICON_DISPLAY_GRACE_PERIOD_SEC_INT = "5g_icon_display_grace_period_sec_int";
     field public static final String KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY = "5g_nr_ssrsrp_thresholds_int_array";
     field public static final String KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY = "5g_nr_ssrsrq_thresholds_int_array";
     field public static final String KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY = "5g_nr_sssinr_thresholds_int_array";
-    field public static final String KEY_5G_WATCHDOG_TIME_MS_LONG = "5g_watchdog_time_long";
     field public static final String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
     field public static final String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
     field public static final String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
@@ -46783,7 +46791,6 @@
     field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
     field public static final String KEY_SHOW_BLOCKING_PAY_PHONE_OPTION_BOOL = "show_blocking_pay_phone_option_bool";
     field public static final String KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL = "show_call_blocking_disabled_notification_always_bool";
-    field public static final String KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING = "show_carrier_data_icon_pattern_string";
     field public static final String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool";
     field public static final String KEY_SHOW_FORWARDED_NUMBER_BOOL = "show_forwarded_number_bool";
     field public static final String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
@@ -47563,7 +47570,6 @@
     method @NonNull public java.util.List<java.lang.Integer> getAvailableServices();
     method @Nullable public android.telephony.CellIdentity getCellIdentity();
     method public int getDomain();
-    method public int getNrState();
     method @Nullable public String getRegisteredPlmn();
     method public int getTransportType();
     method public boolean isRegistered();
@@ -47766,9 +47772,7 @@
     method public boolean getIsManualSelection();
     method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoList();
     method public String getOperatorAlphaLong();
-    method @Nullable public String getOperatorAlphaLongRaw();
     method public String getOperatorAlphaShort();
-    method @Nullable public String getOperatorAlphaShortRaw();
     method public String getOperatorNumeric();
     method public boolean getRoaming();
     method public int getState();
@@ -55578,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);
@@ -55592,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();
@@ -56157,7 +56167,7 @@
   }
 
   public static final class AccessibilityNodeInfo.ExtraRenderingInfo {
-    method @Nullable public android.util.Size getLayoutParams();
+    method @Nullable public android.util.Size getLayoutSize();
     method public float getTextSizeInPx();
     method public int getTextSizeUnit();
   }
@@ -57991,7 +58001,7 @@
     method @Deprecated public abstract void removeSessionCookie();
     method public abstract void removeSessionCookies(@Nullable android.webkit.ValueCallback<java.lang.Boolean>);
     method public abstract void setAcceptCookie(boolean);
-    method public static void setAcceptFileSchemeCookies(boolean);
+    method @Deprecated public static void setAcceptFileSchemeCookies(boolean);
     method public abstract void setAcceptThirdPartyCookies(android.webkit.WebView, boolean);
     method public abstract void setCookie(String, String);
     method public abstract void setCookie(String, String, @Nullable android.webkit.ValueCallback<java.lang.Boolean>);
@@ -58356,8 +58366,8 @@
     method public abstract String getUserAgentString();
     method public abstract void setAllowContentAccess(boolean);
     method public abstract void setAllowFileAccess(boolean);
-    method public abstract void setAllowFileAccessFromFileURLs(boolean);
-    method public abstract void setAllowUniversalAccessFromFileURLs(boolean);
+    method @Deprecated public abstract void setAllowFileAccessFromFileURLs(boolean);
+    method @Deprecated public abstract void setAllowUniversalAccessFromFileURLs(boolean);
     method public abstract void setAppCacheEnabled(boolean);
     method @Deprecated public abstract void setAppCacheMaxSize(long);
     method public abstract void setAppCachePath(String);
@@ -82223,4 +82233,3 @@
   }
 
 }
-
diff --git a/api/removed.txt b/api/removed.txt
index fb6d576..8537b21 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -69,6 +69,10 @@
 
 package android.content {
 
+  public abstract class ContentResolver {
+    method @Deprecated public void notifyChange(@NonNull Iterable<android.net.Uri>, @Nullable android.database.ContentObserver, int);
+  }
+
   public abstract class Context {
     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 bfdb052..143fa7b 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -231,6 +231,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";
@@ -388,6 +389,7 @@
     field public static final String OPSTR_AUDIO_NOTIFICATION_VOLUME = "android:audio_notification_volume";
     field public static final String OPSTR_AUDIO_RING_VOLUME = "android:audio_ring_volume";
     field public static final String OPSTR_AUDIO_VOICE_VOLUME = "android:audio_voice_volume";
+    field public static final String OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = "android:auto_revoke_permissions_if_unused";
     field public static final String OPSTR_BIND_ACCESSIBILITY_SERVICE = "android:bind_accessibility_service";
     field public static final String OPSTR_CHANGE_WIFI_STATE = "android:change_wifi_state";
     field public static final String OPSTR_GET_ACCOUNTS = "android:get_accounts";
@@ -1758,8 +1760,8 @@
 
 package android.content {
 
-  public class ApexContext {
-    method @NonNull public static android.content.ApexContext getApexContext(@NonNull String);
+  public class ApexEnvironment {
+    method @NonNull public static android.content.ApexEnvironment getApexEnvironment(@NonNull String);
     method @NonNull public java.io.File getCredentialProtectedDataDirForUser(@NonNull android.os.UserHandle);
     method @NonNull public java.io.File getDeviceProtectedDataDir();
     method @NonNull public java.io.File getDeviceProtectedDataDirForUser(@NonNull android.os.UserHandle);
@@ -2014,7 +2016,6 @@
   }
 
   public final class InstallationFile {
-    ctor public InstallationFile(int, @NonNull String, long, @Nullable byte[], @Nullable byte[]);
     method public long getLengthBytes();
     method public int getLocation();
     method @Nullable public byte[] getMetadata();
@@ -2216,9 +2217,7 @@
     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 FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000
-    field public static final int FLAG_PERMISSION_AUTO_REVOKED = 1048576; // 0x100000
-    field public static final int FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED = 131072; // 0x20000
-    field public static final int FLAG_PERMISSION_AUTO_REVOKE_USER_SET = 262144; // 0x40000
+    field public static final int FLAG_PERMISSION_AUTO_REVOKED = 131072; // 0x20000
     field public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 32; // 0x20
     field public static final int FLAG_PERMISSION_GRANTED_BY_ROLE = 32768; // 0x8000
     field public static final int FLAG_PERMISSION_ONE_TIME = 65536; // 0x10000
@@ -2302,7 +2301,7 @@
     method public void onPermissionsChanged(int);
   }
 
-  @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKE_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags {
+  @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags {
   }
 
   public class PermissionGroupInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
@@ -2940,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 {
@@ -4987,7 +4986,7 @@
 
 package android.media.tv.tuner.filter {
 
-  public class AlpFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration {
+  public final class AlpFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration {
     method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.filter.AlpFilterConfiguration.Builder builder(@NonNull android.content.Context);
     method public int getLengthType();
     method public int getPacketType();
@@ -5002,10 +5001,11 @@
     field public static final int PACKET_TYPE_SIGNALING = 4; // 0x4
   }
 
-  public static class AlpFilterConfiguration.Builder extends android.media.tv.tuner.filter.FilterConfiguration.Builder<android.media.tv.tuner.filter.AlpFilterConfiguration.Builder> {
+  public static final class AlpFilterConfiguration.Builder {
     method @NonNull public android.media.tv.tuner.filter.AlpFilterConfiguration build();
     method @NonNull public android.media.tv.tuner.filter.AlpFilterConfiguration.Builder setLengthType(int);
     method @NonNull public android.media.tv.tuner.filter.AlpFilterConfiguration.Builder setPacketType(int);
+    method @NonNull public android.media.tv.tuner.filter.AlpFilterConfiguration.Builder setSettings(@Nullable android.media.tv.tuner.filter.Settings);
   }
 
   public class AudioDescriptor {
@@ -5093,15 +5093,11 @@
     method public abstract int getType();
   }
 
-  public abstract static class FilterConfiguration.Builder<T extends android.media.tv.tuner.filter.FilterConfiguration.Builder<T>> {
-    method @NonNull public T setSettings(@Nullable android.media.tv.tuner.filter.Settings);
-  }
-
   public abstract class FilterEvent {
     ctor public FilterEvent();
   }
 
-  public class IpFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration {
+  public final class IpFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration {
     method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.filter.IpFilterConfiguration.Builder builder(@NonNull android.content.Context);
     method @NonNull @Size(min=4, max=16) public byte[] getDstIpAddress();
     method public int getDstPort();
@@ -5111,11 +5107,12 @@
     method public boolean isPassthrough();
   }
 
-  public static class IpFilterConfiguration.Builder extends android.media.tv.tuner.filter.FilterConfiguration.Builder<android.media.tv.tuner.filter.IpFilterConfiguration.Builder> {
+  public static final class IpFilterConfiguration.Builder {
     method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration build();
     method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setDstIpAddress(@NonNull byte[]);
     method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setDstPort(int);
     method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setPassthrough(boolean);
+    method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setSettings(@Nullable android.media.tv.tuner.filter.Settings);
     method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setSrcIpAddress(@NonNull byte[]);
     method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setSrcPort(int);
   }
@@ -5139,15 +5136,16 @@
     method public boolean isSecureMemory();
   }
 
-  public class MmtpFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration {
+  public final class MmtpFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration {
     method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.filter.MmtpFilterConfiguration.Builder builder(@NonNull android.content.Context);
     method public int getMmtpPacketId();
     method public int getType();
   }
 
-  public static class MmtpFilterConfiguration.Builder extends android.media.tv.tuner.filter.FilterConfiguration.Builder<android.media.tv.tuner.filter.MmtpFilterConfiguration.Builder> {
+  public static final class MmtpFilterConfiguration.Builder {
     method @NonNull public android.media.tv.tuner.filter.MmtpFilterConfiguration build();
     method @NonNull public android.media.tv.tuner.filter.MmtpFilterConfiguration.Builder setMmtpPacketId(int);
+    method @NonNull public android.media.tv.tuner.filter.MmtpFilterConfiguration.Builder setSettings(@Nullable android.media.tv.tuner.filter.Settings);
   }
 
   public class MmtpRecordEvent extends android.media.tv.tuner.filter.FilterEvent {
@@ -5282,7 +5280,7 @@
     field public static final long TIMESTAMP_UNAVAILABLE = -1L; // 0xffffffffffffffffL
   }
 
-  public class TlvFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration {
+  public final class TlvFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration {
     method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.filter.TlvFilterConfiguration.Builder builder(@NonNull android.content.Context);
     method public int getPacketType();
     method public int getType();
@@ -5295,21 +5293,23 @@
     field public static final int PACKET_TYPE_SIGNALING = 254; // 0xfe
   }
 
-  public static class TlvFilterConfiguration.Builder extends android.media.tv.tuner.filter.FilterConfiguration.Builder<android.media.tv.tuner.filter.TlvFilterConfiguration.Builder> {
+  public static final class TlvFilterConfiguration.Builder {
     method @NonNull public android.media.tv.tuner.filter.TlvFilterConfiguration build();
     method @NonNull public android.media.tv.tuner.filter.TlvFilterConfiguration.Builder setCompressedIpPacket(boolean);
     method @NonNull public android.media.tv.tuner.filter.TlvFilterConfiguration.Builder setPacketType(int);
     method @NonNull public android.media.tv.tuner.filter.TlvFilterConfiguration.Builder setPassthrough(boolean);
+    method @NonNull public android.media.tv.tuner.filter.TlvFilterConfiguration.Builder setSettings(@Nullable android.media.tv.tuner.filter.Settings);
   }
 
-  public class TsFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration {
+  public final class TsFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration {
     method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.filter.TsFilterConfiguration.Builder builder(@NonNull android.content.Context);
     method public int getTpid();
     method public int getType();
   }
 
-  public static class TsFilterConfiguration.Builder extends android.media.tv.tuner.filter.FilterConfiguration.Builder<android.media.tv.tuner.filter.TsFilterConfiguration.Builder> {
+  public static final class TsFilterConfiguration.Builder {
     method @NonNull public android.media.tv.tuner.filter.TsFilterConfiguration build();
+    method @NonNull public android.media.tv.tuner.filter.TsFilterConfiguration.Builder setSettings(@Nullable android.media.tv.tuner.filter.Settings);
     method @NonNull public android.media.tv.tuner.filter.TsFilterConfiguration.Builder setTpid(int);
   }
 
@@ -6128,7 +6128,7 @@
   }
 
   public class EthernetManager {
-    method @NonNull public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull java.util.concurrent.Executor, @NonNull android.net.EthernetManager.TetheredInterfaceCallback);
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull java.util.concurrent.Executor, @NonNull android.net.EthernetManager.TetheredInterfaceCallback);
   }
 
   public static interface EthernetManager.TetheredInterfaceCallback {
@@ -7329,17 +7329,17 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(int, @Nullable android.net.wifi.WifiManager.ActionListener);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void disable(int, @Nullable android.net.wifi.WifiManager.ActionListener);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK}) public void disableEphemeralNetwork(@NonNull String);
-    method @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public void factoryReset();
+    method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void factoryReset();
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void forget(int, @Nullable android.net.wifi.WifiManager.ActionListener);
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.util.Pair<android.net.wifi.WifiConfiguration,java.util.Map<java.lang.Integer,java.util.List<android.net.wifi.ScanResult>>>> getAllMatchingWifiConfigs(@NonNull java.util.List<android.net.wifi.ScanResult>);
-    method @Nullable @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public String getCountryCode();
+    method @Nullable @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCountryCode();
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public android.net.Network getCurrentNetwork();
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String[] getFactoryMacAddresses();
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,java.util.List<android.net.wifi.ScanResult>> getMatchingOsuProviders(@Nullable java.util.List<android.net.wifi.ScanResult>);
     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();
@@ -7460,9 +7460,9 @@
     method public void onWifiUsabilityStats(int, boolean, @NonNull android.net.wifi.WifiUsabilityStatsEntry);
   }
 
-  public static interface WifiManager.ScoreChangeCallback {
-    method public void onScoreChange(int, int);
-    method public void onTriggerUpdateOfWifiUsabilityStats(int);
+  public static interface WifiManager.ScoreUpdateObserver {
+    method public void notifyScoreUpdate(int, int);
+    method public void triggerUpdateOfWifiUsabilityStats(int);
   }
 
   public static interface WifiManager.SoftApCallback {
@@ -7482,9 +7482,9 @@
   }
 
   public static interface WifiManager.WifiConnectedNetworkScorer {
-    method public void setScoreChangeCallback(@NonNull android.net.wifi.WifiManager.ScoreChangeCallback);
-    method public void start(int);
-    method public void stop(int);
+    method public void onSetScoreUpdateObserver(@NonNull android.net.wifi.WifiManager.ScoreUpdateObserver);
+    method public void onStart(int);
+    method public void onStop(int);
   }
 
   public final class WifiMigration {
@@ -7816,9 +7816,6 @@
     method public int getMaxNumberTxSpatialStreams();
     method public boolean isChannelWidthSupported(int);
     method public boolean isWifiStandardSupported(int);
-    method public void setChannelWidthSupported(int, boolean);
-    method public void setMaxNumberRxSpatialStreams(int);
-    method public void setMaxNumberTxSpatialStreams(int);
     method public void setWifiStandardSupport(int, boolean);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.nl80211.DeviceWiphyCapabilities> CREATOR;
@@ -7989,7 +7986,7 @@
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void factoryReset(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public void requestPersistentGroupInfo(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.PersistentGroupInfoListener);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.OVERRIDE_WIFI_CONFIG}) public void setDeviceName(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull String, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
-    method @RequiresPermission(allOf={android.Manifest.permission.CONNECTIVITY_INTERNAL, android.Manifest.permission.CONFIGURE_WIFI_DISPLAY}) public void setMiracastMode(int);
+    method @RequiresPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) public void setMiracastMode(int);
     method @RequiresPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) public void setWfdInfo(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.WifiP2pWfdInfo, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.OVERRIDE_WIFI_CONFIG}) public void setWifiP2pChannels(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, int, int, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void startListening(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
@@ -8948,7 +8945,7 @@
   }
 
   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>);
@@ -8956,7 +8953,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);
   }
@@ -9439,7 +9436,6 @@
 
   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";
@@ -9448,11 +9444,6 @@
     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
@@ -9867,10 +9858,10 @@
   public static final class FillResponse.Builder {
     ctor public FillResponse.Builder();
     method @NonNull public android.service.autofill.augmented.FillResponse build();
-    method @NonNull public android.service.autofill.augmented.FillResponse.Builder setClientState(@Nullable android.os.Bundle);
-    method @NonNull public android.service.autofill.augmented.FillResponse.Builder setFillWindow(@Nullable android.service.autofill.augmented.FillWindow);
-    method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineActions(@Nullable java.util.List<android.service.autofill.InlinePresentation>);
-    method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineSuggestions(@Nullable java.util.List<android.service.autofill.Dataset>);
+    method @NonNull public android.service.autofill.augmented.FillResponse.Builder setClientState(@NonNull android.os.Bundle);
+    method @NonNull public android.service.autofill.augmented.FillResponse.Builder setFillWindow(@NonNull android.service.autofill.augmented.FillWindow);
+    method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineActions(@NonNull java.util.List<android.service.autofill.InlineAction>);
+    method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineSuggestions(@NonNull java.util.List<android.service.autofill.Dataset>);
   }
 
   public final class FillWindow implements java.lang.AutoCloseable {
@@ -10916,19 +10907,6 @@
     method @NonNull public java.util.List<android.telephony.CbGeoUtils.LatLng> getVertices();
   }
 
-  public final class CdmaEriInformation implements android.os.Parcelable {
-    method public int describeContents();
-    method public int getEriIconIndex();
-    method public int getEriIconMode();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CdmaEriInformation> CREATOR;
-    field public static final int ERI_FLASH = 2; // 0x2
-    field public static final int ERI_ICON_MODE_FLASH = 1; // 0x1
-    field public static final int ERI_ICON_MODE_NORMAL = 0; // 0x0
-    field public static final int ERI_OFF = 1; // 0x1
-    field public static final int ERI_ON = 0; // 0x0
-  }
-
   public class CellBroadcastIntents {
     method public static void sendSmsCbReceivedBroadcast(@NonNull android.content.Context, @Nullable android.os.UserHandle, @NonNull android.telephony.SmsCbMessage, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, int);
     field public static final String ACTION_AREA_INFO_UPDATED = "android.telephony.action.AREA_INFO_UPDATED";
@@ -10986,7 +10964,6 @@
   public final class DataSpecificRegistrationInfo implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public android.telephony.LteVopsSupportInfo getLteVopsSupportInfo();
-    method public boolean isUsingCarrierAggregation();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.DataSpecificRegistrationInfo> CREATOR;
   }
@@ -11224,19 +11201,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();
@@ -11368,11 +11332,9 @@
     method public void fillInNotifierBundle(@NonNull android.os.Bundle);
     method public int getDataNetworkType();
     method public int getDataRegistrationState();
-    method public boolean getDataRoamingFromRegistration();
     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);
-    method public int getNrFrequencyRange();
     method @NonNull public static android.telephony.ServiceState newFromBundle(@NonNull android.os.Bundle);
     field public static final int ROAMING_TYPE_DOMESTIC = 2; // 0x2
     field public static final int ROAMING_TYPE_INTERNATIONAL = 3; // 0x3
@@ -11624,7 +11586,6 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCarrierPrivilegeStatus(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<java.lang.String> getCarrierPrivilegedPackagesForAllActiveSubscriptions();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierRestrictionRules getCarrierRestrictionRules();
-    method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CdmaEriInformation getCdmaEriInformation();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin();
@@ -11701,6 +11662,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetAllCarrierActions();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetCarrierKeysForImsiEncryption();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void resetIms(int);
+    method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void resetOtaEmergencyNumberDbFilePath();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean resetRadioConfig();
     method @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public void resetSettings();
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
@@ -11731,15 +11693,13 @@
     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();
+    method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void updateOtaEmergencyNumberDbFilePath(@NonNull android.os.ParcelFileDescriptor);
     method public void updateServiceLocation();
-    method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void updateTestOtaEmergencyNumberDbFilePath(@NonNull String);
     field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final String ACTION_ANOMALY_REPORTED = "android.telephony.action.ANOMALY_REPORTED";
     field public static final String ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE = "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE";
     field public static final String ACTION_CARRIER_SIGNAL_PCO_VALUE = "com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE";
@@ -11907,23 +11867,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();
@@ -12249,7 +12192,7 @@
     method public int getEmergencyServiceCategories();
     method @NonNull public java.util.List<java.lang.String> getEmergencyUrns();
     method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile();
-    method @Nullable public android.os.Bundle getProprietaryCallExtras();
+    method @NonNull public android.os.Bundle getProprietaryCallExtras();
     method public int getRestrictCause();
     method public int getServiceType();
     method public static int getVideoStateFromCallType(int);
@@ -12707,8 +12650,8 @@
     field public static final int KEY_RCS_CAPABILITY_DISCOVERY_ENABLED = 17; // 0x11
     field public static final int KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC = 23; // 0x17
     field public static final int KEY_RCS_MAX_NUM_ENTRIES_IN_RCL = 22; // 0x16
+    field public static final int KEY_RCS_PUBLISH_OFFLINE_AVAILABILITY_TIMER_SEC = 16; // 0x10
     field public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21; // 0x15
-    field public static final int KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC = 16; // 0x10
     field public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15; // 0xf
     field public static final int KEY_REGISTRATION_DOMAIN_NAME = 12; // 0xc
     field public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33; // 0x21
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 07b8969..23e2499 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -11,6 +11,12 @@
 
   public class AppOpsManager {
     method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, @NonNull String, @Nullable int[]);
+    method @Deprecated public void setNotedAppOpsCollector(@Nullable android.app.AppOpsManager.AppOpsCollector);
+  }
+
+  @Deprecated public abstract static class AppOpsManager.AppOpsCollector extends android.app.AppOpsManager.OnOpNotedCallback {
+    ctor public AppOpsManager.AppOpsCollector();
+    method @NonNull public java.util.concurrent.Executor getAsyncNotedExecutor();
   }
 
   public class Notification implements android.os.Parcelable {
diff --git a/api/test-current.txt b/api/test-current.txt
index c95bd09..218b934 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";
@@ -590,6 +591,14 @@
 
 }
 
+package android.app.blob {
+
+  public class BlobStoreManager {
+    method public void waitForIdle(long) throws java.lang.InterruptedException, java.util.concurrent.TimeoutException;
+  }
+
+}
+
 package android.app.prediction {
 
   public final class AppPredictionContext implements android.os.Parcelable {
@@ -784,6 +793,8 @@
   }
 
   public abstract class ContentResolver {
+    method @NonNull public static android.net.Uri decodeFromFile(@NonNull java.io.File);
+    method @NonNull public static java.io.File encodeToFile(@NonNull android.net.Uri);
     method public static String[] getSyncAdapterPackagesForAuthorityAsUser(String, int);
   }
 
@@ -1268,7 +1279,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 {
@@ -1727,7 +1738,7 @@
   }
 
   public class EthernetManager {
-    method @NonNull public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull java.util.concurrent.Executor, @NonNull android.net.EthernetManager.TetheredInterfaceCallback);
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull java.util.concurrent.Executor, @NonNull android.net.EthernetManager.TetheredInterfaceCallback);
   }
 
   public static interface EthernetManager.TetheredInterfaceCallback {
@@ -2801,9 +2812,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 {
@@ -2919,6 +2930,11 @@
     method @NonNull public android.provider.DeviceConfig.Properties.Builder setString(@NonNull String, @Nullable String);
   }
 
+  public final class DocumentsContract {
+    method public static boolean isManageMode(@NonNull android.net.Uri);
+    method @NonNull public static android.net.Uri setManageMode(@NonNull android.net.Uri);
+  }
+
   public final class MediaStore {
     method @NonNull @WorkerThread public static android.net.Uri scanFile(@NonNull android.content.ContentResolver, @NonNull java.io.File);
     method @WorkerThread public static void scanVolume(@NonNull android.content.ContentResolver, @NonNull String);
@@ -3227,10 +3243,10 @@
   public static final class FillResponse.Builder {
     ctor public FillResponse.Builder();
     method @NonNull public android.service.autofill.augmented.FillResponse build();
-    method @NonNull public android.service.autofill.augmented.FillResponse.Builder setClientState(@Nullable android.os.Bundle);
-    method @NonNull public android.service.autofill.augmented.FillResponse.Builder setFillWindow(@Nullable android.service.autofill.augmented.FillWindow);
-    method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineActions(@Nullable java.util.List<android.service.autofill.InlinePresentation>);
-    method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineSuggestions(@Nullable java.util.List<android.service.autofill.Dataset>);
+    method @NonNull public android.service.autofill.augmented.FillResponse.Builder setClientState(@NonNull android.os.Bundle);
+    method @NonNull public android.service.autofill.augmented.FillResponse.Builder setFillWindow(@NonNull android.service.autofill.augmented.FillWindow);
+    method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineActions(@NonNull java.util.List<android.service.autofill.InlineAction>);
+    method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineSuggestions(@NonNull java.util.List<android.service.autofill.Dataset>);
   }
 
   public final class FillWindow implements java.lang.AutoCloseable {
@@ -3732,11 +3748,12 @@
     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);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile();
+    method @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public void resetOtaEmergencyNumberDbFilePath();
     method @Deprecated public void setCarrierTestOverride(String, String, String, String, String, String, String);
     method public void setCarrierTestOverride(String, String, String, String, String, String, String, String, String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>);
-    method @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public void updateTestOtaEmergencyNumberDbFilePath(@NonNull String);
+    method @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public void updateOtaEmergencyNumberDbFilePath(@NonNull android.os.ParcelFileDescriptor);
     field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
     field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
     field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
@@ -3837,7 +3854,7 @@
     method public int getEmergencyServiceCategories();
     method @NonNull public java.util.List<java.lang.String> getEmergencyUrns();
     method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile();
-    method @Nullable public android.os.Bundle getProprietaryCallExtras();
+    method @NonNull public android.os.Bundle getProprietaryCallExtras();
     method public int getRestrictCause();
     method public int getServiceType();
     method public static int getVideoStateFromCallType(int);
@@ -4292,8 +4309,8 @@
     field public static final int KEY_RCS_CAPABILITY_DISCOVERY_ENABLED = 17; // 0x11
     field public static final int KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC = 23; // 0x17
     field public static final int KEY_RCS_MAX_NUM_ENTRIES_IN_RCL = 22; // 0x16
+    field public static final int KEY_RCS_PUBLISH_OFFLINE_AVAILABILITY_TIMER_SEC = 16; // 0x10
     field public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21; // 0x15
-    field public static final int KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC = 16; // 0x10
     field public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15; // 0xf
     field public static final int KEY_REGISTRATION_DOMAIN_NAME = 12; // 0xc
     field public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33; // 0x21
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 f18aaa5..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;
@@ -930,8 +931,6 @@
     ENFORCE_UID(AID_SYSTEM);
 
     VLOG("StatsService::informOnePackage was called");
-    // TODO(b/149254662): This is gross. We should consider changing statsd
-    // internals to use std::string.
     String16 utf16App = String16(app.c_str());
     String16 utf16VersionString = String16(versionString.c_str());
     String16 utf16Installer = String16(installer.c_str());
@@ -1190,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/AlarmMonitor.cpp b/cmds/statsd/src/anomaly/AlarmMonitor.cpp
index 02291181..b632d04 100644
--- a/cmds/statsd/src/anomaly/AlarmMonitor.cpp
+++ b/cmds/statsd/src/anomaly/AlarmMonitor.cpp
@@ -38,8 +38,6 @@
 void AlarmMonitor::setStatsCompanionService(
         shared_ptr<IStatsCompanionService> statsCompanionService) {
     std::lock_guard<std::mutex> lock(mLock);
-    // TODO(b/149254662): determine if tmpForLock is needed now that we have moved
-    // from sp to shared_ptr
     shared_ptr<IStatsCompanionService> tmpForLock = mStatsCompanionService;
     mStatsCompanionService = statsCompanionService;
     if (statsCompanionService == nullptr) {
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 9645a46..32a5243 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"];
@@ -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;
@@ -7640,6 +7642,9 @@
 
     // Whether AppOps is guarded by Runtime permission
     optional bool is_runtime_permission = 11;
+
+    // Sampling rate used on device, from 0 to 100
+    optional int32 sampling_rate = 12;
 }
 
 /**
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 9bdb588..6d9c644 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -54,20 +54,22 @@
     shared_ptr<IPendingIntentRef> mPir;
 };
 
-static void configReceiverDied(void* cookie) {
+void ConfigManager::configReceiverDied(void* cookie) {
     auto cookie_ = static_cast<ConfigReceiverDeathCookie*>(cookie);
-    sp<ConfigManager> configManager = cookie_->mConfigManager;
-    ConfigKey configKey = cookie_->mConfigKey;
-    shared_ptr<IPendingIntentRef> pir = cookie_->mPir;
+    sp<ConfigManager>& thiz = cookie_->mConfigManager;
+    ConfigKey& configKey = cookie_->mConfigKey;
+    shared_ptr<IPendingIntentRef>& pir = cookie_->mPir;
 
-    // TODO(b/149254662): Fix threading. This currently fails if a new
-    // pir gets set between the get and the remove.
-    if (configManager->GetConfigReceiver(configKey) == pir) {
-        configManager->RemoveConfigReceiver(configKey);
+    // Erase the mapping from the config key to the config receiver (pir) if the
+    // mapping still exists.
+    lock_guard<mutex> lock(thiz->mMutex);
+    auto it = thiz->mConfigReceivers.find(configKey);
+    if (it != thiz->mConfigReceivers.end() && it->second == pir) {
+        thiz->mConfigReceivers.erase(configKey);
     }
-    // The death recipient corresponding to this specific pir can never
-    // be triggered again, so free up resources.
-    // TODO(b/149254662): Investigate other options to manage the memory.
+
+    // The death recipient corresponding to this specific pir can never be
+    // triggered again, so free up resources.
     delete cookie_;
 }
 
@@ -83,26 +85,29 @@
     shared_ptr<IPendingIntentRef> mPir;
 };
 
-static void activeConfigChangedReceiverDied(void* cookie) {
+void ConfigManager::activeConfigChangedReceiverDied(void* cookie) {
     auto cookie_ = static_cast<ActiveConfigChangedReceiverDeathCookie*>(cookie);
-    sp<ConfigManager> configManager = cookie_->mConfigManager;
+    sp<ConfigManager>& thiz = cookie_->mConfigManager;
     int uid = cookie_->mUid;
-    shared_ptr<IPendingIntentRef> pir = cookie_->mPir;
+    shared_ptr<IPendingIntentRef>& pir = cookie_->mPir;
 
-    // TODO(b/149254662): Fix threading. This currently fails if a new
-    // pir gets set between the get and the remove.
-    if (configManager->GetActiveConfigsChangedReceiver(uid) == pir) {
-        configManager->RemoveActiveConfigsChangedReceiver(uid);
+    // Erase the mapping from the config key to the active config changed
+    // receiver (pir) if the mapping still exists.
+    lock_guard<mutex> lock(thiz->mMutex);
+    auto it = thiz->mActiveConfigsChangedReceivers.find(uid);
+    if (it != thiz->mActiveConfigsChangedReceivers.end() && it->second == pir) {
+        thiz->mActiveConfigsChangedReceivers.erase(uid);
     }
+
     // The death recipient corresponding to this specific pir can never
     // be triggered again, so free up resources.
     delete cookie_;
 }
 
-ConfigManager::ConfigManager():
+ConfigManager::ConfigManager() :
     mConfigReceiverDeathRecipient(AIBinder_DeathRecipient_new(configReceiverDied)),
     mActiveConfigChangedReceiverDeathRecipient(
-        AIBinder_DeathRecipient_new(activeConfigChangedReceiverDied)) {
+            AIBinder_DeathRecipient_new(activeConfigChangedReceiverDied)) {
 }
 
 ConfigManager::~ConfigManager() {
@@ -189,8 +194,10 @@
 
 void ConfigManager::SetActiveConfigsChangedReceiver(const int uid,
                                                     const shared_ptr<IPendingIntentRef>& pir) {
-    lock_guard<mutex> lock(mMutex);
-    mActiveConfigsChangedReceivers[uid] = pir;
+    {
+        lock_guard<mutex> lock(mMutex);
+        mActiveConfigsChangedReceivers[uid] = pir;
+    }
     AIBinder_linkToDeath(pir->asBinder().get(), mActiveConfigChangedReceiverDeathRecipient.get(),
                          new ActiveConfigChangedReceiverDeathCookie(this, uid, pir));
 }
diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h
index 824e588..40146b1 100644
--- a/cmds/statsd/src/config/ConfigManager.h
+++ b/cmds/statsd/src/config/ConfigManager.h
@@ -161,6 +161,19 @@
     // IPendingIntentRef dies.
     ::ndk::ScopedAIBinder_DeathRecipient mConfigReceiverDeathRecipient;
     ::ndk::ScopedAIBinder_DeathRecipient mActiveConfigChangedReceiverDeathRecipient;
+
+    /**
+     * Death recipient callback that is called when a config receiver dies.
+     * The cookie is a pointer to a ConfigReceiverDeathCookie.
+     */
+    static void configReceiverDied(void* cookie);
+
+    /**
+     * Death recipient callback that is called when an active config changed
+     * receiver dies. The cookie is a pointer to an
+     * ActiveConfigChangedReceiverDeathCookie.
+     */
+    static void activeConfigChangedReceiverDied(void* cookie);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 1c38542..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) {
 }
@@ -85,12 +85,9 @@
         return;
     }
 
-    // TODO(b/149254662): Why are we creating a copy here? This is different
-    // from the other places where we create a copy because we don't reassign
-    // mStatsCompanionService so a destructor can't implicitly be called...
-    shared_ptr<IStatsCompanionService> statsCompanionServiceCopy = mStatsCompanionService;
-    if (statsCompanionServiceCopy != nullptr) {
-        statsCompanionServiceCopy->setPullingAlarm(mNextPullTimeNs / 1000000);
+    // TODO(b/151045771): do not hold a lock while making a binder call
+    if (mStatsCompanionService != nullptr) {
+        mStatsCompanionService->setPullingAlarm(mNextPullTimeNs / 1000000);
     } else {
         VLOG("StatsCompanionService not available. Alarm not set.");
     }
@@ -99,8 +96,6 @@
 
 void StatsPullerManager::SetStatsCompanionService(
         shared_ptr<IStatsCompanionService> statsCompanionService) {
-    // TODO(b/149254662): Why are we using AutoMutex instead of lock_guard?
-    // Additionally, do we need the temporary shared_ptr to prevent deadlocks?
     AutoMutex _l(mLock);
     shared_ptr<IStatsCompanionService> tmpForLock = mStatsCompanionService;
     mStatsCompanionService = statsCompanionService;
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 974e203..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 3940aa8..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/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
index 93af5e9..c915ef3 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.cpp
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
@@ -39,33 +39,47 @@
     shared_ptr<IPendingIntentRef> mPir;
 };
 
-static void broadcastSubscriberDied(void* cookie) {
-    BroadcastSubscriberDeathCookie* cookie_ = (BroadcastSubscriberDeathCookie*)cookie;
-    ConfigKey configKey = cookie_->mConfigKey;
+void SubscriberReporter::broadcastSubscriberDied(void* cookie) {
+    auto cookie_ = static_cast<BroadcastSubscriberDeathCookie*>(cookie);
+    ConfigKey& configKey = cookie_->mConfigKey;
     int64_t subscriberId = cookie_->mSubscriberId;
-    shared_ptr<IPendingIntentRef> pir = cookie_->mPir;
+    shared_ptr<IPendingIntentRef>& pir = cookie_->mPir;
 
-    // TODO(b/149254662): Fix threading. This currently fails if a new pir gets
-    // set between the get and the unset.
-    if (SubscriberReporter::getInstance().getBroadcastSubscriber(configKey, subscriberId) == pir) {
-        SubscriberReporter::getInstance().unsetBroadcastSubscriber(configKey, subscriberId);
+    SubscriberReporter& thiz = getInstance();
+
+    // Erase the mapping from a (config_key, subscriberId) to a pir if the
+    // mapping exists.
+    lock_guard<mutex> lock(thiz.mLock);
+    auto subscriberMapIt = thiz.mIntentMap.find(configKey);
+    if (subscriberMapIt != thiz.mIntentMap.end()) {
+        auto subscriberMap = subscriberMapIt->second;
+        auto pirIt = subscriberMap.find(subscriberId);
+        if (pirIt != subscriberMap.end() && pirIt->second == pir) {
+            subscriberMap.erase(subscriberId);
+            if (subscriberMap.empty()) {
+                thiz.mIntentMap.erase(configKey);
+            }
+        }
     }
+
     // The death recipient corresponding to this specific pir can never be
     // triggered again, so free up resources.
     delete cookie_;
 }
 
-static ::ndk::ScopedAIBinder_DeathRecipient sBroadcastSubscriberDeathRecipient(
-        AIBinder_DeathRecipient_new(broadcastSubscriberDied));
+SubscriberReporter::SubscriberReporter() :
+    mBroadcastSubscriberDeathRecipient(AIBinder_DeathRecipient_new(broadcastSubscriberDied)) {
+}
 
 void SubscriberReporter::setBroadcastSubscriber(const ConfigKey& configKey,
                                                 int64_t subscriberId,
                                                 const shared_ptr<IPendingIntentRef>& pir) {
     VLOG("SubscriberReporter::setBroadcastSubscriber called.");
-    lock_guard<mutex> lock(mLock);
-    mIntentMap[configKey][subscriberId] = pir;
-    // TODO(b/149254662): Is it ok to call linkToDeath while holding a lock?
-    AIBinder_linkToDeath(pir->asBinder().get(), sBroadcastSubscriberDeathRecipient.get(),
+    {
+        lock_guard<mutex> lock(mLock);
+        mIntentMap[configKey][subscriberId] = pir;
+    }
+    AIBinder_linkToDeath(pir->asBinder().get(), mBroadcastSubscriberDeathRecipient.get(),
                          new BroadcastSubscriberDeathCookie(configKey, subscriberId, pir));
 }
 
@@ -103,8 +117,6 @@
     }
     int64_t subscriberId = subscription.broadcast_subscriber_details().subscriber_id();
 
-    // TODO(b/149254662): Is there a way to convert a RepeatedPtrField into a
-    // vector without copying?
     vector<string> cookies;
     cookies.reserve(subscription.broadcast_subscriber_details().cookie_size());
     for (auto& cookie : subscription.broadcast_subscriber_details().cookie()) {
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h
index 0f97d39..4fe4281 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.h
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.h
@@ -78,7 +78,7 @@
                                                          int64_t subscriberId);
 
 private:
-    SubscriberReporter() {};
+    SubscriberReporter();
 
     mutable mutex mLock;
 
@@ -94,6 +94,14 @@
                              const Subscription& subscription,
                              const vector<string>& cookies,
                              const MetricDimensionKey& dimKey) const;
+
+    ::ndk::ScopedAIBinder_DeathRecipient mBroadcastSubscriberDeathRecipient;
+
+    /**
+     * Death recipient callback that is called when a broadcast subscriber dies.
+     * The cookie is a pointer to a BroadcastSubscriberDeathCookie.
+     */
+    static void broadcastSubscriberDied(void* cookie);
 };
 
 }  // namespace statsd
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..49163273 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 isolatedUid,
+                                                        int hostUid, 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/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index f613df2..eeb5d41 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -162,7 +162,7 @@
  * might also make sense inside of a single app if the access is forwarded between two features of
  * the app.
  *
- * <p>An app can register an {@link AppOpsCollector} to get informed about what accesses the
+ * <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
  * particularly useful for an app that want to find unexpected private data accesses.
  */
@@ -206,16 +206,16 @@
 
     private static final Object sLock = new Object();
 
-    /** Current {@link AppOpsCollector}. Change via {@link #setNotedAppOpsCollector} */
+    /** Current {@link OnOpNotedCallback}. Change via {@link #setOnOpNotedCallback} */
     @GuardedBy("sLock")
-    private static @Nullable AppOpsCollector sNotedAppOpsCollector;
+    private static @Nullable OnOpNotedCallback sOnOpNotedCallback;
 
     /**
      * Additional collector that collect accesses and forwards a few of them them via
      * {@link IAppOpsService#reportRuntimeAppOpAccessMessageAndGetConfig}.
      */
-    private static AppOpsCollector sMessageCollector =
-            new AppOpsCollector() {
+    private static OnOpNotedCallback sMessageCollector =
+            new OnOpNotedCallback() {
                 @Override
                 public void onNoted(@NonNull SyncNotedAppOp op) {
                     reportStackTraceIfNeeded(op);
@@ -1060,9 +1060,12 @@
     /** @hide Access telephony call audio */
     public static final int OP_ACCESS_CALL_AUDIO = 96;
 
+    /** @hide Auto-revoke app permissions if app is unused for an extended period */
+    public static final int OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = 97;
+
     /** @hide */
     @UnsupportedAppUsage
-    public static final int _NUM_OP = 97;
+    public static final int _NUM_OP = 98;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1357,6 +1360,11 @@
     @SystemApi
     public static final String OPSTR_ACCESS_CALL_AUDIO = "android:access_call_audio";
 
+    /** @hide Auto-revoke app permissions if app is unused for an extended period */
+    @SystemApi
+    public static final String OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED =
+            "android:auto_revoke_permissions_if_unused";
+
     /** @hide Communicate cross-profile within the same profile group. */
     @SystemApi
     public static final String OPSTR_INTERACT_ACROSS_PROFILES = "android:interact_across_profiles";
@@ -1446,6 +1454,7 @@
             OP_INTERACT_ACROSS_PROFILES,
             OP_LOADER_USAGE_STATS,
             OP_ACCESS_CALL_AUDIO,
+            OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
     };
 
     /**
@@ -1554,6 +1563,7 @@
             OP_ACTIVATE_PLATFORM_VPN,           // ACTIVATE_PLATFORM_VPN
             OP_LOADER_USAGE_STATS,              // LOADER_USAGE_STATS
             OP_ACCESS_CALL_AUDIO,               // ACCESS_CALL_AUDIO
+            OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, //AUTO_REVOKE_PERMISSIONS_IF_UNUSED
     };
 
     /**
@@ -1657,6 +1667,7 @@
             OPSTR_ACTIVATE_PLATFORM_VPN,
             OPSTR_LOADER_USAGE_STATS,
             OPSTR_ACCESS_CALL_AUDIO,
+            OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
     };
 
     /**
@@ -1761,6 +1772,7 @@
             "ACTIVATE_PLATFORM_VPN",
             "LOADER_USAGE_STATS",
             "ACCESS_CALL_AUDIO",
+            "AUTO_REVOKE_PERMISSIONS_IF_UNUSED",
     };
 
     /**
@@ -1866,6 +1878,7 @@
             null, // no permission for OP_ACTIVATE_PLATFORM_VPN
             android.Manifest.permission.LOADER_USAGE_STATS,
             Manifest.permission.ACCESS_CALL_AUDIO,
+            null, // no permission for OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
     };
 
     /**
@@ -1971,6 +1984,7 @@
             null, // ACTIVATE_PLATFORM_VPN
             null, // LOADER_USAGE_STATS
             null, // ACCESS_CALL_AUDIO
+            null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
     };
 
     /**
@@ -2075,6 +2089,7 @@
             false, // ACTIVATE_PLATFORM_VPN
             false, // LOADER_USAGE_STATS
             false, // ACCESS_CALL_AUDIO
+            false, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
     };
 
     /**
@@ -2178,6 +2193,7 @@
             AppOpsManager.MODE_IGNORED, // ACTIVATE_PLATFORM_VPN
             AppOpsManager.MODE_DEFAULT, // LOADER_USAGE_STATS
             AppOpsManager.MODE_DEFAULT, // ACCESS_CALL_AUDIO
+            AppOpsManager.MODE_DEFAULT, // OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
     };
 
     /**
@@ -2285,6 +2301,7 @@
             false, // ACTIVATE_PLATFORM_VPN
             false, // LOADER_USAGE_STATS
             false, // ACCESS_CALL_AUDIO
+            false, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
     };
 
     /**
@@ -7783,8 +7800,8 @@
      */
     private void collectNotedOpForSelf(int op, @Nullable String featureId) {
         synchronized (sLock) {
-            if (sNotedAppOpsCollector != null) {
-                sNotedAppOpsCollector.onSelfNoted(new SyncNotedAppOp(op, featureId));
+            if (sOnOpNotedCallback != null) {
+                sOnOpNotedCallback.onSelfNoted(new SyncNotedAppOp(op, featureId));
             }
         }
         sMessageCollector.onSelfNoted(new SyncNotedAppOp(op, featureId));
@@ -7933,8 +7950,8 @@
                 synchronized (sLock) {
                     for (int code = notedAppOps.nextSetBit(0); code != -1;
                             code = notedAppOps.nextSetBit(code + 1)) {
-                        if (sNotedAppOpsCollector != null) {
-                            sNotedAppOpsCollector.onNoted(new SyncNotedAppOp(code, featureId));
+                        if (sOnOpNotedCallback != null) {
+                            sOnOpNotedCallback.onNoted(new SyncNotedAppOp(code, featureId));
                         }
                     }
                 }
@@ -7947,33 +7964,45 @@
     }
 
     /**
-     * Register a new {@link AppOpsCollector}.
+     * Set a new {@link OnOpNotedCallback}.
      *
-     * <p>There can only ever be one collector per process. If there currently is a collector
-     * registered, it will be unregistered.
+     * <p>There can only ever be one collector per process. If there currently is another callback
+     * set, this will fail.
      *
-     * <p><b>Only appops related to dangerous permissions are collected.</b>
+     * @param asyncExecutor executor to execute {@link OnOpNotedCallback#onAsyncNoted} on, {@code
+     * null} to unset
+     * @param callback listener to set, {@code null} to unset
      *
-     * @param collector The collector to set or {@code null} to unregister.
+     * @throws IllegalStateException If another callback is already registered
      */
-    public void setNotedAppOpsCollector(@Nullable AppOpsCollector collector) {
+    public void setOnOpNotedCallback(@Nullable @CallbackExecutor Executor asyncExecutor,
+            @Nullable OnOpNotedCallback callback) {
+        Preconditions.checkState((callback == null) == (asyncExecutor == null));
+
         synchronized (sLock) {
-            if (sNotedAppOpsCollector != null) {
+            if (callback == null) {
+                Preconditions.checkState(sOnOpNotedCallback != null,
+                        "No callback is currently registered");
+
                 try {
                     mService.stopWatchingAsyncNoted(mContext.getPackageName(),
-                            sNotedAppOpsCollector.mAsyncCb);
+                            sOnOpNotedCallback.mAsyncCb);
                 } catch (RemoteException e) {
                     e.rethrowFromSystemServer();
                 }
-            }
 
-            sNotedAppOpsCollector = collector;
+                sOnOpNotedCallback = null;
+            } else {
+                Preconditions.checkState(sOnOpNotedCallback == null,
+                        "Another callback is already registered");
 
-            if (sNotedAppOpsCollector != null) {
+                callback.mAsyncExecutor = asyncExecutor;
+                sOnOpNotedCallback = callback;
+
                 List<AsyncNotedAppOp> missedAsyncOps = null;
                 try {
                     mService.startWatchingAsyncNoted(mContext.getPackageName(),
-                            sNotedAppOpsCollector.mAsyncCb);
+                            sOnOpNotedCallback.mAsyncCb);
                     missedAsyncOps = mService.extractAsyncOps(mContext.getPackageName());
                 } catch (RemoteException e) {
                     e.rethrowFromSystemServer();
@@ -7983,10 +8012,9 @@
                     int numMissedAsyncOps = missedAsyncOps.size();
                     for (int i = 0; i < numMissedAsyncOps; i++) {
                         final AsyncNotedAppOp asyncNotedAppOp = missedAsyncOps.get(i);
-                        if (sNotedAppOpsCollector != null) {
-                            sNotedAppOpsCollector.getAsyncNotedExecutor().execute(
-                                    () -> sNotedAppOpsCollector.onAsyncNoted(
-                                            asyncNotedAppOp));
+                        if (sOnOpNotedCallback != null) {
+                            sOnOpNotedCallback.getAsyncNotedExecutor().execute(
+                                    () -> sOnOpNotedCallback.onAsyncNoted(asyncNotedAppOp));
                         }
                     }
                 }
@@ -7994,27 +8022,50 @@
         }
     }
 
+    // TODO moltmann: Remove
     /**
-     * @return {@code true} iff the process currently is currently collecting noted appops.
+     * Will be removed before R ships, leave it just to not break apps immediately.
      *
-     * @see #setNotedAppOpsCollector(AppOpsCollector)
+     * @removed
      *
      * @hide
      */
-    public static boolean isCollectingNotedAppOps() {
-        return sNotedAppOpsCollector != null;
+    @SystemApi
+    @Deprecated
+    public void setNotedAppOpsCollector(@Nullable AppOpsCollector collector) {
+        synchronized (sLock) {
+            if (collector != null) {
+                if (isListeningForOpNoted()) {
+                    setOnOpNotedCallback(null, null);
+                }
+                setOnOpNotedCallback(new HandlerExecutor(Handler.getMain()), collector);
+            } else if (sOnOpNotedCallback != null) {
+                setOnOpNotedCallback(null, null);
+            }
+        }
     }
 
     /**
-     * Callback an app can {@link #setNotedAppOpsCollector register} to monitor the app-ops the
-     * system has tracked for it. I.e. each time any app calls {@link #noteOp} or {@link #startOp}
-     * one of the callback methods of this object is called.
+     * @return {@code true} iff the process currently is currently collecting noted appops.
      *
-     * <p><b>There will be a callback for all app-ops related to runtime permissions, but not
+     * @see #setOnOpNotedCallback
+     *
+     * @hide
+     */
+    public static boolean isListeningForOpNoted() {
+        return sOnOpNotedCallback != null;
+    }
+
+    /**
+     * Callback an app can {@link #setOnOpNotedCallback set} to monitor the app-ops the
+     * system has tracked for it. I.e. each time any app calls {@link #noteOp} or {@link #startOp}
+     * one of a method of this object is called.
+     *
+     * <p><b>There will be a call for all app-ops related to runtime permissions, but not
      * necessarily for all other app-ops.
      *
      * <pre>
-     * setNotedAppOpsCollector(new AppOpsCollector() {
+     * setOnOpNotedCallback(getMainExecutor(), new OnOpNotedCallback() {
      *     ArraySet<Pair<String, String>> opsNotedForThisProcess = new ArraySet<>();
      *
      *     private synchronized void addAccess(String op, String accessLocation) {
@@ -8039,24 +8090,36 @@
      * });
      * </pre>
      *
-     * @see #setNotedAppOpsCollector
+     * @see #setOnOpNotedCallback
      */
-    public abstract static class AppOpsCollector {
+    public abstract static class OnOpNotedCallback {
+        private @NonNull Executor mAsyncExecutor;
+
         /** Callback registered with the system. This will receive the async notes ops */
         private final IAppOpsAsyncNotedCallback mAsyncCb = new IAppOpsAsyncNotedCallback.Stub() {
             @Override
             public void opNoted(AsyncNotedAppOp op) {
                 Objects.requireNonNull(op);
 
-                getAsyncNotedExecutor().execute(() -> onAsyncNoted(op));
+                long token = Binder.clearCallingIdentity();
+                try {
+                    getAsyncNotedExecutor().execute(() -> onAsyncNoted(op));
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         };
 
+        // TODO moltmann: Remove
         /**
+         * Will be removed before R ships.
+         *
          * @return The executor for the system to use when calling {@link #onAsyncNoted}.
+         *
+         * @hide
          */
-        public @NonNull Executor getAsyncNotedExecutor() {
-            return new HandlerExecutor(Handler.getMain());
+        protected @NonNull Executor getAsyncNotedExecutor() {
+            return mAsyncExecutor;
         }
 
         /**
@@ -8066,7 +8129,7 @@
          * <p>Called on the calling thread before the API returns. This allows the app to e.g.
          * collect stack traces to figure out where the access came from.
          *
-         * @param op The op noted
+         * @param op op noted
          */
         public abstract void onNoted(@NonNull SyncNotedAppOp op);
 
@@ -8076,7 +8139,7 @@
          * <p>This is very similar to {@link #onNoted} only that the tracking was not caused by the
          * API provider in a separate process, but by one in the app's own process.
          *
-         * @param op The op noted
+         * @param op op noted
          */
         public abstract void onSelfNoted(@NonNull SyncNotedAppOp op);
 
@@ -8088,14 +8151,30 @@
          * guaranteed. Due to how async calls work in Android this might even be delivered slightly
          * before the private data is delivered to the app.
          *
-         * <p>If the app is not running or no {@link AppOpsCollector} is registered a small amount
-         * of noted app-ops are buffered and then delivered as soon as a collector is registered.
+         * <p>If the app is not running or no {@link OnOpNotedCallback} is registered a small amount
+         * of noted app-ops are buffered and then delivered as soon as a listener is registered.
          *
-         * @param asyncOp The op noted
+         * @param asyncOp op noted
          */
         public abstract void onAsyncNoted(@NonNull AsyncNotedAppOp asyncOp);
     }
 
+    // TODO moltmann: Remove
+    /**
+     * Will be removed before R ships, leave it just to not break apps immediately.
+     *
+     * @removed
+     *
+     * @hide
+     */
+    @SystemApi
+    @Deprecated
+    public abstract static class AppOpsCollector extends OnOpNotedCallback {
+        public @NonNull Executor getAsyncNotedExecutor() {
+            return new HandlerExecutor(Handler.getMain());
+        }
+    };
+
     /**
      * Generate a stack trace used for noted app-ops logging.
      *
diff --git a/core/java/android/app/AsyncNotedAppOp.java b/core/java/android/app/AsyncNotedAppOp.java
index 6b1afda..4d955db 100644
--- a/core/java/android/app/AsyncNotedAppOp.java
+++ b/core/java/android/app/AsyncNotedAppOp.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.annotation.CurrentTimeMillisLong;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -23,13 +24,15 @@
 
 import com.android.internal.annotations.Immutable;
 import com.android.internal.util.DataClass;
+import com.android.internal.util.Preconditions;
 
 /**
  * When an {@link AppOpsManager#noteOp(String, int, String, String, String) app-op is noted} and the
- * app the app-op is noted for has a {@link AppOpsManager.AppOpsCollector} registered the note-event
- * needs to be delivered to the collector. Usually this is done via an {@link SyncNotedAppOp}, but
- * in some cases this is not possible. In this case an {@link AsyncNotedAppOp} is send to the system
- * server and then forwarded to the {@link AppOpsManager.AppOpsCollector} in the app.
+ * app the app-op is noted for has a {@link AppOpsManager.OnOpNotedCallback} registered the
+ * note-event needs to be delivered to the callback. Usually this is done via an
+ * {@link SyncNotedAppOp}, but in some cases this is not possible. In this case an
+ * {@link AsyncNotedAppOp} is send to the system server and then forwarded to the
+ * {@link AppOpsManager.OnOpNotedCallback} in the app.
  */
 @Immutable
 @DataClass(genEqualsHashCode = true,
@@ -40,7 +43,7 @@
 @DataClass.Suppress({"getOpCode"})
 public final class AsyncNotedAppOp implements Parcelable {
     /** Op that was noted */
-    private final @IntRange(from = 0, to = AppOpsManager._NUM_OP - 1) int mOpCode;
+    private final @IntRange(from = 0) int mOpCode;
 
     /** Uid that noted the op */
     private final @IntRange(from = 0) int mNotingUid;
@@ -52,7 +55,7 @@
     private final @NonNull String mMessage;
 
     /** Milliseconds since epoch when the op was noted */
-    private final @IntRange(from = 0) long mTime;
+    private final @CurrentTimeMillisLong long mTime;
 
     /**
      * @return Op that was noted.
@@ -61,9 +64,15 @@
         return AppOpsManager.opToPublicName(mOpCode);
     }
 
+    //TODO eugenesusla: support inlinable expressions in annotation params of @DataClass members to
+    // allow validating via @IntRange(from = 0, to = AppOpsManager._NUM_OP - 1)
+    private void onConstructed() {
+        Preconditions.checkArgumentInRange(mOpCode, 0, AppOpsManager._NUM_OP - 1, "opCode");
+    }
 
 
-    // Code below generated by codegen v1.0.14.
+
+    // Code below generated by codegen v1.0.15.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -93,16 +102,15 @@
      */
     @DataClass.Generated.Member
     public AsyncNotedAppOp(
-            @IntRange(from = 0, to = AppOpsManager._NUM_OP - 1) int opCode,
+            @IntRange(from = 0) int opCode,
             @IntRange(from = 0) int notingUid,
             @Nullable String featureId,
             @NonNull String message,
-            @IntRange(from = 0) long time) {
+            @CurrentTimeMillisLong long time) {
         this.mOpCode = opCode;
         com.android.internal.util.AnnotationValidations.validate(
                 IntRange.class, null, mOpCode,
-                "from", 0,
-                "to", AppOpsManager._NUM_OP - 1);
+                "from", 0);
         this.mNotingUid = notingUid;
         com.android.internal.util.AnnotationValidations.validate(
                 IntRange.class, null, mNotingUid,
@@ -113,10 +121,9 @@
                 NonNull.class, null, mMessage);
         this.mTime = time;
         com.android.internal.util.AnnotationValidations.validate(
-                IntRange.class, null, mTime,
-                "from", 0);
+                CurrentTimeMillisLong.class, null, mTime);
 
-        // onConstructed(); // You can define this method to get a callback
+        onConstructed();
     }
 
     /**
@@ -147,7 +154,7 @@
      * Milliseconds since epoch when the op was noted
      */
     @DataClass.Generated.Member
-    public @IntRange(from = 0) long getTime() {
+    public @CurrentTimeMillisLong long getTime() {
         return mTime;
     }
 
@@ -223,8 +230,7 @@
         this.mOpCode = opCode;
         com.android.internal.util.AnnotationValidations.validate(
                 IntRange.class, null, mOpCode,
-                "from", 0,
-                "to", AppOpsManager._NUM_OP - 1);
+                "from", 0);
         this.mNotingUid = notingUid;
         com.android.internal.util.AnnotationValidations.validate(
                 IntRange.class, null, mNotingUid,
@@ -235,10 +241,9 @@
                 NonNull.class, null, mMessage);
         this.mTime = time;
         com.android.internal.util.AnnotationValidations.validate(
-                IntRange.class, null, mTime,
-                "from", 0);
+                CurrentTimeMillisLong.class, null, mTime);
 
-        // onConstructed(); // You can define this method to get a callback
+        onConstructed();
     }
 
     @DataClass.Generated.Member
@@ -256,10 +261,10 @@
     };
 
     @DataClass.Generated(
-            time = 1581728574427L,
-            codegenVersion = "1.0.14",
+            time = 1583866178330L,
+            codegenVersion = "1.0.15",
             sourceFile = "frameworks/base/core/java/android/app/AsyncNotedAppOp.java",
-            inputSignatures = "private final @android.annotation.IntRange(from=0L, to=96L) 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.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\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 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)")
     @Deprecated
     private void __metadata() {}
 
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/SyncNotedAppOp.java b/core/java/android/app/SyncNotedAppOp.java
index aa11b95..13b90ca 100644
--- a/core/java/android/app/SyncNotedAppOp.java
+++ b/core/java/android/app/SyncNotedAppOp.java
@@ -28,9 +28,9 @@
  * Description of an app-op that was noted for the current process.
  *
  * <p>This is either delivered after a
- * {@link AppOpsManager.AppOpsCollector#onNoted(SyncNotedAppOp) two way binder call} or
+ * {@link AppOpsManager.OnOpNotedCallback#onNoted(SyncNotedAppOp) two way binder call} or
  * when the app
- * {@link AppOpsManager.AppOpsCollector#onSelfNoted(SyncNotedAppOp) notes an app-op for
+ * {@link AppOpsManager.OnOpNotedCallback#onSelfNoted(SyncNotedAppOp) notes an app-op for
  * itself}.
  */
 @Immutable
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/WindowContext.java b/core/java/android/app/WindowContext.java
index 5684562..878993e 100644
--- a/core/java/android/app/WindowContext.java
+++ b/core/java/android/app/WindowContext.java
@@ -60,7 +60,6 @@
         mWms = WindowManagerGlobal.getWindowManagerService();
         mToken = new WindowTokenClient();
 
-
         final ContextImpl contextImpl = createBaseWindowContext(base, mToken);
         attachBaseContext(contextImpl);
         contextImpl.setOuterContext(this);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index b219394..c532279 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -8781,18 +8781,20 @@
     }
 
     /**
+     * This method is mostly deprecated.
+     * Most of the settings that still have an effect have dedicated setter methods or user
+     * restrictions. See individual settings for details.
+     * <p>
      * Called by device owner to update {@link android.provider.Settings.Global} settings.
      * Validation that the value of the setting is in the correct form for the setting type should
      * be performed by the caller.
      * <p>
      * The settings that can be updated with this method are:
      * <ul>
-     * <li>{@link android.provider.Settings.Global#ADB_ENABLED}</li>
-     * <li>{@link android.provider.Settings.Global#AUTO_TIME}</li>
-     * <li>{@link android.provider.Settings.Global#AUTO_TIME_ZONE}</li>
-     * <li>{@link android.provider.Settings.Global#DATA_ROAMING}</li>
+     * <li>{@link android.provider.Settings.Global#ADB_ENABLED} : use
+     * {@link UserManager#DISALLOW_DEBUGGING_FEATURES} instead to restrict users from enabling
+     * debugging features and this setting to turn adb on.</li>
      * <li>{@link android.provider.Settings.Global#USB_MASS_STORAGE_ENABLED}</li>
-     * <li>{@link android.provider.Settings.Global#WIFI_SLEEP_POLICY}</li>
      * <li>{@link android.provider.Settings.Global#STAY_ON_WHILE_PLUGGED_IN} This setting is only
      * available from {@link android.os.Build.VERSION_CODES#M} onwards and can only be set if
      * {@link #setMaximumTimeToLock} is not used to set a timeout.</li>
@@ -8800,6 +8802,16 @@
      * setting is only available from {@link android.os.Build.VERSION_CODES#M} onwards.</li>
      * </ul>
      * <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
+     * {@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#DATA_ROAMING} : Use
+     * {@link UserManager#DISALLOW_DATA_ROAMING} instead.</li>
+     * </ul>
+     * <p>
      * Changing the following settings has no effect as of {@link android.os.Build.VERSION_CODES#M}:
      * <ul>
      * <li>{@link android.provider.Settings.Global#BLUETOOTH_ON}. Use
@@ -8811,6 +8823,7 @@
      * <li>{@link android.provider.Settings.Global#NETWORK_PREFERENCE}</li>
      * <li>{@link android.provider.Settings.Global#WIFI_ON}. Use
      * {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)} instead.</li>
+     * <li>{@link android.provider.Settings.Global#WIFI_SLEEP_POLICY}. No longer has effect.</li>
      * </ul>
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
@@ -8989,6 +9002,11 @@
     }
 
     /**
+     * This method is mostly deprecated.
+     * Most of the settings that still have an effect have dedicated setter methods
+     * (e.g. {@link #setLocationEnabled}) or user restrictions.
+     * <p>
+     *
      * Called by profile or device owners to update {@link android.provider.Settings.Secure}
      * settings. Validation that the value of the setting is in the correct form for the setting
      * type should be performed by the caller.
@@ -9001,7 +9019,7 @@
      * <p>
      * A device owner can additionally update the following settings:
      * <ul>
-     * <li>{@link android.provider.Settings.Secure#LOCATION_MODE}</li>
+     * <li>{@link android.provider.Settings.Secure#LOCATION_MODE}, but see note below.</li>
      * </ul>
      *
      * <strong>Note: Starting from Android O, apps should no longer call this method with the
@@ -10355,19 +10373,23 @@
     }
 
     /**
-     * Indicates the entity that controls the device or profile owner. Two users/profiles are
-     * affiliated if the set of ids set by their device or profile owners intersect.
+     * Indicates the entity that controls the device. Two users are
+     * affiliated if the set of ids set by the device owner and the admin of the secondary user.
      *
-     * <p>A user/profile that is affiliated with the device owner user is considered to be
+     * <p>A user that is affiliated with the device owner user is considered to be
      * affiliated with the device.
      *
      * <p><strong>Note:</strong> Features that depend on user affiliation (such as security logging
-     * or {@link #bindDeviceAdminServiceAsUser}) won't be available when a secondary user or profile
+     * or {@link #bindDeviceAdminServiceAsUser}) won't be available when a secondary user
      * is created, until it becomes affiliated. Therefore it is recommended that the appropriate
-     * affiliation ids are set by its profile owner as soon as possible after the user/profile is
+     * affiliation ids are set by its owner as soon as possible after the user is
      * created.
+     * <p>
+     * Note: This method used to be available for affiliating device owner and profile
+     * owner. However, since Android 11, this combination is not possible. This method is now
+     * only useful for affiliating the primary user with managed secondary users.
      *
-     * @param admin Which profile or device owner this request is associated with.
+     * @param admin Which device owner, or owner of secondary user, this request is associated with.
      * @param ids A set of opaque non-empty affiliation ids.
      *
      * @throws IllegalArgumentException if {@code ids} is null or contains an empty string.
@@ -10399,10 +10421,10 @@
     }
 
     /**
-     * Returns whether this user/profile is affiliated with the device.
+     * Returns whether this user is affiliated with the device.
      * <p>
      * By definition, the user that the device owner runs on is always affiliated with the device.
-     * Any other user/profile is considered affiliated with the device if the set specified by its
+     * Any other user is considered affiliated with the device if the set specified by its
      * profile owner via {@link #setAffiliationIds} intersects with the device owner's.
      * @see #setAffiliationIds
      */
@@ -10706,14 +10728,18 @@
     }
 
     /**
-     * Called by a device owner to bind to a service from a profile owner or vice versa.
-     * See {@link #getBindDeviceAdminTargetUsers} for a definition of which
-     * device/profile owners are allowed to bind to services of another profile/device owner.
+     * Called by a device owner to bind to a service from a secondary managed user or vice versa.
+     * See {@link #getBindDeviceAdminTargetUsers} for the pre-requirements of a
+     * device owner to bind to services of another managed user.
      * <p>
      * The service must be protected by {@link android.Manifest.permission#BIND_DEVICE_ADMIN}.
      * Note that the {@link Context} used to obtain this
      * {@link DevicePolicyManager} instance via {@link Context#getSystemService(Class)} will be used
      * to bind to the {@link android.app.Service}.
+     * <p>
+     * Note: This method used to be available for communication between device owner and profile
+     * owner. However, since Android 11, this combination is not possible. This method is now
+     * only useful for communication between device owner and managed secondary users.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param serviceIntent Identifies the service to connect to.  The Intent must specify either an
@@ -10751,14 +10777,11 @@
     }
 
     /**
-     * Returns the list of target users that the calling device or profile owner can use when
-     * calling {@link #bindDeviceAdminServiceAsUser}.
+     * Returns the list of target users that the calling device owner or owner of secondary user
+     * can use when calling {@link #bindDeviceAdminServiceAsUser}.
      * <p>
-     * A device owner can bind to a service from a profile owner and vice versa, provided that:
-     * <ul>
-     * <li>Both belong to the same package name.
-     * <li>Both users are affiliated. See {@link #setAffiliationIds}.
-     * </ul>
+     * A device owner can bind to a service from a secondary managed user and vice versa, provided
+     * that both users are affiliated. See {@link #setAffiliationIds}.
      */
     public @NonNull List<UserHandle> getBindDeviceAdminTargetUsers(@NonNull ComponentName admin) {
         throwIfParentInstance("getBindDeviceAdminTargetUsers");
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 66bfcbd..6ae68fc 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1229,10 +1229,11 @@
     public boolean factoryReset() {
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.factoryReset();
+            if (mService != null && mService.factoryReset()
+                    && mManagerService != null && mManagerService.onFactoryReset()) {
+                return true;
             }
-            Log.e(TAG, "factoryReset(): IBluetooth Service is null");
+            Log.e(TAG, "factoryReset(): Setting persist.bluetooth.factoryreset to retry later");
             SystemProperties.set("persist.bluetooth.factoryreset", "true");
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
diff --git a/core/java/android/companion/WifiDeviceFilter.java b/core/java/android/companion/WifiDeviceFilter.java
index 58bf874..8b48f4c 100644
--- a/core/java/android/companion/WifiDeviceFilter.java
+++ b/core/java/android/companion/WifiDeviceFilter.java
@@ -51,6 +51,7 @@
      * expression will be shown
      */
     @DataClass.ParcelWith(Parcelling.BuiltIn.ForPattern.class)
+    @DataClass.MaySetToNull
     private @Nullable Pattern mNamePattern = null;
 
     /**
@@ -86,7 +87,7 @@
 
 
 
-    // Code below generated by codegen v1.0.11.
+    // Code below generated by codegen v1.0.15.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -273,7 +274,7 @@
          * If set, only devices with BSSID matching the given one will be shown
          */
         @DataClass.Generated.Member
-        public @NonNull Builder setBssid(@Nullable MacAddress value) {
+        public @NonNull Builder setBssid(@NonNull MacAddress value) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x2;
             mBssid = value;
@@ -322,11 +323,15 @@
     }
 
     @DataClass.Generated(
-            time = 1571960300742L,
-            codegenVersion = "1.0.11",
+            time = 1582688421965L,
+            codegenVersion = "1.0.15",
             sourceFile = "frameworks/base/core/java/android/companion/WifiDeviceFilter.java",
-            inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.Nullable java.util.regex.Pattern mNamePattern\nprivate @android.annotation.Nullable android.net.MacAddress mBssid\nprivate @android.annotation.NonNull android.net.MacAddress mBssidMask\npublic @java.lang.Override boolean matches(android.net.wifi.ScanResult)\npublic @java.lang.Override java.lang.String getDeviceDisplayName(android.net.wifi.ScanResult)\npublic @java.lang.Override int getMediumType()\nclass WifiDeviceFilter extends java.lang.Object implements [android.companion.DeviceFilter<android.net.wifi.ScanResult>]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=false, genBuilder=true, genEqualsHashCode=true, genHiddenGetters=true)")
+            inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @com.android.internal.util.DataClass.MaySetToNull @android.annotation.Nullable java.util.regex.Pattern mNamePattern\nprivate @android.annotation.Nullable android.net.MacAddress mBssid\nprivate @android.annotation.NonNull android.net.MacAddress mBssidMask\npublic @java.lang.Override boolean matches(android.net.wifi.ScanResult)\npublic @java.lang.Override java.lang.String getDeviceDisplayName(android.net.wifi.ScanResult)\npublic @java.lang.Override int getMediumType()\nclass WifiDeviceFilter extends java.lang.Object implements [android.companion.DeviceFilter<android.net.wifi.ScanResult>]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=false, genBuilder=true, genEqualsHashCode=true, genHiddenGetters=true)")
     @Deprecated
     private void __metadata() {}
 
+
+    //@formatter:on
+    // End of generated code
+
 }
diff --git a/core/java/android/content/ApexContext.java b/core/java/android/content/ApexEnvironment.java
similarity index 80%
rename from core/java/android/content/ApexContext.java
rename to core/java/android/content/ApexEnvironment.java
index fe5cedc..b4cc3c2 100644
--- a/core/java/android/content/ApexContext.java
+++ b/core/java/android/content/ApexEnvironment.java
@@ -30,29 +30,29 @@
  * @hide
  */
 @SystemApi
-public class ApexContext {
+public class ApexEnvironment {
 
     private static final String APEX_DATA = "apexdata";
 
     /**
-     * Returns an ApexContext instance for the APEX with the provided {@code apexModuleName}.
+     * Returns an ApexEnvironment instance for the APEX with the provided {@code apexModuleName}.
      *
-     * <p>To preserve the safety and integrity of APEX modules, you must only obtain the ApexContext
-     * for your specific APEX, and you <em>must never</em> attempt to obtain an ApexContext for
-     * another APEX.  Any coordination between APEXs must be performed through well-defined
-     * interfaces; attempting to directly read or write raw files belonging to another APEX will
-     * violate the hermetic storage requirements placed upon each module.
+     * <p>To preserve the safety and integrity of APEX modules, you must only obtain the
+     * ApexEnvironment for your specific APEX, and you <em>must never</em> attempt to obtain an
+     * ApexEnvironment for another APEX.  Any coordination between APEXs must be performed through
+     * well-defined interfaces; attempting to directly read or write raw files belonging to another
+     * APEX will violate the hermetic storage requirements placed upon each module.
      */
     @NonNull
-    public static ApexContext getApexContext(@NonNull String apexModuleName) {
+    public static ApexEnvironment getApexEnvironment(@NonNull String apexModuleName) {
         Objects.requireNonNull(apexModuleName, "apexModuleName cannot be null");
         //TODO(b/141148175): Check that apexModuleName is an actual APEX name
-        return new ApexContext(apexModuleName);
+        return new ApexEnvironment(apexModuleName);
     }
 
     private final String mApexModuleName;
 
-    private ApexContext(String apexModuleName) {
+    private ApexEnvironment(String apexModuleName) {
         mApexModuleName = apexModuleName;
     }
 
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index ae786aa..31e1fc8 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -23,6 +23,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.annotation.UserIdInt;
@@ -82,6 +83,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.Objects;
 import java.util.Random;
@@ -2669,6 +2671,15 @@
                 ContentProvider.getUserIdFromUri(uri, mContext.getUserId()));
     }
 
+    /** @removed */
+    @Deprecated
+    public void notifyChange(@NonNull Iterable<Uri> uris, @Nullable ContentObserver observer,
+            @NotifyFlags int flags) {
+        final Collection<Uri> asCollection = new ArrayList<>();
+        uris.forEach(asCollection::add);
+        notifyChange(asCollection, observer, flags);
+    }
+
     /**
      * Notify registered observers that several rows have been updated.
      * <p>
@@ -2693,7 +2704,7 @@
      * @param flags Flags such as {@link #NOTIFY_SYNC_TO_NETWORK} or
      *            {@link #NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS}.
      */
-    public void notifyChange(@NonNull Iterable<Uri> uris, @Nullable ContentObserver observer,
+    public void notifyChange(@NonNull Collection<Uri> uris, @Nullable ContentObserver observer,
             @NotifyFlags int flags) {
         Objects.requireNonNull(uris, "uris");
 
@@ -4014,6 +4025,10 @@
      * @hide
      */
     @SystemApi
+    @TestApi
+    // We can't accept an already-opened FD here, since these methods are
+    // rewriting actual filesystem paths
+    @SuppressLint("StreamFiles")
     public static @NonNull Uri decodeFromFile(@NonNull File file) {
         return translateDeprecatedDataPath(file.getAbsolutePath());
     }
@@ -4030,6 +4045,10 @@
      * @hide
      */
     @SystemApi
+    @TestApi
+    // We can't accept an already-opened FD here, since these methods are
+    // rewriting actual filesystem paths
+    @SuppressLint("StreamFiles")
     public static @NonNull File encodeToFile(@NonNull Uri uri) {
         return new File(translateDeprecatedDataPath(uri));
     }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index c6f6972..4bb7346 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1881,6 +1881,20 @@
             "android.intent.action.MANAGE_PERMISSIONS";
 
     /**
+     * Activity action: Launch UI to manage auto-revoke state.
+     * <p>
+     * Input: {@link #EXTRA_PACKAGE_NAME} specifies the package whose
+     * auto-revoke state will be reviewed (mandatory).
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_AUTO_REVOKE_PERMISSIONS =
+            "android.intent.action.AUTO_REVOKE_PERMISSIONS";
+
+    /**
      * Activity action: Launch UI to review permissions for an app.
      * The system uses this intent if permission review for apps not
      * supporting the new runtime permissions model is enabled. In
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 61128f2..f19ab01 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -274,6 +274,9 @@
      */
     public static final String SCHEME_HTTPS = "https";
 
+    /** The value to indicate a wildcard for incoming match arguments. */
+    private static final String WILDCARD = "*";
+
     private int mPriority;
     @UnsupportedAppUsage
     private int mOrder;
@@ -758,6 +761,17 @@
      * @return True if the action is listed in the filter.
      */
     public final boolean matchAction(String action) {
+        return matchAction(action, false);
+    }
+
+    /**
+     * Variant of {@link #matchAction(String)} that allows a wildcard for the provided action.
+     * @param wildcardSupported if true, will allow action to use wildcards
+     */
+    private boolean matchAction(String action, boolean wildcardSupported) {
+        if (wildcardSupported && !mActions.isEmpty() && WILDCARD.equals(action)) {
+            return true;
+        }
         return hasAction(action);
     }
 
@@ -1120,20 +1134,33 @@
          * {@link IntentFilter#MATCH_CATEGORY_HOST}.
          */
         public int match(Uri data) {
+            return match(data, false);
+        }
+
+        /**
+         * Variant of {@link #match(Uri)} that supports wildcards on the scheme, host and
+         * path of the provided {@link Uri}
+         *
+         * @param wildcardSupported if true, will allow parameters to use wildcards
+         * @hide
+         */
+        public int match(Uri data, boolean wildcardSupported) {
             String host = data.getHost();
             if (host == null) {
                 return NO_MATCH_DATA;
             }
             if (false) Log.v("IntentFilter",
                     "Match host " + host + ": " + mHost);
-            if (mWild) {
-                if (host.length() < mHost.length()) {
+            if (!wildcardSupported || !WILDCARD.equals(host)) {
+                if (mWild) {
+                    if (host.length() < mHost.length()) {
+                        return NO_MATCH_DATA;
+                    }
+                    host = host.substring(host.length() - mHost.length());
+                }
+                if (host.compareToIgnoreCase(mHost) != 0) {
                     return NO_MATCH_DATA;
                 }
-                host = host.substring(host.length()-mHost.length());
-            }
-            if (host.compareToIgnoreCase(mHost) != 0) {
-                return NO_MATCH_DATA;
             }
             if (mPort >= 0) {
                 if (mPort != data.getPort()) {
@@ -1207,9 +1234,21 @@
      *         filter.
      */
     public final boolean hasDataSchemeSpecificPart(String data) {
+        return hasDataSchemeSpecificPart(data, false);
+    }
+
+    /**
+     * Variant of {@link #hasDataSchemeSpecificPart(String)} that supports wildcards on the provided
+     * ssp.
+     * @param supportWildcards if true, will allow parameters to use wildcards
+     */
+    private boolean hasDataSchemeSpecificPart(String data, boolean supportWildcards) {
         if (mDataSchemeSpecificParts == null) {
             return false;
         }
+        if (supportWildcards && WILDCARD.equals(data) && mDataSchemeSpecificParts.size() > 0) {
+            return true;
+        }
         final int numDataSchemeSpecificParts = mDataSchemeSpecificParts.size();
         for (int i = 0; i < numDataSchemeSpecificParts; i++) {
             final PatternMatcher pe = mDataSchemeSpecificParts.get(i);
@@ -1388,9 +1427,21 @@
      *         filter.
      */
     public final boolean hasDataPath(String data) {
+        return hasDataPath(data, false);
+    }
+
+    /**
+     * Variant of {@link #hasDataPath(String)} that supports wildcards on the provided scheme, host,
+     * and path.
+     * @param wildcardSupported if true, will allow parameters to use wildcards
+     */
+    private boolean hasDataPath(String data, boolean wildcardSupported) {
         if (mDataPaths == null) {
             return false;
         }
+        if (wildcardSupported && WILDCARD.equals(data)) {
+            return true;
+        }
         final int numDataPaths = mDataPaths.size();
         for (int i = 0; i < numDataPaths; i++) {
             final PatternMatcher pe = mDataPaths.get(i);
@@ -1435,13 +1486,24 @@
      * {@link #MATCH_CATEGORY_PORT}, {@link #NO_MATCH_DATA}.
      */
     public final int matchDataAuthority(Uri data) {
-        if (mDataAuthorities == null || data == null) {
+        return matchDataAuthority(data, false);
+    }
+
+    /**
+     * Variant of {@link #matchDataAuthority(Uri)} that allows wildcard matching of the provided
+     * authority.
+     *
+     * @param wildcardSupported if true, will allow parameters to use wildcards
+     * @hide
+     */
+    public final int matchDataAuthority(Uri data, boolean wildcardSupported) {
+        if (data == null || mDataAuthorities == null) {
             return NO_MATCH_DATA;
         }
         final int numDataAuthorities = mDataAuthorities.size();
         for (int i = 0; i < numDataAuthorities; i++) {
             final AuthorityEntry ae = mDataAuthorities.get(i);
-            int match = ae.match(data);
+            int match = ae.match(data, wildcardSupported);
             if (match >= 0) {
                 return match;
             }
@@ -1488,6 +1550,15 @@
      * @see #match
      */
     public final int matchData(String type, String scheme, Uri data) {
+        return matchData(type, scheme, data, false);
+    }
+
+    /**
+     * Variant of {@link #matchData(String, String, Uri)} that allows wildcard matching
+     * of the provided type, scheme, host and path parameters.
+     * @param wildcardSupported if true, will allow parameters to use wildcards
+     */
+    private int matchData(String type, String scheme, Uri data, boolean wildcardSupported) {
         final ArrayList<String> types = mDataTypes;
         final ArrayList<String> schemes = mDataSchemes;
 
@@ -1499,7 +1570,8 @@
         }
 
         if (schemes != null) {
-            if (schemes.contains(scheme != null ? scheme : "")) {
+            if (schemes.contains(scheme != null ? scheme : "")
+                    || wildcardSupported && WILDCARD.equals(scheme)) {
                 match = MATCH_CATEGORY_SCHEME;
             } else {
                 return NO_MATCH_DATA;
@@ -1507,19 +1579,19 @@
 
             final ArrayList<PatternMatcher> schemeSpecificParts = mDataSchemeSpecificParts;
             if (schemeSpecificParts != null && data != null) {
-                match = hasDataSchemeSpecificPart(data.getSchemeSpecificPart())
+                match = hasDataSchemeSpecificPart(data.getSchemeSpecificPart(), wildcardSupported)
                         ? MATCH_CATEGORY_SCHEME_SPECIFIC_PART : NO_MATCH_DATA;
             }
             if (match != MATCH_CATEGORY_SCHEME_SPECIFIC_PART) {
                 // If there isn't any matching ssp, we need to match an authority.
                 final ArrayList<AuthorityEntry> authorities = mDataAuthorities;
                 if (authorities != null) {
-                    int authMatch = matchDataAuthority(data);
+                    int authMatch = matchDataAuthority(data, wildcardSupported);
                     if (authMatch >= 0) {
                         final ArrayList<PatternMatcher> paths = mDataPaths;
                         if (paths == null) {
                             match = authMatch;
-                        } else if (hasDataPath(data.getPath())) {
+                        } else if (hasDataPath(data.getPath(), wildcardSupported)) {
                             match = MATCH_CATEGORY_PATH;
                         } else {
                             return NO_MATCH_DATA;
@@ -1541,7 +1613,8 @@
             // to force everyone to say they handle content: or file: URIs.
             if (scheme != null && !"".equals(scheme)
                     && !"content".equals(scheme)
-                    && !"file".equals(scheme)) {
+                    && !"file".equals(scheme)
+                    && !(wildcardSupported && WILDCARD.equals(scheme))) {
                 return NO_MATCH_DATA;
             }
         }
@@ -1701,13 +1774,23 @@
      */
     public final int match(String action, String type, String scheme,
             Uri data, Set<String> categories, String logTag) {
-        if (action != null && !matchAction(action)) {
+        return match(action, type, scheme, data, categories, logTag, false /*supportWildcards*/);
+    }
+
+    /**
+     * Variant of {@link #match(ContentResolver, Intent, boolean, String)} that supports wildcards
+     * in the action, type, scheme, host and path.
+     * @hide if true, will allow supported parameters to use wildcards to match this filter
+     */
+    public final int match(String action, String type, String scheme,
+            Uri data, Set<String> categories, String logTag, boolean supportWildcards) {
+        if (action != null && !matchAction(action, supportWildcards)) {
             if (false) Log.v(
                 logTag, "No matching action " + action + " for " + this);
             return NO_MATCH_ACTION;
         }
 
-        int dataMatch = matchData(type, scheme, data);
+        int dataMatch = matchData(type, scheme, data, supportWildcards);
         if (dataMatch < 0) {
             if (false) {
                 if (dataMatch == NO_MATCH_TYPE) {
diff --git a/core/java/android/content/pm/InstallationFile.java b/core/java/android/content/pm/InstallationFile.java
index edc04c9..de761ad 100644
--- a/core/java/android/content/pm/InstallationFile.java
+++ b/core/java/android/content/pm/InstallationFile.java
@@ -21,13 +21,25 @@
 import android.annotation.SystemApi;
 
 /**
- * Defines the properties of a file in an installation session.
+ * Definition of a file in a streaming installation session.
+ * You can use this class to retrieve the information of such a file, such as its name, size and
+ * metadata. These file attributes will be consistent with those used in:
+ * {@code PackageInstaller.Session#addFile}, when the file was first added into the session.
+ *
+ * WARNING: This is a system API to aid internal development.
+ * Use at your own risk. It will change or be removed without warning.
+ *
+ * @see android.content.pm.PackageInstaller.Session#addFile
  * @hide
  */
 @SystemApi
 public final class InstallationFile {
     private final @NonNull InstallationFileParcel mParcel;
 
+    /**
+     * Constructor, internal use only
+     * @hide
+     */
     public InstallationFile(@PackageInstaller.FileLocation int location, @NonNull String name,
             long lengthBytes, @Nullable byte[] metadata, @Nullable byte[] signature) {
         mParcel = new InstallationFileParcel();
@@ -38,22 +50,44 @@
         mParcel.signature = signature;
     }
 
+    /**
+     * Installation Location of this file. Can be one of the following three locations:
+     * <ul>
+     *     <li>(1) {@code PackageInstaller.LOCATION_DATA_APP}</li>
+     *     <li>(2) {@code PackageInstaller.LOCATION_MEDIA_OBB}</li>
+     *     <li>(3) {@code PackageInstaller.LOCATION_MEDIA_DATA}</li>
+     * </ul>
+     * @see android.content.pm.PackageInstaller
+     * @return Integer that denotes the installation location of the file.
+     */
     public @PackageInstaller.FileLocation int getLocation() {
         return mParcel.location;
     }
 
+    /**
+     * @return Name of the file.
+     */
     public @NonNull String getName() {
         return mParcel.name;
     }
 
+    /**
+     * @return File size in bytes.
+     */
     public long getLengthBytes() {
         return mParcel.size;
     }
 
+    /**
+     * @return File metadata as a byte array
+     */
     public @Nullable byte[] getMetadata() {
         return mParcel.metadata;
     }
 
+    /**
+     * @return File signature info as a byte array
+     */
     public @Nullable byte[] getSignature() {
         return mParcel.signature;
     }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 4065b11..7600a08 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3403,29 +3403,12 @@
     public static final int FLAG_PERMISSION_ONE_TIME = 1 << 16;
 
     /**
-     * Permission flag: The permission is whitelisted to not be auto-revoked when app goes unused.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED = 1 << 17;
-
-    /**
-     * Permission flag: Whether {@link #FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED} state was set by
-     * user.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int FLAG_PERMISSION_AUTO_REVOKE_USER_SET = 1 << 18;
-
-    /**
      * Permission flag: Whether permission was revoked by auto-revoke.
      *
      * @hide
      */
     @SystemApi
-    public static final int FLAG_PERMISSION_AUTO_REVOKED = 1 << 20;
+    public static final int FLAG_PERMISSION_AUTO_REVOKED = 1 << 17;
 
     /**
      * Permission flags: Reserved for use by the permission controller.
@@ -3479,8 +3462,6 @@
             | FLAG_PERMISSION_GRANTED_BY_ROLE
             | FLAG_PERMISSION_REVOKED_COMPAT
             | FLAG_PERMISSION_ONE_TIME
-            | FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED
-            | FLAG_PERMISSION_AUTO_REVOKE_USER_SET
             | FLAG_PERMISSION_AUTO_REVOKED;
 
     /**
@@ -4305,8 +4286,6 @@
             FLAG_PERMISSION_GRANTED_BY_ROLE,
             FLAG_PERMISSION_REVOKED_COMPAT,
             FLAG_PERMISSION_ONE_TIME,
-            FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED,
-            FLAG_PERMISSION_AUTO_REVOKE_USER_SET,
             FLAG_PERMISSION_AUTO_REVOKED
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -7474,8 +7453,6 @@
             case FLAG_PERMISSION_GRANTED_BY_ROLE: return "GRANTED_BY_ROLE";
             case FLAG_PERMISSION_REVOKED_COMPAT: return "REVOKED_COMPAT";
             case FLAG_PERMISSION_ONE_TIME: return "ONE_TIME";
-            case FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED: return "AUTO_REVOKE_IF_UNUSED";
-            case FLAG_PERMISSION_AUTO_REVOKE_USER_SET: return "AUTO_REVOKE_USER_SET";
             case FLAG_PERMISSION_AUTO_REVOKED: return "AUTO_REVOKED";
             default: return Integer.toString(flag);
         }
diff --git a/core/java/android/database/ContentObserver.java b/core/java/android/database/ContentObserver.java
index ede264d..578d53b 100644
--- a/core/java/android/database/ContentObserver.java
+++ b/core/java/android/database/ContentObserver.java
@@ -19,6 +19,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentResolver.NotifyFlags;
 import android.net.Uri;
@@ -26,12 +29,26 @@
 import android.os.UserHandle;
 
 import java.util.Arrays;
+import java.util.Collection;
 
 /**
  * Receives call backs for changes to content.
  * Must be implemented by objects which are added to a {@link ContentObservable}.
  */
 public abstract class ContentObserver {
+    /**
+     * Starting in {@link android.os.Build.VERSION_CODES#R}, there is a new
+     * public API overload {@link #onChange(boolean, Uri, int)} that delivers a
+     * {@code int flags} argument.
+     * <p>
+     * Some apps may be relying on a previous hidden API that delivered a
+     * {@code int userId} argument, and this change is used to control delivery
+     * of the new {@code int flags} argument in its place.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion=android.os.Build.VERSION_CODES.Q)
+    private static final long ADD_CONTENT_OBSERVER_FLAGS = 150939131L;
+
     private final Object mLock = new Object();
     private Transport mTransport; // guarded by mLock
 
@@ -164,16 +181,26 @@
      * @param uris The Uris of the changed content.
      * @param flags Flags indicating details about this change.
      */
-    public void onChange(boolean selfChange, @NonNull Iterable<Uri> uris, @NotifyFlags int flags) {
+    public void onChange(boolean selfChange, @NonNull Collection<Uri> uris,
+            @NotifyFlags int flags) {
         for (Uri uri : uris) {
             onChange(selfChange, uri, flags);
         }
     }
 
     /** @hide */
-    public void onChange(boolean selfChange, @NonNull Iterable<Uri> uris, @NotifyFlags int flags,
-            @UserIdInt int userId) {
-        onChange(selfChange, uris, flags);
+    public void onChange(boolean selfChange, @NonNull Collection<Uri> uris,
+            @NotifyFlags int flags, @UserIdInt int userId) {
+        // There are dozens of people relying on the hidden API inside the
+        // system UID, so hard-code the old behavior for all of them; for
+        // everyone else we gate based on a specific change
+        if (!CompatChanges.isChangeEnabled(ADD_CONTENT_OBSERVER_FLAGS)
+                || android.os.Process.myUid() == android.os.Process.SYSTEM_UID) {
+            // Deliver userId through argument to preserve hidden API behavior
+            onChange(selfChange, uris, userId);
+        } else {
+            onChange(selfChange, uris, flags);
+        }
     }
 
     /**
@@ -186,7 +213,7 @@
      *
      * @deprecated Callers should migrate towards using a richer overload that
      *             provides more details about the change, such as
-     *             {@link #dispatchChange(boolean, Iterable, int)}.
+     *             {@link #dispatchChange(boolean, Collection, int)}.
      */
     @Deprecated
     public final void dispatchChange(boolean selfChange) {
@@ -206,7 +233,7 @@
      * @param uri The Uri of the changed content.
      */
     public final void dispatchChange(boolean selfChange, @Nullable Uri uri) {
-        dispatchChange(selfChange, Arrays.asList(uri), 0, UserHandle.getCallingUserId());
+        dispatchChange(selfChange, uri, 0);
     }
 
     /**
@@ -224,7 +251,7 @@
      */
     public final void dispatchChange(boolean selfChange, @Nullable Uri uri,
             @NotifyFlags int flags) {
-        dispatchChange(selfChange, Arrays.asList(uri), flags, UserHandle.getCallingUserId());
+        dispatchChange(selfChange, Arrays.asList(uri), flags);
     }
 
     /**
@@ -240,13 +267,13 @@
      * @param uris The Uri of the changed content.
      * @param flags Flags indicating details about this change.
      */
-    public final void dispatchChange(boolean selfChange, @NonNull Iterable<Uri> uris,
+    public final void dispatchChange(boolean selfChange, @NonNull Collection<Uri> uris,
             @NotifyFlags int flags) {
         dispatchChange(selfChange, uris, flags, UserHandle.getCallingUserId());
     }
 
     /** @hide */
-    public final void dispatchChange(boolean selfChange, @NonNull Iterable<Uri> uris,
+    public final void dispatchChange(boolean selfChange, @NonNull Collection<Uri> uris,
             @NotifyFlags int flags, @UserIdInt int userId) {
         if (mHandler == null) {
             onChange(selfChange, uris, flags, userId);
diff --git a/core/java/android/database/CursorToBulkCursorAdaptor.java b/core/java/android/database/CursorToBulkCursorAdaptor.java
index 1855dd2..ce86807 100644
--- a/core/java/android/database/CursorToBulkCursorAdaptor.java
+++ b/core/java/android/database/CursorToBulkCursorAdaptor.java
@@ -20,10 +20,12 @@
 import android.annotation.UserIdInt;
 import android.content.ContentResolver.NotifyFlags;
 import android.net.Uri;
-import android.os.*;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
 
 import java.util.ArrayList;
-
+import java.util.Collection;
 
 /**
  * Wraps a BulkCursor around an existing Cursor making it remotable.
@@ -81,7 +83,7 @@
         }
 
         @Override
-        public void onChange(boolean selfChange, @NonNull Iterable<Uri> uris,
+        public void onChange(boolean selfChange, @NonNull Collection<Uri> uris,
                 @NotifyFlags int flags, @UserIdInt int userId) {
             // Since we deliver changes from the most-specific to least-specific
             // overloads, we only need to redirect from the most-specific local
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 85ef4a3..a091f84 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -846,6 +846,33 @@
                 @NonNull String physicalCameraId) {
             // default empty implementation
         }
+
+        /**
+         * A camera device has been opened by an application.
+         *
+         * <p>The default implementation of this method does nothing.</p>
+         *
+         * @param cameraId The unique identifier of the new camera.
+         * @param packageId The package Id of the application opening the camera.
+         *
+         * @see #onCameraClosed
+         */
+        /** @hide */
+        public void onCameraOpened(@NonNull String cameraId, @NonNull String packageId) {
+            // default empty implementation
+        }
+
+        /**
+         * A previously-opened camera has been closed.
+         *
+         * <p>The default implementation of this method does nothing.</p>
+         *
+         * @param cameraId The unique identifier of the closed camera.
+         */
+        /** @hide */
+        public void onCameraClosed(@NonNull String cameraId) {
+            // default empty implementation
+        }
     }
 
     /**
@@ -1276,6 +1303,12 @@
                 }
                 @Override
                 public void onCameraAccessPrioritiesChanged() {
+                }
+                @Override
+                public void onCameraOpened(String id, String clientPackageId) {
+                }
+                @Override
+                public void onCameraClosed(String id) {
                 }};
 
             String[] cameraIds = null;
@@ -1503,6 +1536,38 @@
             }
         }
 
+        private void postSingleCameraOpenedUpdate(final AvailabilityCallback callback,
+                final Executor executor, final String id, final String packageId) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                executor.execute(
+                    new Runnable() {
+                        @Override
+                        public void run() {
+                            callback.onCameraOpened(id, packageId);
+                        }
+                    });
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        private void postSingleCameraClosedUpdate(final AvailabilityCallback callback,
+                final Executor executor, final String id) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                executor.execute(
+                    new Runnable() {
+                        @Override
+                        public void run() {
+                            callback.onCameraClosed(id);
+                        }
+                    });
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
         private void postSingleUpdate(final AvailabilityCallback callback, final Executor executor,
                 final String id, final String physicalId, final int status) {
             if (isAvailable(status)) {
@@ -1846,6 +1911,32 @@
             }
         }
 
+        @Override
+        public void onCameraOpened(String cameraId, String clientPackageId) {
+            synchronized (mLock) {
+                final int callbackCount = mCallbackMap.size();
+                for (int i = 0; i < callbackCount; i++) {
+                    Executor executor = mCallbackMap.valueAt(i);
+                    final AvailabilityCallback callback = mCallbackMap.keyAt(i);
+
+                    postSingleCameraOpenedUpdate(callback, executor, cameraId, clientPackageId);
+                }
+            }
+        }
+
+        @Override
+        public void onCameraClosed(String cameraId) {
+            synchronized (mLock) {
+                final int callbackCount = mCallbackMap.size();
+                for (int i = 0; i < callbackCount; i++) {
+                    Executor executor = mCallbackMap.valueAt(i);
+                    final AvailabilityCallback callback = mCallbackMap.keyAt(i);
+
+                    postSingleCameraClosedUpdate(callback, executor, cameraId);
+                }
+            }
+        }
+
         /**
          * Try to connect to camera service after some delay if any client registered camera
          * availability callback or torch status callback.
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 2a71da8..9145142 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -129,10 +129,6 @@
             super(mac);
         }
 
-        public CryptoObject(@NonNull IdentityCredential credential) {
-            super(credential);
-        }
-
         /**
          * Get {@link Signature} object.
          * @return {@link Signature} object or null if this doesn't contain one.
@@ -160,8 +156,9 @@
         /**
          * Get {@link IdentityCredential} object.
          * @return {@link IdentityCredential} object or null if this doesn't contain one.
+         * @hide
          */
-        public @Nullable IdentityCredential getIdentityCredential() {
+        public IdentityCredential getIdentityCredential() {
             return super.getIdentityCredential();
         }
     }
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/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/net/EthernetManager.java b/core/java/android/net/EthernetManager.java
index 139f5be..83b5f63 100644
--- a/core/java/android/net/EthernetManager.java
+++ b/core/java/android/net/EthernetManager.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
@@ -248,6 +249,10 @@
      * interface, the existing interface will be used.
      * @param callback A callback to be called once the request has been fulfilled.
      */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_STACK,
+            android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
+    })
     @NonNull
     public TetheredInterfaceRequest requestTetheredInterface(@NonNull final Executor executor,
             @NonNull final TetheredInterfaceCallback callback) {
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index dd3f9fd..20e5f24 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -526,7 +526,7 @@
         final AppOpsManager.PausedNotedAppOpsCollection prevCollection =
                 AppOpsManager.pauseNotedAppOpsCollection();
 
-        if ((flags & FLAG_ONEWAY) == 0 && AppOpsManager.isCollectingNotedAppOps()) {
+        if ((flags & FLAG_ONEWAY) == 0 && AppOpsManager.isListeningForOpNoted()) {
             flags |= FLAG_COLLECT_NOTED_APP_OPS;
         }
 
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 21a1e0f..f2fb5b2 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -39,6 +39,7 @@
 import java.util.Collection;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Provides access to environment variables.
@@ -1253,6 +1254,50 @@
                 uid, context.getOpPackageName()) == AppOpsManager.MODE_ALLOWED;
     }
 
+    /**
+     * Returns whether the calling app has All Files Access on the primary shared/external storage
+     * media.
+     * <p>Declaring the permission {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} isn't
+     * enough to gain the access.
+     * <p>To request access, use
+     * {@link android.provider.Settings#ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION}.
+     */
+    public static boolean isExternalStorageManager() {
+        final File externalDir = sCurrentUser.getExternalDirs()[0];
+        return isExternalStorageManager(externalDir);
+    }
+
+    /**
+     * Returns whether the calling app has All Files Access at the given {@code path}
+     * <p>Declaring the permission {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} isn't
+     * enough to gain the access.
+     * <p>To request access, use
+     * {@link android.provider.Settings#ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION}.
+     */
+    public static boolean isExternalStorageManager(@NonNull File path) {
+        final Context context = Objects.requireNonNull(AppGlobals.getInitialApplication());
+        String packageName = Objects.requireNonNull(context.getPackageName());
+        int uid = context.getApplicationInfo().uid;
+
+        final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
+        final int opMode =
+                appOps.checkOpNoThrow(AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE, uid, packageName);
+
+        switch (opMode) {
+            case AppOpsManager.MODE_DEFAULT:
+                return PackageManager.PERMISSION_GRANTED
+                        == context.checkPermission(
+                                Manifest.permission.MANAGE_EXTERNAL_STORAGE, Process.myPid(), uid);
+            case AppOpsManager.MODE_ALLOWED:
+                return true;
+            case AppOpsManager.MODE_ERRORED:
+            case AppOpsManager.MODE_IGNORED:
+                return false;
+            default:
+                throw new IllegalStateException("Unknown AppOpsManager mode " + opMode);
+        }
+    }
+
     static File getDirectory(String variableName, String defaultPath) {
         String path = System.getenv(variableName);
         return path == null ? new File(defaultPath) : new File(path);
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 47f2461..a10a456 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -22,6 +22,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentInterface;
 import android.content.ContentProvider;
@@ -1303,6 +1304,7 @@
      * {@hide}
      */
     @SystemApi
+    @TestApi
     public static @NonNull Uri setManageMode(@NonNull Uri uri) {
         Preconditions.checkNotNull(uri, "uri can not be null");
         return uri.buildUpon().appendQueryParameter(PARAM_MANAGE, "true").build();
@@ -1314,6 +1316,7 @@
      * {@hide}
      */
     @SystemApi
+    @TestApi
     public static boolean isManageMode(@NonNull Uri uri) {
         Preconditions.checkNotNull(uri, "uri can not be null");
         return uri.getBooleanQueryParameter(PARAM_MANAGE, false);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 641de4a..084f37f 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
 
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 97f7533..03b38ab 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -25,6 +25,7 @@
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -3734,7 +3735,6 @@
          * @deprecated this column is no longer supported, use {@link #NETWORK_TYPE_BITMASK} instead
          */
         @Deprecated
-        @SystemApi
         public static final String BEARER_BITMASK = "bearer_bitmask";
 
         /**
@@ -3784,7 +3784,6 @@
          * <p>Type: INTEGER</p>
          *@hide
          */
-        @SystemApi
         public static final String PROFILE_ID = "profile_id";
 
         /**
@@ -3985,7 +3984,6 @@
          *
          * @hide
          */
-        @SystemApi
         public static final String SKIP_464XLAT = "skip_464xlat";
 
         /**
@@ -3994,7 +3992,6 @@
          *
          * @hide
          */
-        @SystemApi
         public static final int SKIP_464XLAT_DEFAULT = -1;
 
         /**
@@ -4003,7 +4000,6 @@
          *
          * @hide
          */
-        @SystemApi
         public static final int SKIP_464XLAT_DISABLE = 0;
 
         /**
@@ -4012,7 +4008,6 @@
          *
          * @hide
          */
-        @SystemApi
         public static final int SKIP_464XLAT_ENABLE = 1;
 
 
@@ -4036,6 +4031,7 @@
          * @hide
          */
         @ChangeId
+        @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.Q)
         public static final long APN_READING_PERMISSION_CHANGE_ID = 124107808L;
     }
 
@@ -4390,6 +4386,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/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index e8e1223..06b5fa0 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -89,7 +89,7 @@
     private final @Nullable int[] mCancelIds;
     private final boolean mSupportsInlineSuggestions;
     // TODO(b/149240554): revert back to use ParceledListSlice after the bug is resolved.
-    private final @Nullable ArrayList<InlinePresentation> mInlineActions;
+    private final @Nullable ArrayList<InlineAction> mInlineActions;
 
     private FillResponse(@NonNull Builder builder) {
         mDatasets = (builder.mDatasets != null) ? new ParceledListSlice<>(builder.mDatasets) : null;
@@ -213,7 +213,7 @@
     }
 
     /** @hide */
-    public @Nullable List<InlinePresentation> getInlineActions() {
+    public @Nullable List<InlineAction> getInlineActions() {
         return mInlineActions;
     }
 
@@ -239,7 +239,7 @@
         private UserData mUserData;
         private int[] mCancelIds;
         private boolean mSupportsInlineSuggestions;
-        private ArrayList<InlinePresentation> mInlineActions;
+        private ArrayList<InlineAction> mInlineActions;
 
         /**
          * Triggers a custom UI before before autofilling the screen with any data set in this
@@ -656,15 +656,12 @@
         }
 
         /**
-         * Adds a new {@link InlinePresentation} to this response representing an action UI.
-         *
-         * <p> For example, the UI can be associated with an intent which can open an activity for
-         * the user to manage the Autofill provider settings.
+         * Adds a new {@link InlineAction} to this response representing an action UI.
          *
          * @return This builder.
          */
         @NonNull
-        public Builder addInlineAction(@NonNull InlinePresentation inlineAction) {
+        public Builder addInlineAction(@NonNull InlineAction inlineAction) {
             throwIfDestroyed();
             throwIfAuthenticationCalled();
             if (mInlineActions == null) {
@@ -881,10 +878,10 @@
             final int[] cancelIds = parcel.createIntArray();
             builder.setPresentationCancelIds(cancelIds);
 
-            final List<InlinePresentation> inlineActions = parcel.createTypedArrayList(
-                    InlinePresentation.CREATOR);
+            final List<InlineAction> inlineActions = parcel.createTypedArrayList(
+                    InlineAction.CREATOR);
             if (inlineActions != null) {
-                for (InlinePresentation inlineAction : inlineActions) {
+                for (InlineAction inlineAction : inlineActions) {
                     builder.addInlineAction(inlineAction);
                 }
             }
diff --git a/wifi/java/android/net/wifi/IScoreChangeCallback.aidl b/core/java/android/service/autofill/InlineAction.aidl
similarity index 71%
copy from wifi/java/android/net/wifi/IScoreChangeCallback.aidl
copy to core/java/android/service/autofill/InlineAction.aidl
index d691f41..7f85118 100644
--- a/wifi/java/android/net/wifi/IScoreChangeCallback.aidl
+++ b/core/java/android/service/autofill/InlineAction.aidl
@@ -14,16 +14,6 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+package android.service.autofill;
 
-/**
- * Interface for Wi-Fi score callback.
- *
- * @hide
- */
-oneway interface IScoreChangeCallback
-{
-    void onScoreChange(int sessionId, int score);
-
-    void onTriggerUpdateOfWifiUsabilityStats(int sessionId);
-}
+parcelable InlineAction;
diff --git a/core/java/android/service/autofill/InlineAction.java b/core/java/android/service/autofill/InlineAction.java
new file mode 100644
index 0000000..17c4b33
--- /dev/null
+++ b/core/java/android/service/autofill/InlineAction.java
@@ -0,0 +1,205 @@
+/*
+ * 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.service.autofill;
+
+import android.annotation.NonNull;
+import android.content.IntentSender;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Represents an inline action as part of the autofill/augmented autofill response.
+ *
+ * <p> It includes both the action intent and the UI presentation. For example, the UI can be
+ * associated with an intent which can open an activity for the user to manage the Autofill provider
+ * settings.
+ */
+@DataClass(
+        genToString = true,
+        genHiddenConstDefs = true,
+        genEqualsHashCode = true)
+public final class InlineAction implements Parcelable {
+
+    /**
+     * Representation of the inline action.
+     */
+    private final @NonNull InlinePresentation mInlinePresentation;
+
+    /**
+     * The associated intent which will be triggered when the action is selected. It will only be
+     * called by the OS.
+     */
+    private final @NonNull IntentSender mAction;
+
+
+
+    // 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/service/autofill/InlineAction.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new InlineAction.
+     *
+     * @param inlinePresentation
+     *   Representation of the inline action.
+     * @param action
+     *   The associated intent which will be triggered when the action is selected. It will only be
+     *   invoked by the OS.
+     */
+    @DataClass.Generated.Member
+    public InlineAction(
+            @NonNull InlinePresentation inlinePresentation,
+            @NonNull IntentSender action) {
+        this.mInlinePresentation = inlinePresentation;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mInlinePresentation);
+        this.mAction = action;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mAction);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * Representation of the inline action.
+     */
+    @DataClass.Generated.Member
+    public @NonNull InlinePresentation getInlinePresentation() {
+        return mInlinePresentation;
+    }
+
+    /**
+     * The associated intent which will be triggered when the action is selected. It will only be
+     * invoked by the OS.
+     */
+    @DataClass.Generated.Member
+    public @NonNull IntentSender getAction() {
+        return mAction;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "InlineAction { " +
+                "inlinePresentation = " + mInlinePresentation + ", " +
+                "action = " + mAction +
+        " }";
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@android.annotation.Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(InlineAction other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        InlineAction that = (InlineAction) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mInlinePresentation, that.mInlinePresentation)
+                && java.util.Objects.equals(mAction, that.mAction);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mInlinePresentation);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mAction);
+        return _hash;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeTypedObject(mInlinePresentation, flags);
+        dest.writeTypedObject(mAction, flags);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ InlineAction(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        InlinePresentation inlinePresentation = (InlinePresentation) in.readTypedObject(InlinePresentation.CREATOR);
+        IntentSender action = (IntentSender) in.readTypedObject(IntentSender.CREATOR);
+
+        this.mInlinePresentation = inlinePresentation;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mInlinePresentation);
+        this.mAction = action;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mAction);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<InlineAction> CREATOR
+            = new Parcelable.Creator<InlineAction>() {
+        @Override
+        public InlineAction[] newArray(int size) {
+            return new InlineAction[size];
+        }
+
+        @Override
+        public InlineAction createFromParcel(@NonNull android.os.Parcel in) {
+            return new InlineAction(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1583798182424L,
+            codegenVersion = "1.0.15",
+            sourceFile = "frameworks/base/core/java/android/service/autofill/InlineAction.java",
+            inputSignatures = "private final @android.annotation.NonNull android.service.autofill.InlinePresentation mInlinePresentation\nprivate final @android.annotation.NonNull android.content.IntentSender mAction\nclass InlineAction extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index fe792b1..ed27dd5 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -40,7 +40,7 @@
 import android.os.SystemClock;
 import android.service.autofill.Dataset;
 import android.service.autofill.FillEventHistory;
-import android.service.autofill.InlinePresentation;
+import android.service.autofill.InlineAction;
 import android.service.autofill.augmented.PresentationParams.SystemPopupPresentationParams;
 import android.util.Log;
 import android.util.Pair;
@@ -559,7 +559,7 @@
         }
 
         void reportResult(@Nullable List<Dataset> inlineSuggestionsData,
-                @Nullable List<InlinePresentation> inlineActions) {
+                @Nullable List<InlineAction> inlineActions) {
             try {
                 mCallback.onSuccess(inlineSuggestionsData, inlineActions);
             } catch (RemoteException e) {
diff --git a/core/java/android/service/autofill/augmented/FillResponse.java b/core/java/android/service/autofill/augmented/FillResponse.java
index b7fdf5a..f564b3b 100644
--- a/core/java/android/service/autofill/augmented/FillResponse.java
+++ b/core/java/android/service/autofill/augmented/FillResponse.java
@@ -21,7 +21,7 @@
 import android.annotation.TestApi;
 import android.os.Bundle;
 import android.service.autofill.Dataset;
-import android.service.autofill.InlinePresentation;
+import android.service.autofill.InlineAction;
 
 import com.android.internal.util.DataClass;
 
@@ -53,11 +53,10 @@
     private @Nullable List<Dataset> mInlineSuggestions;
 
     /**
-     * The {@link InlinePresentation}s representing the inline actions. Defaults to null if no
-     * inline actions are provided.
+     * Defaults to null if no inline actions are provided.
      */
     @DataClass.PluralOf("inlineAction")
-    private @Nullable List<InlinePresentation> mInlineActions;
+    private @Nullable List<InlineAction> mInlineActions;
 
     /**
      * The client state that {@link AugmentedAutofillService} implementation can put anything in to
@@ -74,7 +73,7 @@
         return null;
     }
 
-    private static List<InlinePresentation> defaultInlineActions() {
+    private static List<InlineAction> defaultInlineActions() {
         return null;
     }
 
@@ -86,12 +85,12 @@
     /** @hide */
     abstract static class BaseBuilder {
         abstract FillResponse.Builder addInlineSuggestion(@NonNull Dataset value);
-        abstract FillResponse.Builder addInlineAction(@NonNull InlinePresentation value);
+        abstract FillResponse.Builder addInlineAction(@NonNull InlineAction value);
     }
 
 
 
-    // Code below generated by codegen v1.0.14.
+    // Code below generated by codegen v1.0.15.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -108,7 +107,7 @@
     /* package-private */ FillResponse(
             @Nullable FillWindow fillWindow,
             @Nullable List<Dataset> inlineSuggestions,
-            @Nullable List<InlinePresentation> inlineActions,
+            @Nullable List<InlineAction> inlineActions,
             @Nullable Bundle clientState) {
         this.mFillWindow = fillWindow;
         this.mInlineSuggestions = inlineSuggestions;
@@ -140,13 +139,12 @@
     }
 
     /**
-     * The {@link InlinePresentation}s representing the inline actions. Defaults to null if no
-     * inline actions are provided.
+     * Defaults to null if no inline actions are provided.
      *
      * @hide
      */
     @DataClass.Generated.Member
-    public @Nullable List<InlinePresentation> getInlineActions() {
+    public @Nullable List<InlineAction> getInlineActions() {
         return mInlineActions;
     }
 
@@ -171,7 +169,7 @@
 
         private @Nullable FillWindow mFillWindow;
         private @Nullable List<Dataset> mInlineSuggestions;
-        private @Nullable List<InlinePresentation> mInlineActions;
+        private @Nullable List<InlineAction> mInlineActions;
         private @Nullable Bundle mClientState;
 
         private long mBuilderFieldsSet = 0L;
@@ -183,7 +181,7 @@
          * The {@link FillWindow} used to display the Autofill UI.
          */
         @DataClass.Generated.Member
-        public @NonNull Builder setFillWindow(@Nullable FillWindow value) {
+        public @NonNull Builder setFillWindow(@NonNull FillWindow value) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x1;
             mFillWindow = value;
@@ -195,7 +193,7 @@
          * inline suggestions are available from the service.
          */
         @DataClass.Generated.Member
-        public @NonNull Builder setInlineSuggestions(@Nullable List<Dataset> value) {
+        public @NonNull Builder setInlineSuggestions(@NonNull List<Dataset> value) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x2;
             mInlineSuggestions = value;
@@ -212,11 +210,10 @@
         }
 
         /**
-         * The {@link InlinePresentation}s representing the inline actions. Defaults to null if no
-         * inline actions are provided.
+         * Defaults to null if no inline actions are provided.
          */
         @DataClass.Generated.Member
-        public @NonNull Builder setInlineActions(@Nullable List<InlinePresentation> value) {
+        public @NonNull Builder setInlineActions(@NonNull List<InlineAction> value) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x4;
             mInlineActions = value;
@@ -226,7 +223,7 @@
         /** @see #setInlineActions */
         @DataClass.Generated.Member
         @Override
-        @NonNull FillResponse.Builder addInlineAction(@NonNull InlinePresentation value) {
+        @NonNull FillResponse.Builder addInlineAction(@NonNull InlineAction value) {
             if (mInlineActions == null) setInlineActions(new ArrayList<>());
             mInlineActions.add(value);
             return this;
@@ -238,7 +235,7 @@
          * {@link AugmentedAutofillService#getFillEventHistory()}.
          */
         @DataClass.Generated.Member
-        public @NonNull Builder setClientState(@Nullable Bundle value) {
+        public @NonNull Builder setClientState(@NonNull Bundle value) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x8;
             mClientState = value;
@@ -279,10 +276,10 @@
     }
 
     @DataClass.Generated(
-            time = 1582682935951L,
-            codegenVersion = "1.0.14",
+            time = 1583793032373L,
+            codegenVersion = "1.0.15",
             sourceFile = "frameworks/base/core/java/android/service/autofill/augmented/FillResponse.java",
-            inputSignatures = "private @android.annotation.Nullable android.service.autofill.augmented.FillWindow mFillWindow\nprivate @com.android.internal.util.DataClass.PluralOf(\"inlineSuggestion\") @android.annotation.Nullable java.util.List<android.service.autofill.Dataset> mInlineSuggestions\nprivate @com.android.internal.util.DataClass.PluralOf(\"inlineAction\") @android.annotation.Nullable java.util.List<android.service.autofill.InlinePresentation> mInlineActions\nprivate @android.annotation.Nullable android.os.Bundle mClientState\nprivate static  android.service.autofill.augmented.FillWindow defaultFillWindow()\nprivate static  java.util.List<android.service.autofill.Dataset> defaultInlineSuggestions()\nprivate static  java.util.List<android.service.autofill.InlinePresentation> defaultInlineActions()\nprivate static  android.os.Bundle defaultClientState()\nclass FillResponse extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genHiddenGetters=true)\nabstract  android.service.autofill.augmented.FillResponse.Builder addInlineSuggestion(android.service.autofill.Dataset)\nabstract  android.service.autofill.augmented.FillResponse.Builder addInlineAction(android.service.autofill.InlinePresentation)\nclass BaseBuilder extends java.lang.Object implements []")
+            inputSignatures = "private @android.annotation.Nullable android.service.autofill.augmented.FillWindow mFillWindow\nprivate @com.android.internal.util.DataClass.PluralOf(\"inlineSuggestion\") @android.annotation.Nullable java.util.List<android.service.autofill.Dataset> mInlineSuggestions\nprivate @com.android.internal.util.DataClass.PluralOf(\"inlineAction\") @android.annotation.Nullable java.util.List<android.service.autofill.InlineAction> mInlineActions\nprivate @android.annotation.Nullable android.os.Bundle mClientState\nprivate static  android.service.autofill.augmented.FillWindow defaultFillWindow()\nprivate static  java.util.List<android.service.autofill.Dataset> defaultInlineSuggestions()\nprivate static  java.util.List<android.service.autofill.InlineAction> defaultInlineActions()\nprivate static  android.os.Bundle defaultClientState()\nclass FillResponse extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genHiddenGetters=true)\nabstract  android.service.autofill.augmented.FillResponse.Builder addInlineSuggestion(android.service.autofill.Dataset)\nabstract  android.service.autofill.augmented.FillResponse.Builder addInlineAction(android.service.autofill.InlineAction)\nclass BaseBuilder extends java.lang.Object implements []")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/service/autofill/augmented/IFillCallback.aidl b/core/java/android/service/autofill/augmented/IFillCallback.aidl
index bf0adcd..545dab2 100644
--- a/core/java/android/service/autofill/augmented/IFillCallback.aidl
+++ b/core/java/android/service/autofill/augmented/IFillCallback.aidl
@@ -20,7 +20,7 @@
 import android.os.ICancellationSignal;
 
 import android.service.autofill.Dataset;
-import android.service.autofill.InlinePresentation;
+import android.service.autofill.InlineAction;
 
 import java.util.List;
 
@@ -32,7 +32,7 @@
 interface IFillCallback {
     void onCancellable(in ICancellationSignal cancellation);
     void onSuccess(in @nullable List<Dataset> inlineSuggestionsData,
-                   in @nullable List<InlinePresentation> inlineActions);
+                   in @nullable List<InlineAction> inlineActions);
     boolean isCompleted();
     void cancel();
 }
diff --git a/core/java/android/service/contentcapture/DataShareReadAdapter.java b/core/java/android/service/contentcapture/DataShareReadAdapter.java
index a481ec8..8cd9eea 100644
--- a/core/java/android/service/contentcapture/DataShareReadAdapter.java
+++ b/core/java/android/service/contentcapture/DataShareReadAdapter.java
@@ -20,6 +20,7 @@
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.os.ParcelFileDescriptor;
+import android.view.contentcapture.ContentCaptureManager.DataShareError;
 
 /**
  * Adapter class to be used for the Content Capture Service app to propagate the status of the
@@ -46,5 +47,5 @@
      * these 2 events is not defined, and it's important that the service treats end of stream
      * correctly in this situation.
      **/
-    void onError(int errorCode);
+    void onError(@DataShareError int errorCode);
 }
diff --git a/core/java/android/service/controls/TokenProvider.aidl b/core/java/android/service/controls/TokenProvider.aidl
deleted file mode 100644
index 8f4b795..0000000
--- a/core/java/android/service/controls/TokenProvider.aidl
+++ /dev/null
@@ -1,7 +0,0 @@
-package android.service.controls;
-
-/** @hide */
-interface TokenProvider {
-    void setAuthToken(String token);
-    String getAccountName();
-}
\ No newline at end of file
diff --git a/core/java/android/service/dataloader/DataLoaderService.java b/core/java/android/service/dataloader/DataLoaderService.java
index 5bf1975..0b9a8af 100644
--- a/core/java/android/service/dataloader/DataLoaderService.java
+++ b/core/java/android/service/dataloader/DataLoaderService.java
@@ -172,7 +172,7 @@
         @Override
         public void prepareImage(InstallationFileParcel[] addedFiles, String[] removedFiles) {
             if (!nativePrepareImage(mId, addedFiles, removedFiles)) {
-                Slog.w(TAG, "Failed to destroy loader: " + mId);
+                Slog.w(TAG, "Failed to prepare image for data loader: " + mId);
             }
         }
     }
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 74b9136..1beeb65 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -237,16 +237,24 @@
     public StatusBarNotification cloneLight() {
         final Notification no = new Notification();
         this.notification.cloneInto(no, false); // light copy
-        return new StatusBarNotification(this.pkg, this.opPkg,
-                this.id, this.tag, this.uid, this.initialPid,
-                no, this.user, this.overrideGroupKey, this.postTime);
+        return cloneShallow(no);
     }
 
     @Override
     public StatusBarNotification clone() {
-        return new StatusBarNotification(this.pkg, this.opPkg,
+        return cloneShallow(this.notification.clone());
+    }
+
+    /**
+     * @param notification Some kind of clone of this.notification.
+     * @return A shallow copy of self, with notification in place of this.notification.
+     */
+    StatusBarNotification cloneShallow(Notification notification) {
+        StatusBarNotification result = new StatusBarNotification(this.pkg, this.opPkg,
                 this.id, this.tag, this.uid, this.initialPid,
-                this.notification.clone(), this.user, this.overrideGroupKey, this.postTime);
+                notification, this.user, this.overrideGroupKey, this.postTime);
+        result.setInstanceId(this.mInstanceId);
+        return result;
     }
 
     @Override
diff --git a/core/java/android/view/ITaskOrganizer.aidl b/core/java/android/view/ITaskOrganizer.aidl
index 5ccdd30..565f694 100644
--- a/core/java/android/view/ITaskOrganizer.aidl
+++ b/core/java/android/view/ITaskOrganizer.aidl
@@ -27,7 +27,7 @@
  */
 oneway interface ITaskOrganizer {
     void taskAppeared(in ActivityManager.RunningTaskInfo taskInfo);
-    void taskVanished(in IWindowContainer container);
+    void taskVanished(in ActivityManager.RunningTaskInfo taskInfo);
 
     /**
      * Called upon completion of
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 73601d9..84ac90b 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -724,11 +724,12 @@
 
     /**
      * Called to get the expected window insets.
-     * TODO(window-context): Remove when new insets flag is available.
+     *
+     * @return {@code true} if system bars are always comsumed.
      */
-    void getWindowInsets(in WindowManager.LayoutParams attrs, int displayId,
+    boolean getWindowInsets(in WindowManager.LayoutParams attrs, int displayId,
             out Rect outContentInsets, out Rect outStableInsets,
-            out DisplayCutout.ParcelableWrapper displayCutout);
+            out DisplayCutout.ParcelableWrapper outDisplayCutout, out InsetsState outInsetsState);
 
     /**
      * Called to show global actions.
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 573d8fc..607886f 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -52,6 +52,7 @@
 import android.view.animation.PathInterpolator;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -59,6 +60,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.function.BiFunction;
 
 /**
@@ -306,6 +308,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) -> {
@@ -459,6 +466,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 */);
         }
@@ -542,9 +556,7 @@
     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();
@@ -554,6 +566,13 @@
                 false /* fade */, animationType, getLayoutInsetsDuringAnimationMode(types));
     }
 
+    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,
@@ -567,6 +586,7 @@
             return cancellationSignal;
         }
         cancelExistingControllers(types);
+        mLastStartedAnimTypes |= types;
 
         final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
         final SparseArray<InsetsSourceControl> controls = new SparseArray<>();
@@ -724,12 +744,13 @@
             mApplier = new SyncRtSurfaceTransactionApplier(mViewRoot.mView);
         }
         if (mViewRoot.mView.isHardwareAccelerated()) {
-            mApplier.scheduleApply(params);
+            mApplier.scheduleApply(false /* earlyWakeup */, params);
         } else {
             // Window doesn't support hardware acceleration, no synchronization for now.
             // TODO(b/149342281): use mViewRoot.mSurface.getNextFrameNumber() to sync on every
             //  frame instead.
-            mApplier.applyParams(new Transaction(), -1 /* frame */, params);
+            mApplier.applyParams(new Transaction(), -1 /* frame */, false /* earlyWakeup */,
+                    params);
         }
     }
 
@@ -992,4 +1013,65 @@
         }
         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
+     * a SyncRtSurfaceTransaction applier to use it from the RenderThread. To avoid
+     * synchronization issues we also release from the RenderThread so this release
+     * happens after any existing items on the work queue.
+     */
+    public void releaseSurfaceControlFromRt(SurfaceControl sc) {
+        if (mViewRoot.mView != null && mViewRoot.mView.isHardwareAccelerated()) {
+            mViewRoot.registerRtFrameCallback(frame -> {
+                  sc.release();
+            });
+            // Make sure a frame gets scheduled.
+            mViewRoot.mView.invalidate();
+        } else {
+              sc.release();
+        }
+    }
 }
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index f07f1ce..252fc0c 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -117,7 +117,7 @@
             }
         }
         if (lastControl != null) {
-            lastControl.release();
+            lastControl.release(mController);
         }
     }
 
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index 29ba56a..75f6eab 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -94,9 +94,9 @@
         dest.writeParcelable(mSurfacePosition, 0 /* flags*/);
     }
 
-    public void release() {
+    public void release(InsetsController controller) {
         if (mLeash != null) {
-            mLeash.release();
+            controller.releaseSurfaceControlFromRt(mLeash);
         }
     }
 
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index e4aa410..c2ad74a 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -80,6 +80,12 @@
     })
     public @interface InternalInsetsType {}
 
+    /**
+     * Special value to be used to by methods returning an {@link InternalInsetsType} to indicate
+     * that the objects/parameters aren't associated with an {@link InternalInsetsType}
+     */
+    public static final int ITYPE_INVALID = -1;
+
     static final int FIRST_TYPE = 0;
 
     public static final int ITYPE_STATUS_BAR = FIRST_TYPE;
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/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index b04372a..3c22ed8 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -20,9 +20,11 @@
 import static android.view.RemoteAnimationTargetProto.CONTENT_INSETS;
 import static android.view.RemoteAnimationTargetProto.IS_TRANSLUCENT;
 import static android.view.RemoteAnimationTargetProto.LEASH;
+import static android.view.RemoteAnimationTargetProto.LOCAL_BOUNDS;
 import static android.view.RemoteAnimationTargetProto.MODE;
 import static android.view.RemoteAnimationTargetProto.POSITION;
 import static android.view.RemoteAnimationTargetProto.PREFIX_ORDER_INDEX;
+import static android.view.RemoteAnimationTargetProto.SCREEN_SPACE_BOUNDS;
 import static android.view.RemoteAnimationTargetProto.SOURCE_CONTAINER_BOUNDS;
 import static android.view.RemoteAnimationTargetProto.START_BOUNDS;
 import static android.view.RemoteAnimationTargetProto.START_LEASH;
@@ -130,19 +132,38 @@
      * The source position of the app, in screen spaces coordinates. If the position of the leash
      * is modified from the controlling app, any animation transform needs to be offset by this
      * amount.
+     * @deprecated Use {@link #localBounds} instead.
      */
+    @Deprecated
     @UnsupportedAppUsage
     public final Point position;
 
     /**
+     * Bounds of the target relative to its parent.
+     * When the app target animating on its parent, we need to use the local coordinates relative to
+     * its parent with {@code localBounds.left} & {@code localBounds.top} rather than using
+     * {@code position} in screen coordinates.
+     */
+    public final Rect localBounds;
+
+    /**
      * The bounds of the source container the app lives in, in screen space coordinates. If the crop
      * of the leash is modified from the controlling app, it needs to take the source container
      * bounds into account when calculating the crop.
+     * @deprecated Renamed to {@link #screenSpaceBounds}
      */
+    @Deprecated
     @UnsupportedAppUsage
     public final Rect sourceContainerBounds;
 
     /**
+     * Bounds of the target relative to the screen. If the crop of the leash is modified from the
+     * controlling app, it needs to take the screen space bounds into account when calculating the
+     * crop.
+     */
+    public final Rect screenSpaceBounds;
+
+    /**
      * The starting bounds of the source container in screen space coordinates. This is {@code null}
      * if the animation target isn't MODE_CHANGING. Since this is the starting bounds, it's size
      * should be equivalent to the size of the starting thumbnail. Note that sourceContainerBounds
@@ -165,7 +186,8 @@
 
     public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent,
             Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position,
-            Rect sourceContainerBounds, WindowConfiguration windowConfig, boolean isNotInRecents,
+            Rect localBounds, Rect screenSpaceBounds,
+            WindowConfiguration windowConfig, boolean isNotInRecents,
             SurfaceControl startLeash, Rect startBounds) {
         this.mode = mode;
         this.taskId = taskId;
@@ -175,7 +197,9 @@
         this.contentInsets = new Rect(contentInsets);
         this.prefixOrderIndex = prefixOrderIndex;
         this.position = new Point(position);
-        this.sourceContainerBounds = new Rect(sourceContainerBounds);
+        this.localBounds = new Rect(localBounds);
+        this.sourceContainerBounds = new Rect(screenSpaceBounds);
+        this.screenSpaceBounds = new Rect(screenSpaceBounds);
         this.windowConfiguration = windowConfig;
         this.isNotInRecents = isNotInRecents;
         this.startLeash = startLeash;
@@ -191,7 +215,9 @@
         contentInsets = in.readParcelable(null);
         prefixOrderIndex = in.readInt();
         position = in.readParcelable(null);
+        localBounds = in.readParcelable(null);
         sourceContainerBounds = in.readParcelable(null);
+        screenSpaceBounds = in.readParcelable(null);
         windowConfiguration = in.readParcelable(null);
         isNotInRecents = in.readBoolean();
         startLeash = in.readParcelable(null);
@@ -213,7 +239,9 @@
         dest.writeParcelable(contentInsets, 0 /* flags */);
         dest.writeInt(prefixOrderIndex);
         dest.writeParcelable(position, 0 /* flags */);
+        dest.writeParcelable(localBounds, 0 /* flags */);
         dest.writeParcelable(sourceContainerBounds, 0 /* flags */);
+        dest.writeParcelable(screenSpaceBounds, 0 /* flags */);
         dest.writeParcelable(windowConfiguration, 0 /* flags */);
         dest.writeBoolean(isNotInRecents);
         dest.writeParcelable(startLeash, 0 /* flags */);
@@ -229,6 +257,8 @@
         pw.print(" prefixOrderIndex="); pw.print(prefixOrderIndex);
         pw.print(" position="); position.printShortString(pw);
         pw.print(" sourceContainerBounds="); sourceContainerBounds.printShortString(pw);
+        pw.print(" screenSpaceBounds="); screenSpaceBounds.printShortString(pw);
+        pw.print(" localBounds="); localBounds.printShortString(pw);
         pw.println();
         pw.print(prefix); pw.print("windowConfiguration="); pw.println(windowConfiguration);
         pw.print(prefix); pw.print("leash="); pw.println(leash);
@@ -245,6 +275,8 @@
         proto.write(PREFIX_ORDER_INDEX, prefixOrderIndex);
         position.dumpDebug(proto, POSITION);
         sourceContainerBounds.dumpDebug(proto, SOURCE_CONTAINER_BOUNDS);
+        screenSpaceBounds.dumpDebug(proto, SCREEN_SPACE_BOUNDS);
+        localBounds.dumpDebug(proto, LOCAL_BOUNDS);
         windowConfiguration.dumpDebug(proto, WINDOW_CONFIGURATION);
         if (startLeash != null) {
             startLeash.dumpDebug(proto, START_LEASH);
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index fb7c04a..1f7c3504 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -605,7 +605,7 @@
                 mTmpRect.set(0, 0, mSurfaceWidth, mSurfaceHeight);
             }
             SyncRtSurfaceTransactionApplier applier = new SyncRtSurfaceTransactionApplier(this);
-            applier.scheduleApply(
+            applier.scheduleApply(false /* earlyWakeup */,
                     new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(mSurfaceControl)
                             .withWindowCrop(mTmpRect)
                             .build());
diff --git a/core/java/android/view/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
index 206e4f9..9c97f3e 100644
--- a/core/java/android/view/SyncRtSurfaceTransactionApplier.java
+++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
@@ -53,10 +53,11 @@
     /**
      * Schedules applying surface parameters on the next frame.
      *
+     * @param earlyWakeup Whether to set {@link Transaction#setEarlyWakeup()} on transaction.
      * @param params The surface parameters to apply. DO NOT MODIFY the list after passing into
      *               this method to avoid synchronization issues.
      */
-    public void scheduleApply(final SurfaceParams... params) {
+    public void scheduleApply(boolean earlyWakeup, final SurfaceParams... params) {
         if (mTargetViewRootImpl == null) {
             return;
         }
@@ -66,7 +67,7 @@
                 return;
             }
             Transaction t = new Transaction();
-            applyParams(t, frame, params);
+            applyParams(t, frame, earlyWakeup, params);
         });
 
         // Make sure a frame gets scheduled.
@@ -77,10 +78,12 @@
      * Applies surface parameters on the next frame.
      * @param t transaction to apply all parameters in.
      * @param frame frame to synchronize to. Set -1 when sync is not required.
+     * @param earlyWakeup Whether to set {@link Transaction#setEarlyWakeup()} on transaction.
      * @param params The surface parameters to apply. DO NOT MODIFY the list after passing into
      *               this method to avoid synchronization issues.
      */
-    void applyParams(Transaction t, long frame, final SurfaceParams... params) {
+     void applyParams(Transaction t, long frame, boolean earlyWakeup,
+            final SurfaceParams... params) {
         for (int i = params.length - 1; i >= 0; i--) {
             SurfaceParams surfaceParams = params[i];
             SurfaceControl surface = surfaceParams.surface;
@@ -89,7 +92,9 @@
             }
             applyParams(t, surfaceParams, mTmpFloat9);
         }
-        t.setEarlyWakeup();
+        if (earlyWakeup) {
+            t.setEarlyWakeup();
+        }
         t.apply();
     }
 
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index b6c46be..7d4ec3d 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3714,7 +3714,7 @@
         if (extraDataKey.equals(AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY)) {
             final AccessibilityNodeInfo.ExtraRenderingInfo extraRenderingInfo =
                     AccessibilityNodeInfo.ExtraRenderingInfo.obtain();
-            extraRenderingInfo.setLayoutParams(getLayoutParams().width, getLayoutParams().height);
+            extraRenderingInfo.setLayoutSize(getLayoutParams().width, getLayoutParams().height);
             info.setExtraRenderingInfo(extraRenderingInfo);
         }
     }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 8d3cffc..9e5298b 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1735,7 +1735,7 @@
             mBoundsLayer = new SurfaceControl.Builder(mSurfaceSession)
                     .setContainerLayer()
                     .setName("Bounds for - " + getTitle().toString())
-                    .setParent(mSurfaceControl)
+                    .setParent(getRenderSurfaceControl())
                     .build();
             setBoundsLayerCrop();
             mTransaction.show(mBoundsLayer).apply();
@@ -5699,9 +5699,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);
             }
 
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/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 6435b42..4050da1 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -18,8 +18,11 @@
 
 import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
 import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
+import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
 
 import android.annotation.NonNull;
 import android.app.ResourcesManager;
@@ -215,9 +218,9 @@
     @Override
     public WindowMetrics getCurrentWindowMetrics() {
         final Context context = mParentWindow != null ? mParentWindow.getContext() : mContext;
-        final Rect bound = getCurrentBounds(context);
+        final Rect bounds = getCurrentBounds(context);
 
-        return new WindowMetrics(toSize(bound), computeWindowInsets());
+        return new WindowMetrics(toSize(bounds), computeWindowInsets(bounds));
     }
 
     private static Rect getCurrentBounds(Context context) {
@@ -228,7 +231,8 @@
 
     @Override
     public WindowMetrics getMaximumWindowMetrics() {
-        return new WindowMetrics(toSize(getMaximumBounds()), computeWindowInsets());
+        final Rect maxBounds = getMaximumBounds();
+        return new WindowMetrics(toSize(maxBounds), computeWindowInsets(maxBounds));
     }
 
     private Size toSize(Rect frame) {
@@ -244,9 +248,8 @@
         return new Rect(0, 0, displaySize.x, displaySize.y);
     }
 
-    private WindowInsets computeWindowInsets() {
-        // TODO(b/118118435): This can only be properly implemented
-        //  once we flip the new insets mode flag.
+    // TODO(b/150095967): Set window type to LayoutParams
+    private WindowInsets computeWindowInsets(Rect bounds) {
         // Initialize params which used for obtaining all system insets.
         final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
         params.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
@@ -257,21 +260,34 @@
         params.setFitInsetsTypes(0);
         params.setFitInsetsSides(0);
 
-        return getWindowInsetsFromServer(params);
+        return getWindowInsetsFromServer(params, bounds);
     }
 
-    private WindowInsets getWindowInsetsFromServer(WindowManager.LayoutParams attrs) {
+    private WindowInsets getWindowInsetsFromServer(WindowManager.LayoutParams attrs, Rect bounds) {
         try {
             final Rect systemWindowInsets = new Rect();
             final Rect stableInsets = new Rect();
             final DisplayCutout.ParcelableWrapper displayCutout =
                     new DisplayCutout.ParcelableWrapper();
-            WindowManagerGlobal.getWindowManagerService().getWindowInsets(attrs,
-                    mContext.getDisplayId(), systemWindowInsets, stableInsets, displayCutout);
-            return new WindowInsets.Builder()
-                    .setSystemWindowInsets(Insets.of(systemWindowInsets))
-                    .setStableInsets(Insets.of(stableInsets))
-                    .setDisplayCutout(displayCutout.get()).build();
+            final InsetsState insetsState = new InsetsState();
+            final boolean alwaysConsumeSystemBars = WindowManagerGlobal.getWindowManagerService()
+                    .getWindowInsets(attrs, mContext.getDisplayId(), systemWindowInsets,
+                    stableInsets, displayCutout, insetsState);
+            final boolean isScreenRound =
+                    mContext.getResources().getConfiguration().isScreenRound();
+            if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) {
+                return insetsState.calculateInsets(bounds, null /* ignoringVisibilityState*/,
+                        isScreenRound, alwaysConsumeSystemBars, displayCutout.get(),
+                        systemWindowInsets, stableInsets, SOFT_INPUT_ADJUST_NOTHING,
+                        SYSTEM_UI_FLAG_VISIBLE, null /* typeSideMap */);
+            } else {
+                return new WindowInsets.Builder()
+                        .setAlwaysConsumeSystemBars(alwaysConsumeSystemBars)
+                        .setRound(isScreenRound)
+                        .setSystemWindowInsets(Insets.of(systemWindowInsets))
+                        .setStableInsets(Insets.of(stableInsets))
+                        .setDisplayCutout(displayCutout.get()).build();
+            }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index f2cbf89..5fccf40 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -559,14 +559,6 @@
     public static final String ACTION_ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT =
             "android.view.accessibility.action.ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT";
 
-    /**
-     * Argument to represent the IME action Id to press the returning key on a node.
-     * For use with R.id.accessibilityActionImeEnter
-     * @hide
-     */
-    public static final String ACTION_ARGUMENT_IME_ACTION_ID_INT =
-            "android.view.accessibility.action.ARGUMENT_IME_ACTION_ID_INT";
-
     // Focus types
 
     /**
@@ -649,7 +641,7 @@
      * argument.
      * <p>
      * The data can be retrieved from the {@link ExtraRenderingInfo} returned by
-     * {@link #getExtraRenderingInfo()} using {@link ExtraRenderingInfo#getLayoutParams},
+     * {@link #getExtraRenderingInfo()} using {@link ExtraRenderingInfo#getLayoutSize},
      * {@link ExtraRenderingInfo#getTextSizeInPx()} and
      * {@link ExtraRenderingInfo#getTextSizeUnit()}. For layout params, it is supported by both
      * {@link TextView} and {@link ViewGroup}. For text size and unit, it is only supported by
@@ -657,7 +649,6 @@
      *
      * @see #refreshWithExtraData(String, Bundle)
      */
-
     public static final String EXTRA_DATA_RENDERING_INFO_KEY =
             "android.view.accessibility.extra.DATA_RENDERING_INFO_KEY";
 
@@ -1038,8 +1029,8 @@
      *                     with this mechanism is generally expensive to retrieve, so should only be
      *                     requested when needed. See
      *                     {@link #EXTRA_DATA_RENDERING_INFO_KEY},
-     *                     {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY} and
-     *                     {@link #getAvailableExtraData()}.
+     *                     {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY},
+     *                     {@link #getAvailableExtraData()} and {@link #getExtraRenderingInfo()}.
      * @param args A bundle of arguments for the request. These depend on the particular request.
      *
      * @return {@code true} if the refresh succeeded. {@code false} if the {@link View} represented
@@ -2437,9 +2428,13 @@
     }
 
     /**
-     * Gets the conformance info if the node is meant to be refreshed with extra data.
+     * Gets the {@link ExtraRenderingInfo extra rendering info} if the node is meant to be
+     * refreshed with extra data to examine rendering related accessibility issues.
      *
-     * @return The conformance info.
+     * @return The {@link ExtraRenderingInfo extra rendering info}.
+     *
+     * @see #EXTRA_DATA_RENDERING_INFO_KEY
+     * @see #refreshWithExtraData(String, Bundle)
      */
     @Nullable
     public ExtraRenderingInfo getExtraRenderingInfo() {
@@ -2447,14 +2442,15 @@
     }
 
     /**
-     * Sets the conformance info if the node is meant to be refreshed with extra data.
+     * Sets the extra rendering info, <code>extraRenderingInfo<code/>, if the node is meant to be
+     * refreshed with extra data.
      * <p>
      *   <strong>Note:</strong> Cannot be called from an
      *   {@link android.accessibilityservice.AccessibilityService}.
      *   This class is made immutable before being delivered to an AccessibilityService.
      * </p>
      *
-     * @param extraRenderingInfo The conformance info.
+     * @param extraRenderingInfo The {@link ExtraRenderingInfo extra rendering info}.
      * @hide
      */
     public void setExtraRenderingInfo(@NonNull ExtraRenderingInfo extraRenderingInfo) {
@@ -3925,7 +3921,7 @@
         }
 
         if (isBitSet(nonDefaultFields, fieldIndex++)) {
-            parcel.writeValue(mExtraRenderingInfo.getLayoutParams());
+            parcel.writeValue(mExtraRenderingInfo.getLayoutSize());
             parcel.writeFloat(mExtraRenderingInfo.getTextSizeInPx());
             parcel.writeInt(mExtraRenderingInfo.getTextSizeUnit());
         }
@@ -4189,7 +4185,7 @@
         if (isBitSet(nonDefaultFields, fieldIndex++)) {
             if (mExtraRenderingInfo != null) mExtraRenderingInfo.recycle();
             mExtraRenderingInfo = ExtraRenderingInfo.obtain();
-            mExtraRenderingInfo.mLayoutParams = (Size) parcel.readValue(null);
+            mExtraRenderingInfo.mLayoutSize = (Size) parcel.readValue(null);
             mExtraRenderingInfo.mTextSizeInPx = parcel.readFloat();
             mExtraRenderingInfo.mTextSizeUnit = parcel.readInt();
         }
@@ -4969,10 +4965,11 @@
                 new AccessibilityAction(R.id.accessibilityActionPressAndHold);
 
         /**
-         * Action to send an ime action which is from
-         * {@link android.view.inputmethod.EditorInfo#actionId}. This action would be
+         * Action to send an ime actionId which is from
+         * {@link android.view.inputmethod.EditorInfo#actionId}. This ime actionId sets by
+         * {@link TextView#setImeActionLabel(CharSequence, int)}, or it would be
          * {@link android.view.inputmethod.EditorInfo#IME_ACTION_UNSPECIFIED} if no specific
-         * actionId defined. A node should expose this action only for views that are currently
+         * actionId has set. A node should expose this action only for views that are currently
          * with input focus and editable.
          */
         @NonNull public static final AccessibilityAction ACTION_IME_ENTER =
@@ -5808,7 +5805,7 @@
         private static final SynchronizedPool<ExtraRenderingInfo> sPool =
                 new SynchronizedPool<>(MAX_POOL_SIZE);
 
-        private Size mLayoutParams;
+        private Size mLayoutSize;
         private float mTextSizeInPx = UNDEFINED_VALUE;
         private int mTextSizeUnit = UNDEFINED_VALUE;
 
@@ -5828,32 +5825,36 @@
         /** Obtains a pooled instance that is a clone of another one. */
         private static ExtraRenderingInfo obtain(ExtraRenderingInfo other) {
             ExtraRenderingInfo extraRenderingInfo = ExtraRenderingInfo.obtain();
-            extraRenderingInfo.mLayoutParams = other.mLayoutParams;
+            extraRenderingInfo.mLayoutSize = other.mLayoutSize;
             extraRenderingInfo.mTextSizeInPx = other.mTextSizeInPx;
             extraRenderingInfo.mTextSizeUnit = other.mTextSizeUnit;
             return extraRenderingInfo;
         }
 
         /**
-         * Creates a new conformance info of a view, and this new instance is initialized from
+         * Creates a new rendering info of a view, and this new instance is initialized from
          * the given <code>other</code>.
          *
          * @param other The instance to clone.
          */
         private ExtraRenderingInfo(@Nullable ExtraRenderingInfo other) {
             if (other != null) {
-                mLayoutParams = other.mLayoutParams;
+                mLayoutSize = other.mLayoutSize;
                 mTextSizeInPx = other.mTextSizeInPx;
                 mTextSizeUnit = other.mTextSizeUnit;
             }
         }
 
         /**
+         * Gets the size object containing the height and the width of layout params if the node is
+         * a {@link ViewGroup} or a {@link TextView}, or null otherwise. Useful for accessibility
+         * scanning tool to understand whether the text is scalable and fits the view or not.
+         *
          * @return a {@link Size} stores layout height and layout width of the view,
          *         or null otherwise.
          */
-        public @Nullable Size getLayoutParams() {
-            return mLayoutParams;
+        public @Nullable Size getLayoutSize() {
+            return mLayoutSize;
         }
 
         /**
@@ -5863,11 +5864,15 @@
          * @param height The layout height.
          * @hide
          */
-        public void setLayoutParams(int width, int height) {
-            mLayoutParams = new Size(width, height);
+        public void setLayoutSize(int width, int height) {
+            mLayoutSize = new Size(width, height);
         }
 
         /**
+         * Gets the text size if the node is a {@link TextView}, or -1 otherwise.  Useful for
+         * accessibility scanning tool to understand whether the text is scalable and fits the view
+         * or not.
+         *
          * @return the text size of a {@code TextView}, or -1 otherwise.
          */
         public float getTextSizeInPx() {
@@ -5885,6 +5890,11 @@
         }
 
         /**
+         * Gets the text size unit if the node is a {@link TextView}, or -1 otherwise.
+         * Text size returned from {@link #getTextSizeInPx} in raw pixels may scale by factors and
+         * convert from other units. Useful for accessibility scanning tool to understand whether
+         * the text is scalable and fits the view or not.
+         *
          * @return the text size unit which type is {@link TypedValue#TYPE_DIMENSION} of a
          *         {@code TextView}, or -1 otherwise.
          *
@@ -5915,7 +5925,7 @@
         }
 
         private void clear() {
-            mLayoutParams = null;
+            mLayoutSize = null;
             mTextSizeInPx = UNDEFINED_VALUE;
             mTextSizeUnit = UNDEFINED_VALUE;
         }
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index b7b54c8..b988927 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -217,6 +217,15 @@
     public static final int DATA_SHARE_ERROR_TIMEOUT_INTERRUPTED = 3;
 
     /** @hide */
+    @IntDef(flag = false, value = {
+            DATA_SHARE_ERROR_UNKNOWN,
+            DATA_SHARE_ERROR_CONCURRENT_REQUEST,
+            DATA_SHARE_ERROR_TIMEOUT_INTERRUPTED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DataShareError {}
+
+    /** @hide */
     public static final int RESULT_CODE_OK = 0;
     /** @hide */
     public static final int RESULT_CODE_TRUE = 1;
diff --git a/core/java/android/view/contentcapture/DataShareWriteAdapter.java b/core/java/android/view/contentcapture/DataShareWriteAdapter.java
index 2beaede..3b5b756 100644
--- a/core/java/android/view/contentcapture/DataShareWriteAdapter.java
+++ b/core/java/android/view/contentcapture/DataShareWriteAdapter.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.os.ParcelFileDescriptor;
+import android.view.contentcapture.ContentCaptureManager.DataShareError;
 
 /** Adapter class used by apps to share data with the Content Capture service. */
 public interface DataShareWriteAdapter {
@@ -42,7 +43,7 @@
      *
      * @param errorCode the error code corresponding to an ERROR_* value.
      */
-    default void onError(int errorCode) {
+    default void onError(@DataShareError int errorCode) {
         /* do nothing - stub */
     }
 }
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index ff80ef7..f62a28e 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -268,17 +268,28 @@
     protected abstract boolean allowFileSchemeCookiesImpl();
 
     /**
-     * Sets whether the application's {@link WebView} instances should send and
-     * accept cookies for file scheme URLs.
-     * Use of cookies with file scheme URLs is potentially insecure and turned
-     * off by default.
-     * Do not use this feature unless you can be sure that no unintentional
-     * sharing of cookie data can take place.
+     * Sets whether the application's {@link WebView} instances should send and accept cookies for
+     * file scheme URLs.
      * <p>
-     * Note that calls to this method will have no effect if made after a
-     * {@link WebView} or CookieManager instance has been created.
+     * Use of cookies with file scheme URLs is potentially insecure and turned off by default. All
+     * {@code file://} URLs share all their cookies, which may lead to leaking private app cookies
+     * (ex. any malicious file can access cookies previously set by other (trusted) files).
+     * <p class="note">
+     * Loading content via {@code file://} URLs is generally discouraged. See the note in
+     * {@link WebSettings#setAllowFileAccess}.
+     * Using <a href="{@docRoot}reference/androidx/webkit/WebViewAssetLoader.html">
+     * androidx.webkit.WebViewAssetLoader</a> to load files over {@code http(s)://} URLs allows
+     * the standard web security model to be used for setting and sharing cookies for local files.
+     * <p>
+     * Note that calls to this method will have no effect if made after calling other
+     * {@link CookieManager} APIs.
+     *
+     * @deprecated This setting is not secure, please use
+     *             <a href="{@docRoot}reference/androidx/webkit/WebViewAssetLoader.html">
+     *             androidx.webkit.WebViewAssetLoader</a> instead.
      */
     // Static for backward compatibility.
+    @Deprecated
     public static void setAcceptFileSchemeCookies(boolean accept) {
         getInstance().setAcceptFileSchemeCookiesImpl(accept);
     }
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 53541f7..35dd576 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -983,48 +983,63 @@
     public abstract void setJavaScriptEnabled(boolean flag);
 
     /**
-     * Sets whether JavaScript running in the context of a file scheme URL
-     * should be allowed to access content from any origin. This includes
-     * access to content from other file scheme URLs. See
-     * {@link #setAllowFileAccessFromFileURLs}. To enable the most restrictive,
-     * and therefore secure policy, this setting should be disabled.
-     * Note that this setting affects only JavaScript access to file scheme
-     * resources. Other access to such resources, for example, from image HTML
-     * elements, is unaffected. To prevent possible violation of same domain policy
-     * when targeting {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and earlier,
-     * you should explicitly set this value to {@code false}.
+     * Sets whether cross-origin requests in the context of a file scheme URL should be allowed to
+     * access content from <i>any</i> origin. This includes access to content from other file
+     * scheme URLs or web contexts. Note that some access such as image HTML elements doesn't
+     * follow same-origin rules and isn't affected by this setting.
+     * <p>
+     * <b>Don't</b> enable this setting if you open files that may be created or altered by
+     * external sources. Enabling this setting allows malicious scripts loaded in a {@code file://}
+     * context to launch cross-site scripting attacks, either accessing arbitrary local files
+     * including WebView cookies, app private data or even credentials used on arbitrary web sites.
+     * <p class="note">
+     * Loading content via {@code file://} URLs is generally discouraged. See the note in
+     * {@link #setAllowFileAccess}.
      * <p>
      * The default value is {@code true} for apps targeting
-     * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below,
-     * and {@code false} when targeting {@link android.os.Build.VERSION_CODES#JELLY_BEAN}
-     * and above.
+     * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below, and {@code false}
+     * when targeting {@link android.os.Build.VERSION_CODES#JELLY_BEAN} and above. To prevent
+     * possible violation of same domain policy when targeting
+     * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and earlier, you should
+     * explicitly set this value to {@code false}.
      *
-     * @param flag whether JavaScript running in the context of a file scheme
-     *             URL should be allowed to access content from any origin
+     * @param flag whether JavaScript running in the context of a file scheme URL should be allowed
+     *             to access content from any origin
+     * @deprecated This setting is not secure, please use
+     *             <a href="{@docRoot}reference/androidx/webkit/WebViewAssetLoader.html">
+     *             androidx.webkit.WebViewAssetLoader</a> to load file content securely.
      */
+    @Deprecated
     public abstract void setAllowUniversalAccessFromFileURLs(boolean flag);
 
     /**
-     * Sets whether JavaScript running in the context of a file scheme URL
-     * should be allowed to access content from other file scheme URLs. To
-     * enable the most restrictive, and therefore secure, policy this setting
-     * should be disabled. Note that the value of this setting is ignored if
-     * the value of {@link #getAllowUniversalAccessFromFileURLs} is {@code true}.
-     * Note too, that this setting affects only JavaScript access to file scheme
-     * resources. Other access to such resources, for example, from image HTML
-     * elements, is unaffected. To prevent possible violation of same domain policy
-     * when targeting {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and earlier,
-     * you should explicitly set this value to {@code false}.
+     * Sets whether cross-origin requests in the context of a file scheme URL should be allowed to
+     * access content from other file scheme URLs. Note that some accesses such as image HTML
+     * elements don't follow same-origin rules and aren't affected by this setting.
      * <p>
-     * The default value is {@code true} for apps targeting
-     * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below,
-     * and {@code false} when targeting {@link android.os.Build.VERSION_CODES#JELLY_BEAN}
-     * and above.
+     * <b>Don't</b> enable this setting if you open files that may be created or altered by
+     * external sources. Enabling this setting allows malicious scripts loaded in a {@code file://}
+     * context to access arbitrary local files including WebView cookies and app private data.
+     * <p class="note">
+     * Loading content via {@code file://} URLs is generally discouraged. See the note in
+     * {@link #setAllowFileAccess}.
+     * <p>
+     * Note that the value of this setting is ignored if the value of
+     * {@link #getAllowUniversalAccessFromFileURLs} is {@code true}. The default value is
+     * {@code true} for apps targeting {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1}
+     * and below, and {@code false} when targeting {@link android.os.Build.VERSION_CODES#JELLY_BEAN}
+     * and above. To prevent possible violation of same domain policy when targeting
+     * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and earlier, you should
+     * explicitly set this value to {@code false}.
      *
      * @param flag whether JavaScript running in the context of a file scheme
      *             URL should be allowed to access content from other file
      *             scheme URLs
+     * @deprecated This setting is not secure, please use
+     *             <a href="{@docRoot}reference/androidx/webkit/WebViewAssetLoader.html">
+     *             androidx.webkit.WebViewAssetLoader</a> to load file content securely.
      */
+    @Deprecated
     public abstract void setAllowFileAccessFromFileURLs(boolean flag);
 
     /**
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 13c1f67..816612f 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -4587,7 +4587,8 @@
         protected int mHorizontalGravity;
         // Offsets the hotspot point up, so that cursor is not hidden by the finger when moving up
         private float mTouchOffsetY;
-        // Where the touch position should be on the handle to ensure a maximum cursor visibility
+        // Where the touch position should be on the handle to ensure a maximum cursor visibility.
+        // This is the distance in pixels from the top of the handle view.
         private float mIdealVerticalOffset;
         // Parent's (TextView) previous position in window
         private int mLastParentX, mLastParentY;
@@ -4612,6 +4613,11 @@
         // when magnifier is used.
         private float mTextViewScaleX;
         private float mTextViewScaleY;
+        /**
+         * The vertical distance in pixels from finger to the cursor Y while dragging.
+         * See {@link Editor.InsertionPointCursorController#getLineDuringDrag}.
+         */
+        private final int mIdealFingerToCursorOffset;
 
         private HandleView(Drawable drawableLtr, Drawable drawableRtl, final int id) {
             super(mTextView.getContext());
@@ -4633,12 +4639,17 @@
             final int handleHeight = getPreferredHeight();
             mTouchOffsetY = -0.3f * handleHeight;
             mIdealVerticalOffset = 0.7f * handleHeight;
+            mIdealFingerToCursorOffset = (int)(mIdealVerticalOffset - mTouchOffsetY);
         }
 
         public float getIdealVerticalOffset() {
             return mIdealVerticalOffset;
         }
 
+        final int getIdealFingerToCursorOffset() {
+            return mIdealFingerToCursorOffset;
+        }
+
         void setDrawables(final Drawable drawableLtr, final Drawable drawableRtl) {
             mDrawableLtr = drawableLtr;
             mDrawableRtl = drawableRtl;
@@ -6123,36 +6134,34 @@
          */
         private int getLineDuringDrag(MotionEvent event) {
             final Layout layout = mTextView.getLayout();
-            if (mTouchState.isOnHandle()) {
-                // The drag was initiated from the handle, so no need to apply the snap logic. See
-                // InsertionHandleView.touchThrough().
+            if (mPrevLineDuringDrag == UNSET_LINE) {
                 return getCurrentLineAdjustedForSlop(layout, mPrevLineDuringDrag, event.getY());
             }
+            // In case of touch through on handle (when isOnHandle() returns true), event.getY()
+            // returns the midpoint of the cursor vertical bar, while event.getRawY() returns the
+            // finger location on the screen. See {@link InsertionHandleView#touchThrough}.
+            final float fingerY = mTouchState.isOnHandle()
+                    ? event.getRawY() - mTextView.getLocationOnScreen()[1]
+                    : event.getY();
+            final float cursorY = fingerY - getHandle().getIdealFingerToCursorOffset();
+            int line = getCurrentLineAdjustedForSlop(layout, mPrevLineDuringDrag, cursorY);
             if (mIsTouchSnappedToHandleDuringDrag) {
-                float cursorY = event.getY() - getHandle().getIdealVerticalOffset();
-                return getCurrentLineAdjustedForSlop(layout, mPrevLineDuringDrag, cursorY);
-            }
-            int line = getCurrentLineAdjustedForSlop(layout, mPrevLineDuringDrag, event.getY());
-            if (mPrevLineDuringDrag == UNSET_LINE || line <= mPrevLineDuringDrag) {
-                // User's finger is on the same line or moving up; continue positioning the cursor
-                // directly at the touch location.
+                // Just returns the line hit by cursor Y when already snapped.
                 return line;
             }
-            // User's finger is moving downwards; delay jumping to the lower line to allow the
-            // touch to move to the handle.
-            float cursorY = event.getY() - getHandle().getIdealVerticalOffset();
-            line = getCurrentLineAdjustedForSlop(layout, mPrevLineDuringDrag, cursorY);
             if (line < mPrevLineDuringDrag) {
-                return mPrevLineDuringDrag;
+                // The cursor Y aims too high & not yet snapped, check the finger Y.
+                // If finger Y is moving downwards, don't jump to lower line (until snap).
+                // If finger Y is moving upwards, can jump to upper line.
+                return Math.min(mPrevLineDuringDrag,
+                        getCurrentLineAdjustedForSlop(layout, mPrevLineDuringDrag, fingerY));
             }
-            // User's finger is now over the handle, at the ideal offset from the cursor. From now
-            // on, position the cursor higher up from the actual touch location so that the user's
-            // finger stays "snapped" to the handle. This provides better visibility of the text.
+            // The cursor Y aims not too high, so snap!
             mIsTouchSnappedToHandleDuringDrag = true;
             if (TextView.DEBUG_CURSOR) {
                 logCursor("InsertionPointCursorController",
-                        "snapped touch to handle: eventY=%d, cursorY=%d, mLastLine=%d, line=%d",
-                        (int) event.getY(), (int) cursorY, mPrevLineDuringDrag, line);
+                        "snapped touch to handle: fingerY=%d, cursorY=%d, mLastLine=%d, line=%d",
+                        (int) fingerY, (int) cursorY, mPrevLineDuringDrag, line);
             }
             return line;
         }
@@ -6252,7 +6261,7 @@
             }
         }
 
-        private InsertionHandleView getHandle() {
+        public InsertionHandleView getHandle() {
             if (mHandle == null) {
                 loadHandleDrawables(false /* overwrite */);
                 mHandle = new InsertionHandleView(mSelectHandleCenter);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f3243aa..0d82065 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -11763,13 +11763,8 @@
             if (isTextEditable() && isFocused()) {
                 CharSequence imeActionLabel = mContext.getResources().getString(
                         com.android.internal.R.string.keyboardview_keycode_enter);
-                if (getImeActionId() != 0 && getImeActionLabel() != null) {
+                if (getImeActionLabel() != null) {
                     imeActionLabel = getImeActionLabel();
-                    final int imeActionId = getImeActionId();
-                    // put ime action id into the extra data with ACTION_ARGUMENT_IME_ACTION_ID_INT.
-                    final Bundle argument = info.getExtras();
-                    argument.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_IME_ACTION_ID_INT,
-                            imeActionId);
                 }
                 AccessibilityNodeInfo.AccessibilityAction action =
                         new AccessibilityNodeInfo.AccessibilityAction(
@@ -11881,7 +11876,7 @@
         if (extraDataKey.equals(AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY)) {
             final AccessibilityNodeInfo.ExtraRenderingInfo extraRenderingInfo =
                     AccessibilityNodeInfo.ExtraRenderingInfo.obtain();
-            extraRenderingInfo.setLayoutParams(getLayoutParams().width, getLayoutParams().height);
+            extraRenderingInfo.setLayoutSize(getLayoutParams().width, getLayoutParams().height);
             extraRenderingInfo.setTextSizeInPx(getTextSize());
             extraRenderingInfo.setTextSizeUnit(getTextSizeUnit());
             info.setExtraRenderingInfo(extraRenderingInfo);
@@ -12100,13 +12095,7 @@
             } return true;
             case R.id.accessibilityActionImeEnter: {
                 if (isFocused() && isTextEditable()) {
-                    final int imeActionId = (arguments != null) ? arguments.getInt(
-                            AccessibilityNodeInfo.ACTION_ARGUMENT_IME_ACTION_ID_INT,
-                            EditorInfo.IME_ACTION_UNSPECIFIED)
-                            : EditorInfo.IME_ACTION_UNSPECIFIED;
-                    if (imeActionId == getImeActionId()) {
-                        onEditorAction(imeActionId);
-                    }
+                    onEditorAction(getImeActionId());
                 }
             } return true;
             default: {
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 5cdcab0..54ea57a 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -56,6 +56,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
@@ -133,7 +134,7 @@
         // Keep track of state of shortcut settings
         final ContentObserver co = new ContentObserver(handler) {
             @Override
-            public void onChange(boolean selfChange, Uri uri, int userId) {
+            public void onChange(boolean selfChange, Collection<Uri> uris, int flags, int userId) {
                 if (userId == mUserId) {
                     onSettingsChanged();
                 }
diff --git a/core/java/com/android/internal/compat/OverrideAllowedState.java b/core/java/com/android/internal/compat/OverrideAllowedState.java
index bed41b3..9a78ad2 100644
--- a/core/java/com/android/internal/compat/OverrideAllowedState.java
+++ b/core/java/com/android/internal/compat/OverrideAllowedState.java
@@ -50,7 +50,7 @@
     public static final int DISABLED_NOT_DEBUGGABLE = 1;
     /**
      * Change cannot be overridden, due to the build being non-debuggable and the change being
-     * non-targetSdk.
+     * enabled regardless of targetSdk.
      */
     public static final int DISABLED_NON_TARGET_SDK = 2;
     /**
@@ -159,4 +159,28 @@
                 && appTargetSdk == otherState.appTargetSdk
                 && changeIdTargetSdk == otherState.changeIdTargetSdk;
     }
+
+    private String stateName() {
+        switch (state) {
+            case ALLOWED:
+                return "ALLOWED";
+            case DISABLED_NOT_DEBUGGABLE:
+                return "DISABLED_NOT_DEBUGGABLE";
+            case DISABLED_NON_TARGET_SDK:
+                return "DISABLED_NON_TARGET_SDK";
+            case DISABLED_TARGET_SDK_TOO_HIGH:
+                return "DISABLED_TARGET_SDK_TOO_HIGH";
+            case PACKAGE_DOES_NOT_EXIST:
+                return "PACKAGE_DOES_NOT_EXIST";
+            case LOGGING_ONLY_CHANGE:
+                return "LOGGING_ONLY_CHANGE";
+        }
+        return "UNKNOWN";
+    }
+
+    @Override
+    public String toString() {
+        return "OverrideAllowedState(state=" + stateName() + "; appTargetSdk=" + appTargetSdk
+                + "; changeIdTargetSdk=" + changeIdTargetSdk + ")";
+    }
 }
diff --git a/core/java/com/android/internal/logging/InstanceId.java b/core/java/com/android/internal/logging/InstanceId.java
index 85643fc..c90d8512 100644
--- a/core/java/com/android/internal/logging/InstanceId.java
+++ b/core/java/com/android/internal/logging/InstanceId.java
@@ -48,6 +48,17 @@
         return mId;
     }
 
+    /**
+     * Create a fake instance ID for testing purposes.  Not for production use. See also
+     * InstanceIdSequenceFake, which is a testing replacement for InstanceIdSequence.
+     * @param id The ID you want to assign.
+     * @return new InstanceId.
+     */
+    @VisibleForTesting
+    public static InstanceId fakeInstanceId(int id) {
+        return new InstanceId(id);
+    }
+
     @Override
     public int hashCode() {
         return mId;
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java b/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java
index f8c0d9e..fdcc8a8 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java
@@ -29,6 +29,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.function.Predicate;
 import java.util.regex.Matcher;
@@ -99,7 +100,7 @@
     }
 
     @Override
-    public void onChange(boolean selfChange, Uri uri, int userId) {
+    public void onChange(boolean selfChange, Collection<Uri> uris, int flags, int userId) {
         updateReader();
     }
 
diff --git a/core/java/com/android/internal/util/NotificationMessagingUtil.java b/core/java/com/android/internal/util/NotificationMessagingUtil.java
index bf796cd..28994fd 100644
--- a/core/java/com/android/internal/util/NotificationMessagingUtil.java
+++ b/core/java/com/android/internal/util/NotificationMessagingUtil.java
@@ -28,6 +28,7 @@
 import android.service.notification.StatusBarNotification;
 import android.util.ArrayMap;
 
+import java.util.Collection;
 import java.util.Objects;
 
 /**
@@ -77,8 +78,8 @@
     private final ContentObserver mSmsContentObserver = new ContentObserver(
             new Handler(Looper.getMainLooper())) {
         @Override
-        public void onChange(boolean selfChange, Uri uri, int userId) {
-            if (Settings.Secure.getUriFor(DEFAULT_SMS_APP_SETTING).equals(uri)) {
+        public void onChange(boolean selfChange, Collection<Uri> uris, int flags, int userId) {
+            if (uris.contains(Settings.Secure.getUriFor(DEFAULT_SMS_APP_SETTING))) {
                 cacheDefaultSmsApp(userId);
             }
         }
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 3378c07..d40924b 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -82,6 +82,9 @@
     // property for runtime configuration differentiation
     private static final String SKU_PROPERTY = "ro.boot.product.hardware.sku";
 
+    // property for runtime configuration differentiation in vendor
+    private static final String VENDOR_SKU_PROPERTY = "ro.boot.product.vendor.sku";
+
     // Group-ids that are given to all packages as read from etc/permissions/*.xml.
     int[] mGlobalGids;
 
@@ -468,6 +471,17 @@
         readPermissions(Environment.buildPath(
                 Environment.getVendorDirectory(), "etc", "permissions"), vendorPermissionFlag);
 
+        String vendorSkuProperty = SystemProperties.get(VENDOR_SKU_PROPERTY, "");
+        if (!vendorSkuProperty.isEmpty()) {
+            String vendorSkuDir = "sku_" + vendorSkuProperty;
+            readPermissions(Environment.buildPath(
+                    Environment.getVendorDirectory(), "etc", "sysconfig", vendorSkuDir),
+                    vendorPermissionFlag);
+            readPermissions(Environment.buildPath(
+                    Environment.getVendorDirectory(), "etc", "permissions", vendorSkuDir),
+                    vendorPermissionFlag);
+        }
+
         // Allow ODM to customize system configs as much as Vendor, because /odm is another
         // vendor partition other than /vendor.
         int odmPermissionFlag = vendorPermissionFlag;
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 4e139a3..2128f99 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -134,6 +134,11 @@
 static bool gIsSecurityEnforced = true;
 
 /**
+ * True if the app process is running in its mount namespace.
+ */
+static bool gInAppMountNamespace = false;
+
+/**
  * The maximum number of characters (not including a null terminator) that a
  * process name may contain.
  */
@@ -548,6 +553,17 @@
   }
 }
 
+static void ensureInAppMountNamespace(fail_fn_t fail_fn) {
+  if (gInAppMountNamespace) {
+    // In app mount namespace already
+    return;
+  }
+  if (unshare(CLONE_NEWNS) == -1) {
+    fail_fn(CREATE_ERROR("Failed to unshare(): %s", strerror(errno)));
+  }
+  gInAppMountNamespace = true;
+}
+
 // Sets the resource limits via setrlimit(2) for the values in the
 // two-dimensional array of integers that's passed in. The second dimension
 // contains a tuple of length 3: (resource, rlim_cur, rlim_max). nullptr is
@@ -811,9 +827,7 @@
   }
 
   // Create a second private mount namespace for our process
-  if (unshare(CLONE_NEWNS) == -1) {
-    fail_fn(CREATE_ERROR("Failed to unshare(): %s", strerror(errno)));
-  }
+  ensureInAppMountNamespace(fail_fn);
 
   // Handle force_mount_namespace with MOUNT_EXTERNAL_NONE.
   if (mount_mode == MOUNT_EXTERNAL_NONE) {
@@ -1319,6 +1333,7 @@
   if ((size % 3) != 0) {
     fail_fn(CREATE_ERROR("Wrong pkg_inode_list size %d", size));
   }
+  ensureInAppMountNamespace(fail_fn);
 
   // Mount tmpfs on all possible data directories, so app no longer see the original apps data.
   char internalCePath[PATH_MAX];
diff --git a/core/proto/android/bluetooth/enums.proto b/core/proto/android/bluetooth/enums.proto
index b4f3d1e..22f2498 100644
--- a/core/proto/android/bluetooth/enums.proto
+++ b/core/proto/android/bluetooth/enums.proto
@@ -40,6 +40,7 @@
     ENABLE_DISABLE_REASON_CRASH = 7;
     ENABLE_DISABLE_REASON_USER_SWITCH = 8;
     ENABLE_DISABLE_REASON_RESTORE_USER_SETTING = 9;
+    ENABLE_DISABLE_REASON_FACTORY_RESET = 10;
 }
 
 enum DirectionEnum {
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index e8a0b46..608925578 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -53,10 +53,10 @@
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
     optional WindowContainerProto window_container = 1;
-    repeated DisplayContentProto displays = 2;
+    repeated DisplayContentProto displays = 2 [deprecated=true];
     reserved 3; // IdentifierProto windows
     /* window references in top down z order */
-    repeated WindowStateProto windows = 4;
+    repeated WindowStateProto windows = 4 [deprecated=true];
     optional KeyguardControllerProto keyguard_controller = 5;
     // Whether or not the home activity is the recents activity. This is needed for the CTS tests to
     // know what activity types to check for when invoking splitscreen multi-window.
@@ -192,7 +192,8 @@
     optional bool single_task_instance = 22;
     optional int32 focused_root_task_id = 23;
     optional .com.android.server.wm.IdentifierProto resumed_activity = 24;
-    repeated TaskProto tasks = 25;
+    repeated TaskProto tasks = 25 [deprecated=true];
+    optional bool display_ready = 26;
 }
 
 /* represents DisplayArea object */
@@ -201,7 +202,7 @@
 
     optional WindowContainerProto window_container = 1;
     optional string name = 2 [ (.android.privacy).dest = DEST_EXPLICIT ];
-    repeated DisplayAreaChildProto children = 3;
+    repeated DisplayAreaChildProto children = 3 [deprecated=true];
 }
 
 /* represents a generic child of a DisplayArea */
@@ -254,8 +255,8 @@
     optional int32 surface_width = 8;
     optional int32 surface_height = 9;
 
-    repeated TaskProto tasks = 10;
-    repeated ActivityRecordProto activities = 11;
+    repeated TaskProto tasks = 10 [deprecated=true];
+    repeated ActivityRecordProto activities = 11 [deprecated=true];
 
     optional .com.android.server.wm.IdentifierProto resumed_activity = 12;
     optional string real_activity = 13;
@@ -321,7 +322,7 @@
 
     optional WindowContainerProto window_container = 1;
     optional int32 hash_code = 2;
-    repeated WindowStateProto windows = 3;
+    repeated WindowStateProto windows = 3 [deprecated=true];
     optional bool waiting_to_show = 5;
     optional bool paused = 6;
 }
@@ -346,7 +347,7 @@
     optional .android.graphics.RectProto surface_insets = 12;
     optional WindowStateAnimatorProto animator = 13;
     optional bool animating_exit = 14;
-    repeated WindowStateProto child_windows = 15;
+    repeated WindowStateProto child_windows = 15 [deprecated=true];
     optional .android.graphics.RectProto surface_position = 16;
     optional int32 requested_width = 18;
     optional int32 requested_height = 19;
@@ -426,6 +427,32 @@
     optional int32 orientation = 2;
     optional bool visible = 3;
     optional SurfaceAnimatorProto surface_animator = 4;
+    repeated WindowContainerChildProto children = 5;
+}
+
+/* represents a generic child of a WindowContainer */
+message WindowContainerChildProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    /* A window container can have multiple children of different types stored as
+     * a WindowContainerChildProto but each instance of WindowContainerChildProto
+     * can only contain a single type.
+     */
+    /* We do not know the derived typ and the class is dumped
+     * as a WindowContainer */
+    optional WindowContainerProto window_container = 2;
+    /* represents a DisplayContent child */
+    optional DisplayContentProto display_content = 3;
+    /* represents a DisplayArea child */
+    optional DisplayAreaProto display_area = 4;
+    /* represents a Task child */
+    optional TaskProto task = 5;
+    /* represents an ActivityRecord child */
+    optional ActivityRecordProto activity = 6;
+    /* represents a WindowToken child */
+    optional WindowTokenProto window_token = 7;
+    /* represents a WindowState child */
+    optional WindowStateProto window = 8;
 }
 
 /* represents ConfigurationContainer */
diff --git a/core/proto/android/view/remote_animation_target.proto b/core/proto/android/view/remote_animation_target.proto
index 24d2785..150493d 100644
--- a/core/proto/android/view/remote_animation_target.proto
+++ b/core/proto/android/view/remote_animation_target.proto
@@ -44,4 +44,6 @@
     optional .android.app.WindowConfigurationProto window_configuration = 10;
     optional .android.view.SurfaceControlProto start_leash = 11;
     optional .android.graphics.RectProto start_bounds = 12;
+    optional .android.graphics.RectProto local_bounds = 13;
+    optional .android.graphics.RectProto screen_space_bounds = 14;
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8851170..dac6576 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1305,7 +1305,7 @@
         android:description="@string/permdesc_camera"
         android:protectionLevel="dangerous|instant" />
 
-      <!-- @SystemApi Required in addition to android.permission.CAMERA to be able to access
+    <!-- @SystemApi Required in addition to android.permission.CAMERA to be able to access
            system only camera devices.
            <p>Protection level: system|signature
            @hide -->
@@ -1315,6 +1315,15 @@
         android:description="@string/permdesc_systemCamera"
         android:protectionLevel="system|signature" />
 
+    <!-- Allows receiving the camera service notifications when a camera is opened
+            (by a certain application package) or closed.
+        @hide -->
+    <permission android:name="android.permission.CAMERA_OPEN_CLOSE_LISTENER"
+        android:permissionGroup="android.permission-group.UNDEFINED"
+        android:label="@string/permlab_cameraOpenCloseListener"
+        android:description="@string/permdesc_cameraOpenCloseListener"
+        android:protectionLevel="signature" />
+
     <!-- ====================================================================== -->
     <!-- Permissions for accessing the device sensors                           -->
     <!-- ====================================================================== -->
@@ -3726,7 +3735,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" />
 
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index caec9ed..632c400 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1631,12 +1631,9 @@
     <string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Verhoog volume bo aanbevole vlak?\n\nOm lang tydperke teen hoë volume te luister, kan jou gehoor beskadig."</string>
     <string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Gebruik toeganklikheidkortpad?"</string>
     <string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Wanneer die kortpad aan is, sal \'n toeganklikheidkenmerk begin word as albei volumeknoppies 3 sekondes lank gedruk word."</string>
-    <!-- no translation found for accessibility_select_shortcut_menu_title (7310194076629867377) -->
-    <skip />
-    <!-- no translation found for accessibility_edit_shortcut_menu_button_title (6096484087245145325) -->
-    <skip />
-    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (4849108668454490699) -->
-    <skip />
+    <string name="accessibility_select_shortcut_menu_title" msgid="7310194076629867377">"Tik op die toeganklikheidprogram wat jy wil gebruik"</string>
+    <string name="accessibility_edit_shortcut_menu_button_title" msgid="6096484087245145325">"Kies programme wat jy met toeganklikheidknoppie wil gebruik"</string>
+    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="4849108668454490699">"Kies programme wat jy met die volumesleutelkortpad wil gebruik"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Wysig kortpaaie"</string>
     <string name="cancel_accessibility_shortcut_menu_button" msgid="1817413122335452474">"Kanselleer"</string>
     <string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Skakel kortpad af"</string>
@@ -2033,29 +2030,19 @@
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> is in die BEPERK-groep geplaas"</string>
     <string name="resolver_personal_tab" msgid="2051260504014442073">"Persoonlik"</string>
     <string name="resolver_work_tab" msgid="2690019516263167035">"Werk"</string>
-    <!-- no translation found for resolver_personal_tab_accessibility (5739524949153091224) -->
-    <skip />
-    <!-- no translation found for resolver_work_tab_accessibility (4753168230363802734) -->
-    <skip />
+    <string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"Persoonlike aansig"</string>
+    <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"Werkaansig"</string>
     <string name="resolver_cant_share_with_work_apps" msgid="7539495559434146897">"Kan nie met werkprogramme deel nie"</string>
     <string name="resolver_cant_share_with_personal_apps" msgid="8020581735267157241">"Kan nie met persoonlike programme deel nie"</string>
-    <!-- no translation found for resolver_cant_share_cross_profile_explanation (5556640604460901386) -->
-    <skip />
-    <!-- no translation found for resolver_cant_access_work_apps (375634344111233790) -->
-    <skip />
-    <!-- no translation found for resolver_cant_access_work_apps_explanation (3958762224516867388) -->
-    <skip />
-    <!-- no translation found for resolver_cant_access_personal_apps (1953215925406474177) -->
-    <skip />
-    <!-- no translation found for resolver_cant_access_personal_apps_explanation (1725572276741281136) -->
-    <skip />
-    <!-- no translation found for resolver_turn_on_work_apps_share (619263911204978175) -->
-    <skip />
-    <!-- no translation found for resolver_turn_on_work_apps_view (3073389230905543680) -->
-    <skip />
+    <string name="resolver_cant_share_cross_profile_explanation" msgid="5556640604460901386">"Jou IT-admin het deling tussen persoonlike en werkprofiele geblokkeer"</string>
+    <string name="resolver_cant_access_work_apps" msgid="375634344111233790">"Kan nie toegang tot werkprogramme kry nie"</string>
+    <string name="resolver_cant_access_work_apps_explanation" msgid="3958762224516867388">"Jou IT-admin laat jou nie toe om persoonlike inhoud in werkprogramme te bekyk nie"</string>
+    <string name="resolver_cant_access_personal_apps" msgid="1953215925406474177">"Kan nie toegang tot persoonlike programme kry nie"</string>
+    <string name="resolver_cant_access_personal_apps_explanation" msgid="1725572276741281136">"Jou IT-admin laat jou nie toe om werkinhoud in persoonlike programme te bekyk nie"</string>
+    <string name="resolver_turn_on_work_apps_share" msgid="619263911204978175">"Skakel werkprofiel aan om inhoud te deel"</string>
+    <string name="resolver_turn_on_work_apps_view" msgid="3073389230905543680">"Skakel werkprofiel aan om inhoud te bekyk"</string>
     <string name="resolver_no_apps_available" msgid="7710339903040989654">"Geen programme beskikbaar nie"</string>
-    <!-- no translation found for resolver_switch_on_work (2873009160846966379) -->
-    <skip />
+    <string name="resolver_switch_on_work" msgid="2873009160846966379">"Skakel aan"</string>
     <string name="permlab_accessCallAudio" msgid="1682957511874097664">"Neem oudio in telefonie-oproepe op of speel dit"</string>
     <string name="permdesc_accessCallAudio" msgid="8448360894684277823">"Laat hierdie program, indien dit as die verstekbellerprogram aangewys is, toe om oudio in telefonie-oproepe op te neem of te speel."</string>
 </resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 63497aa..1a7f301 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1631,12 +1631,9 @@
     <string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"推奨レベルを超えるまで音量を上げますか?\n\n大音量で長時間聞き続けると、聴力を損なう恐れがあります。"</string>
     <string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ユーザー補助機能のショートカットの使用"</string>
     <string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ショートカットが ON の場合、両方の音量ボタンを 3 秒ほど長押しするとユーザー補助機能が起動します。"</string>
-    <!-- no translation found for accessibility_select_shortcut_menu_title (7310194076629867377) -->
-    <skip />
-    <!-- no translation found for accessibility_edit_shortcut_menu_button_title (6096484087245145325) -->
-    <skip />
-    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (4849108668454490699) -->
-    <skip />
+    <string name="accessibility_select_shortcut_menu_title" msgid="7310194076629867377">"使用するユーザー補助アプリをタップ"</string>
+    <string name="accessibility_edit_shortcut_menu_button_title" msgid="6096484087245145325">"ユーザー補助機能ボタンで使用できるアプリを選択"</string>
+    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="4849108668454490699">"音量キーのショートカットで使用できるアプリを選択"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"ショートカットの編集"</string>
     <string name="cancel_accessibility_shortcut_menu_button" msgid="1817413122335452474">"キャンセル"</string>
     <string name="disable_accessibility_shortcut" msgid="5806091378745232383">"ショートカットを OFF にする"</string>
@@ -2033,29 +2030,19 @@
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> は RESTRICTED バケットに移動しました。"</string>
     <string name="resolver_personal_tab" msgid="2051260504014442073">"個人用"</string>
     <string name="resolver_work_tab" msgid="2690019516263167035">"仕事用"</string>
-    <!-- no translation found for resolver_personal_tab_accessibility (5739524949153091224) -->
-    <skip />
-    <!-- no translation found for resolver_work_tab_accessibility (4753168230363802734) -->
-    <skip />
+    <string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"個人用ビュー"</string>
+    <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"仕事用ビュー"</string>
     <string name="resolver_cant_share_with_work_apps" msgid="7539495559434146897">"仕事用アプリと共有できません"</string>
     <string name="resolver_cant_share_with_personal_apps" msgid="8020581735267157241">"個人用アプリと共有できません"</string>
-    <!-- no translation found for resolver_cant_share_cross_profile_explanation (5556640604460901386) -->
-    <skip />
-    <!-- no translation found for resolver_cant_access_work_apps (375634344111233790) -->
-    <skip />
-    <!-- no translation found for resolver_cant_access_work_apps_explanation (3958762224516867388) -->
-    <skip />
-    <!-- no translation found for resolver_cant_access_personal_apps (1953215925406474177) -->
-    <skip />
-    <!-- no translation found for resolver_cant_access_personal_apps_explanation (1725572276741281136) -->
-    <skip />
-    <!-- no translation found for resolver_turn_on_work_apps_share (619263911204978175) -->
-    <skip />
-    <!-- no translation found for resolver_turn_on_work_apps_view (3073389230905543680) -->
-    <skip />
+    <string name="resolver_cant_share_cross_profile_explanation" msgid="5556640604460901386">"IT 管理者が個人用プロファイルと仕事用プロファイルの間の共有をブロックしました"</string>
+    <string name="resolver_cant_access_work_apps" msgid="375634344111233790">"仕事用アプリにアクセスできません"</string>
+    <string name="resolver_cant_access_work_apps_explanation" msgid="3958762224516867388">"IT 管理者は個人用コンテンツを仕事用アプリで表示することを許可していません"</string>
+    <string name="resolver_cant_access_personal_apps" msgid="1953215925406474177">"個人用アプリにアクセスできません"</string>
+    <string name="resolver_cant_access_personal_apps_explanation" msgid="1725572276741281136">"IT 管理者は仕事用コンテンツを個人用アプリで表示することを許可していません"</string>
+    <string name="resolver_turn_on_work_apps_share" msgid="619263911204978175">"コンテンツを共有するには、仕事用プロファイルをオンにしてください"</string>
+    <string name="resolver_turn_on_work_apps_view" msgid="3073389230905543680">"コンテンツを表示するには、仕事用プロファイルをオンにしてください"</string>
     <string name="resolver_no_apps_available" msgid="7710339903040989654">"利用できるアプリはありません"</string>
-    <!-- no translation found for resolver_switch_on_work (2873009160846966379) -->
-    <skip />
+    <string name="resolver_switch_on_work" msgid="2873009160846966379">"オンにする"</string>
     <string name="permlab_accessCallAudio" msgid="1682957511874097664">"通話中に録音または音声の再生を行う"</string>
     <string name="permdesc_accessCallAudio" msgid="8448360894684277823">"デフォルトの電話アプリケーションとして割り当てられている場合、このアプリに通話中の録音または音声の再生を許可します。"</string>
 </resources>
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-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 7b17305..4054a3a 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1631,12 +1631,9 @@
     <string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Дууг санал болгосноос чанга болгож өсгөх үү?\n\nУрт хугацаанд чанга хөгжим сонсох нь таны сонсголыг муутгаж болно."</string>
     <string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Хүртээмжийн товчлолыг ашиглах уу?"</string>
     <string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Товчлол асаалттай үед дууны түвшний хоёр товчлуурыг хамтад нь 3 секунд дарснаар хандалтын онцлогийг эхлүүлнэ."</string>
-    <!-- no translation found for accessibility_select_shortcut_menu_title (7310194076629867377) -->
-    <skip />
-    <!-- no translation found for accessibility_edit_shortcut_menu_button_title (6096484087245145325) -->
-    <skip />
-    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (4849108668454490699) -->
-    <skip />
+    <string name="accessibility_select_shortcut_menu_title" msgid="7310194076629867377">"Ашиглахыг хүсэж буй хандалтын аппаа товших"</string>
+    <string name="accessibility_edit_shortcut_menu_button_title" msgid="6096484087245145325">"Хандалтын товчлуурын тусламжтай ашиглахыг хүсэж буй аппуудаа сонгох"</string>
+    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="4849108668454490699">"Дууны түвшин тохируулах түлхүүрийн товчлолын тусламжтай ашиглахыг хүсэж буй аппуудаа сонгох"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Товчлолуудыг засах"</string>
     <string name="cancel_accessibility_shortcut_menu_button" msgid="1817413122335452474">"Болих"</string>
     <string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Товчлолыг унтраах"</string>
@@ -2033,29 +2030,19 @@
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>-г ХЯЗГААРЛАСАН сагс руу орууллаа"</string>
     <string name="resolver_personal_tab" msgid="2051260504014442073">"Хувийн"</string>
     <string name="resolver_work_tab" msgid="2690019516263167035">"Ажил"</string>
-    <!-- no translation found for resolver_personal_tab_accessibility (5739524949153091224) -->
-    <skip />
-    <!-- no translation found for resolver_work_tab_accessibility (4753168230363802734) -->
-    <skip />
+    <string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"Хувийн харагдах байдал"</string>
+    <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"Ажлын харагдах байдал"</string>
     <string name="resolver_cant_share_with_work_apps" msgid="7539495559434146897">"Ажлын аппуудтай хуваалцах боломжгүй"</string>
     <string name="resolver_cant_share_with_personal_apps" msgid="8020581735267157241">"Хувийн аппуудтай хуваалцах боломжгүй"</string>
-    <!-- no translation found for resolver_cant_share_cross_profile_explanation (5556640604460901386) -->
-    <skip />
-    <!-- no translation found for resolver_cant_access_work_apps (375634344111233790) -->
-    <skip />
-    <!-- no translation found for resolver_cant_access_work_apps_explanation (3958762224516867388) -->
-    <skip />
-    <!-- no translation found for resolver_cant_access_personal_apps (1953215925406474177) -->
-    <skip />
-    <!-- no translation found for resolver_cant_access_personal_apps_explanation (1725572276741281136) -->
-    <skip />
-    <!-- no translation found for resolver_turn_on_work_apps_share (619263911204978175) -->
-    <skip />
-    <!-- no translation found for resolver_turn_on_work_apps_view (3073389230905543680) -->
-    <skip />
+    <string name="resolver_cant_share_cross_profile_explanation" msgid="5556640604460901386">"Таны IT админ хувийн болон ажлын профайлуудын хооронд хуваалцахыг блоклосон"</string>
+    <string name="resolver_cant_access_work_apps" msgid="375634344111233790">"Ажлын аппуудад хандах боломжгүй байна"</string>
+    <string name="resolver_cant_access_work_apps_explanation" msgid="3958762224516867388">"Таны IT админ ажлын аппууд дахь хувийн контентыг харахыг танд зөвшөөрөхгүй байна"</string>
+    <string name="resolver_cant_access_personal_apps" msgid="1953215925406474177">"Хувийн аппуудад хандах боломжгүй байна"</string>
+    <string name="resolver_cant_access_personal_apps_explanation" msgid="1725572276741281136">"Таны IT админ хувийн аппууд дахь ажлын контентыг харахыг танд зөвшөөрөхгүй байна"</string>
+    <string name="resolver_turn_on_work_apps_share" msgid="619263911204978175">"Контентыг хуваалцахын тулд ажлын профайлыг асаана уу"</string>
+    <string name="resolver_turn_on_work_apps_view" msgid="3073389230905543680">"Контентыг харахын тулд ажлын профайлыг асаана уу"</string>
     <string name="resolver_no_apps_available" msgid="7710339903040989654">"Боломжтой апп алга байна"</string>
-    <!-- no translation found for resolver_switch_on_work (2873009160846966379) -->
-    <skip />
+    <string name="resolver_switch_on_work" msgid="2873009160846966379">"Асаах"</string>
     <string name="permlab_accessCallAudio" msgid="1682957511874097664">"Утасны дуудлагын үеэр аудио бичих эсвэл тоглуулах"</string>
     <string name="permdesc_accessCallAudio" msgid="8448360894684277823">"Энэ аппыг залгагч өгөгдмөл аппликэйшн болгосон үед түүнд утасны дуудлагын үеэр аудио бичих эсвэл тоглуулахыг зөвшөөрдөг."</string>
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0b6e65fe..617949d 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>
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 008c991..4b49dd3 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1166,6 +1166,11 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
     <string name="permdesc_systemCamera">This privileged | system app can take pictures and record videos using a system camera at any time. Requires the android.permission.CAMERA permission to be held by the app as well</string>
 
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
+    <string name="permlab_cameraOpenCloseListener">Allow an application or service to receive callbacks about camera devices being opened or closed.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
+    <string name="permdesc_cameraOpenCloseListener">This signature app can receive callbacks when any camera device is being opened (by what application package) or closed.</string>
+
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_vibrate">control vibration</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -3402,8 +3407,6 @@
     <!-- A notification is shown when connected network without internet due to private dns validation failed. This is the notification's message. [CHAR LIMIT=NONE] -->
     <string name="private_dns_broken_detailed">Private DNS server cannot be accessed</string>
 
-    <!-- A notification is shown after the user logs in to a captive portal network, to indicate that the network should now have internet connectivity. This is the message of notification. [CHAR LIMIT=50] -->
-    <string name="captive_portal_logged_in_detailed">Connected</string>
     <!-- A notification is shown when the user connects to a network that doesn't have access to some services (e.g. Push notifications may not work). This is the notification's title. [CHAR LIMIT=50] -->
     <string name="network_partial_connectivity"><xliff:g id="network_ssid" example="GoogleGuest">%1$s</xliff:g> has limited connectivity</string>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9015cc4..826379d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -688,7 +688,6 @@
   <java-symbol type="string" name="capability_title_canControlMagnification" />
   <java-symbol type="string" name="capability_desc_canPerformGestures" />
   <java-symbol type="string" name="capability_title_canPerformGestures" />
-  <java-symbol type="string" name="captive_portal_logged_in_detailed" />
   <java-symbol type="string" name="capital_off" />
   <java-symbol type="string" name="capital_on" />
   <java-symbol type="string" name="cfTemplateForwarded" />
diff --git a/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java b/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java
index c307e64..328429c 100644
--- a/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java
@@ -39,6 +39,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Collection;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -194,8 +195,8 @@
         }
 
         @Override
-        public void onChange(boolean selfChange, Uri uri, int userId) {
-            if (mExpectedUri.equals(uri) && mExpectedUserId == userId) {
+        public void onChange(boolean selfChange, Collection<Uri> uris, int flags, int userId) {
+            if (uris.contains(mExpectedUri) && mExpectedUserId == userId) {
                 mLatch.countDown();
             }
         }
diff --git a/core/tests/coretests/src/android/content/ApexContextTest.java b/core/tests/coretests/src/android/content/ApexEnvironmentTest.java
similarity index 75%
rename from core/tests/coretests/src/android/content/ApexContextTest.java
rename to core/tests/coretests/src/android/content/ApexEnvironmentTest.java
index d15c64d..438c5ae 100644
--- a/core/tests/coretests/src/android/content/ApexContextTest.java
+++ b/core/tests/coretests/src/android/content/ApexEnvironmentTest.java
@@ -28,20 +28,21 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class ApexContextTest {
+public class ApexEnvironmentTest {
 
     @Test
     public void dataDirectoryPathsAreAsExpected() {
-        ApexContext apexContext = ApexContext.getApexContext("my.apex");
+        ApexEnvironment apexEnvironment = ApexEnvironment.getApexEnvironment("my.apex");
 
         assertEquals("/data/misc/apexdata/my.apex",
-                apexContext.getDeviceProtectedDataDir().getAbsolutePath());
+                apexEnvironment.getDeviceProtectedDataDir().getAbsolutePath());
 
         assertEquals("/data/misc_de/5/apexdata/my.apex",
-                apexContext.getDeviceProtectedDataDirForUser(UserHandle.of(5)).getAbsolutePath());
+                apexEnvironment
+                        .getDeviceProtectedDataDirForUser(UserHandle.of(5)).getAbsolutePath());
 
         assertEquals("/data/misc_ce/16/apexdata/my.apex",
-                apexContext.getCredentialProtectedDataDirForUser(
+                apexEnvironment.getCredentialProtectedDataDirForUser(
                         UserHandle.of(16)).getAbsolutePath());
     }
 }
diff --git a/core/tests/coretests/src/android/os/EnvironmentTest.java b/core/tests/coretests/src/android/os/EnvironmentTest.java
index d98ceaf..c0325ca 100644
--- a/core/tests/coretests/src/android/os/EnvironmentTest.java
+++ b/core/tests/coretests/src/android/os/EnvironmentTest.java
@@ -23,7 +23,10 @@
 import static android.os.Environment.classifyExternalStorageDirectory;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
+import android.app.AppOpsManager;
 import android.content.Context;
 
 import androidx.test.InstrumentationRegistry;
@@ -40,10 +43,33 @@
 public class EnvironmentTest {
     private File dir;
 
-    private Context getContext() {
+    private static Context getContext() {
         return InstrumentationRegistry.getContext();
     }
 
+    /**
+     * Sets {@code mode} for the given {@code ops} and the given {@code uid}.
+     *
+     * <p>This method drops shell permission identity.
+     */
+    private static void setAppOpsModeForUid(int uid, int mode, String... ops) {
+        if (ops == null) {
+            return;
+        }
+        InstrumentationRegistry.getInstrumentation()
+                .getUiAutomation()
+                .adoptShellPermissionIdentity();
+        try {
+            for (String op : ops) {
+                getContext().getSystemService(AppOpsManager.class).setUidMode(op, uid, mode);
+            }
+        } finally {
+            InstrumentationRegistry.getInstrumentation()
+                    .getUiAutomation()
+                    .dropShellPermissionIdentity();
+        }
+    }
+
     @Before
     public void setUp() throws Exception {
         dir = getContext().getDir("testing", Context.MODE_PRIVATE);
@@ -101,4 +127,17 @@
         Environment.buildPath(dir, "Taxes.pdf").createNewFile();
         assertEquals(HAS_OTHER, classifyExternalStorageDirectory(dir));
     }
+
+    @Test
+    public void testIsExternalStorageManager() throws Exception {
+        assertFalse(Environment.isExternalStorageManager());
+        try {
+            setAppOpsModeForUid(Process.myUid(), AppOpsManager.MODE_ALLOWED,
+                    AppOpsManager.OPSTR_MANAGE_EXTERNAL_STORAGE);
+            assertTrue(Environment.isExternalStorageManager());
+        } finally {
+            setAppOpsModeForUid(Process.myUid(), AppOpsManager.MODE_DEFAULT,
+                    AppOpsManager.OPSTR_MANAGE_EXTERNAL_STORAGE);
+        }
+    }
 }
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 7737b1a..023fc17 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -49,6 +49,7 @@
 import android.platform.test.annotations.Presubmit;
 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 +68,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;
@@ -171,15 +174,24 @@
         mController.onControlsChanged(new InsetsSourceControl[] { control });
         assertEquals(mLeash,
                 mController.getSourceConsumer(ITYPE_STATUS_BAR).getControl().getLeash());
+        mController.addOnControllableInsetsChangedListener(
+                ((controller, typeMask) -> assertEquals(statusBars(), typeMask)));
     }
 
     @Test
     public void testControlsRevoked() {
+        OnControllableInsetsChangedListener listener
+                = mock(OnControllableInsetsChangedListener.class);
+        mController.addOnControllableInsetsChangedListener(listener);
         InsetsSourceControl control =
                 new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
         mController.onControlsChanged(new InsetsSourceControl[] { control });
         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
@@ -206,10 +218,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());
     }
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/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index 0a094c61d..f81964c 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -35,10 +35,13 @@
 
 import android.app.Activity;
 import android.app.Instrumentation;
+import android.graphics.Rect;
 import android.text.Layout;
+import android.util.ArraySet;
 import android.util.Log;
 import android.view.InputDevice;
 import android.view.MotionEvent;
+import android.view.View;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -50,11 +53,13 @@
 
 import com.google.common.base.Strings;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicLong;
 
 @RunWith(AndroidJUnit4.class)
@@ -70,6 +75,7 @@
 
     private Instrumentation mInstrumentation;
     private Activity mActivity;
+    private Set<MotionEvent> mMotionEvents = new ArraySet<>();
 
     @Before
     public void before() throws Throwable {
@@ -77,6 +83,14 @@
         mActivity = mActivityRule.getActivity();
     }
 
+    @After
+    public void after() throws Throwable {
+        for (MotionEvent event : mMotionEvents) {
+            event.recycle();
+        }
+        mMotionEvents.clear();
+    }
+
     @Test
     public void testCursorDrag_horizontal_whenTextViewContentsFitOnScreen() throws Throwable {
         String text = "Hello world!";
@@ -243,45 +257,45 @@
 
         // Simulate a tap-and-drag gesture.
         long event1Time = 1001;
-        MotionEvent event1 = downEvent(event1Time, event1Time, 5f, 10f);
+        MotionEvent event1 = downEvent(tv, event1Time, event1Time, 5f, 10f);
         mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event1));
         assertFalse(editor.getInsertionController().isCursorBeingModified());
         assertFalse(editor.getSelectionController().isCursorBeingModified());
 
         long event2Time = 1002;
-        MotionEvent event2 = moveEvent(event1Time, event2Time, 50f, 10f);
+        MotionEvent event2 = moveEvent(tv, event1Time, event2Time, 50f, 10f);
         mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event2));
         assertTrue(editor.getInsertionController().isCursorBeingModified());
         assertFalse(editor.getSelectionController().isCursorBeingModified());
 
         long event3Time = 1003;
-        MotionEvent event3 = moveEvent(event1Time, event3Time, 100f, 10f);
+        MotionEvent event3 = moveEvent(tv, event1Time, event3Time, 100f, 10f);
         mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event3));
         assertTrue(editor.getInsertionController().isCursorBeingModified());
         assertFalse(editor.getSelectionController().isCursorBeingModified());
 
         long event4Time = 2004;
-        MotionEvent event4 = upEvent(event1Time, event4Time, 100f, 10f);
+        MotionEvent event4 = upEvent(tv, event1Time, event4Time, 100f, 10f);
         mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event4));
         assertFalse(editor.getInsertionController().isCursorBeingModified());
         assertFalse(editor.getSelectionController().isCursorBeingModified());
 
         // Simulate a quick tap after the drag, near the location where the drag ended.
         long event5Time = 2005;
-        MotionEvent event5 = downEvent(event5Time, event5Time, 90f, 10f);
+        MotionEvent event5 = downEvent(tv, event5Time, event5Time, 90f, 10f);
         mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event5));
         assertFalse(editor.getInsertionController().isCursorBeingModified());
         assertFalse(editor.getSelectionController().isCursorBeingModified());
 
         long event6Time = 2006;
-        MotionEvent event6 = upEvent(event5Time, event6Time, 90f, 10f);
+        MotionEvent event6 = upEvent(tv, event5Time, event6Time, 90f, 10f);
         mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event6));
         assertFalse(editor.getInsertionController().isCursorBeingModified());
         assertFalse(editor.getSelectionController().isCursorBeingModified());
 
         // Simulate another quick tap in the same location; now selection should be triggered.
         long event7Time = 2007;
-        MotionEvent event7 = downEvent(event7Time, event7Time, 90f, 10f);
+        MotionEvent event7 = downEvent(tv, event7Time, event7Time, 90f, 10f);
         mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event7));
         assertFalse(editor.getInsertionController().isCursorBeingModified());
         assertTrue(editor.getSelectionController().isCursorBeingModified());
@@ -298,19 +312,19 @@
 
         // Simulate a mouse click and drag. This should NOT trigger a cursor drag.
         long event1Time = 1001;
-        MotionEvent event1 = mouseDownEvent(event1Time, event1Time, 20f, 30f);
+        MotionEvent event1 = mouseDownEvent(tv, event1Time, event1Time, 20f, 30f);
         mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event1));
         assertFalse(editor.getInsertionController().isCursorBeingModified());
         assertFalse(editor.getSelectionController().isCursorBeingModified());
 
         long event2Time = 1002;
-        MotionEvent event2 = mouseMoveEvent(event1Time, event2Time, 120f, 30f);
+        MotionEvent event2 = mouseMoveEvent(tv, event1Time, event2Time, 120f, 30f);
         mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event2));
         assertFalse(editor.getInsertionController().isCursorBeingModified());
         assertTrue(editor.getSelectionController().isCursorBeingModified());
 
         long event3Time = 1003;
-        MotionEvent event3 = mouseUpEvent(event1Time, event3Time, 120f, 30f);
+        MotionEvent event3 = mouseUpEvent(tv, event1Time, event3Time, 120f, 30f);
         mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event3));
         assertFalse(editor.getInsertionController().isCursorBeingModified());
         assertFalse(editor.getSelectionController().isCursorBeingModified());
@@ -327,25 +341,25 @@
 
         // Simulate a tap-and-drag gesture. This should trigger a cursor drag.
         long event1Time = 1001;
-        MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
+        MotionEvent event1 = downEvent(tv, event1Time, event1Time, 20f, 30f);
         mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event1));
         assertFalse(editor.getInsertionController().isCursorBeingModified());
         assertFalse(editor.getSelectionController().isCursorBeingModified());
 
         long event2Time = 1002;
-        MotionEvent event2 = moveEvent(event1Time, event2Time, 21f, 30f);
+        MotionEvent event2 = moveEvent(tv, event1Time, event2Time, 21f, 30f);
         mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event2));
         assertFalse(editor.getInsertionController().isCursorBeingModified());
         assertFalse(editor.getSelectionController().isCursorBeingModified());
 
         long event3Time = 1003;
-        MotionEvent event3 = moveEvent(event1Time, event3Time, 120f, 30f);
+        MotionEvent event3 = moveEvent(tv, event1Time, event3Time, 120f, 30f);
         mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event3));
         assertTrue(editor.getInsertionController().isCursorBeingModified());
         assertFalse(editor.getSelectionController().isCursorBeingModified());
 
         long event4Time = 1004;
-        MotionEvent event4 = upEvent(event1Time, event4Time, 120f, 30f);
+        MotionEvent event4 = upEvent(tv, event1Time, event4Time, 120f, 30f);
         mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event4));
         assertFalse(editor.getInsertionController().isCursorBeingModified());
         assertFalse(editor.getSelectionController().isCursorBeingModified());
@@ -362,31 +376,31 @@
 
         // Simulate a double-tap followed by a drag. This should trigger a selection drag.
         long event1Time = 1001;
-        MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
+        MotionEvent event1 = downEvent(tv, event1Time, event1Time, 20f, 30f);
         mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event1));
         assertFalse(editor.getInsertionController().isCursorBeingModified());
         assertFalse(editor.getSelectionController().isCursorBeingModified());
 
         long event2Time = 1002;
-        MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
+        MotionEvent event2 = upEvent(tv, event1Time, event2Time, 20f, 30f);
         mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event2));
         assertFalse(editor.getInsertionController().isCursorBeingModified());
         assertFalse(editor.getSelectionController().isCursorBeingModified());
 
         long event3Time = 1003;
-        MotionEvent event3 = downEvent(event3Time, event3Time, 20f, 30f);
+        MotionEvent event3 = downEvent(tv, event3Time, event3Time, 20f, 30f);
         mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event3));
         assertFalse(editor.getInsertionController().isCursorBeingModified());
         assertTrue(editor.getSelectionController().isCursorBeingModified());
 
         long event4Time = 1004;
-        MotionEvent event4 = moveEvent(event3Time, event4Time, 120f, 30f);
+        MotionEvent event4 = moveEvent(tv, event3Time, event4Time, 120f, 30f);
         mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event4));
         assertFalse(editor.getInsertionController().isCursorBeingModified());
         assertTrue(editor.getSelectionController().isCursorBeingModified());
 
         long event5Time = 1005;
-        MotionEvent event5 = upEvent(event3Time, event5Time, 120f, 30f);
+        MotionEvent event5 = upEvent(tv, event3Time, event5Time, 120f, 30f);
         mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event5));
         assertFalse(editor.getInsertionController().isCursorBeingModified());
         assertFalse(editor.getSelectionController().isCursorBeingModified());
@@ -403,7 +417,7 @@
 
         // Simulate a tap. No error should be thrown.
         long event1Time = 1001;
-        MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
+        MotionEvent event1 = downEvent(tv, event1Time, event1Time, 20f, 30f);
         mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event1));
 
         // Swipe left to right. No error should be thrown.
@@ -440,7 +454,8 @@
     public void testCursorDrag_snapToHandle() throws Throwable {
         String text = "line1: This is the 1st line: A\n"
                     + "line2: This is the 2nd line: B\n"
-                    + "line3: This is the 3rd line: C\n";
+                    + "line3: This is the 3rd line: C\n"
+                    + "line4: This is the 4th line: D\n";
         onView(withId(R.id.textview)).perform(replaceText(text));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
         TextView tv = mActivity.findViewById(R.id.textview);
@@ -454,8 +469,8 @@
                 // Start dragging along the first line
                 motionEventInfo(text.indexOf("line1"), 1.0f),
                 motionEventInfo(text.indexOf("This is the 1st"), 1.0f),
-                // Move to the bottom of the third line; cursor should end up on second line
-                motionEventInfo(text.indexOf("he 3rd"), 0.0f, text.indexOf("he 2nd")),
+                // Move to the middle of the fourth line; cursor should end up on second line
+                motionEventInfo(text.indexOf("he 4th"), 0.5f, text.indexOf("he 2nd")),
                 // Move to the middle of the second line; cursor should end up on the first line
                 motionEventInfo(text.indexOf("he 2nd"), 0.5f, text.indexOf("he 1st"))
         };
@@ -473,37 +488,137 @@
         simulateDrag(tv, events, true);
     }
 
-    private static MotionEvent downEvent(long downTime, long eventTime, float x, float y) {
-        return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0);
+    @Test
+    public void testCursorDrag_snapDistance() throws Throwable {
+        String text = "line1: This is the 1st line: A\n"
+                + "line2: This is the 2nd line: B\n"
+                + "line3: This is the 3rd line: C\n";
+        onView(withId(R.id.textview)).perform(replaceText(text));
+        TextView tv = mActivity.findViewById(R.id.textview);
+        Editor editor = tv.getEditorForTesting();
+        final int startIndex = text.indexOf("he 2nd");
+        Layout layout = tv.getLayout();
+        final float cursorStartX = layout.getPrimaryHorizontal(startIndex) + tv.getTotalPaddingLeft();
+        final float cursorStartY = layout.getLineTop(1) + tv.getTotalPaddingTop();
+        final float dragHandleStartX = 20;
+        final float dragHandleStartY = 20;
+
+        // Drag the handle from the 2nd line to the 3rd line.
+        tapAtPoint(tv, cursorStartX, cursorStartY);
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(startIndex));
+        View handleView = editor.getInsertionController().getHandle();
+        final int rawYOfHandleDrag = dragDownUntilLineChange(
+                handleView, dragHandleStartX, dragHandleStartY, tv.getSelectionStart());
+
+        // Drag the cursor from the 2nd line to the 3rd line.
+        tapAtPoint(tv, cursorStartX, cursorStartY);
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(startIndex));
+        final int rawYOfCursorDrag =
+                dragDownUntilLineChange(tv, cursorStartX, cursorStartY, tv.getSelectionStart());
+
+        // Drag the handle with touch through from the 2nd line to the 3rd line.
+        tv.getEditorForTesting().setFlagInsertionHandleGesturesEnabled(true);
+        tapAtPoint(tv, cursorStartX, cursorStartY);
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(startIndex));
+        handleView = editor.getInsertionController().getHandle();
+        int rawYOfHandleDragWithTouchThrough =
+                dragDownUntilLineChange(handleView, dragHandleStartX, dragHandleStartY, tv.getSelectionStart());
+
+        String msg = String.format(
+                "rawYOfHandleDrag: %d, rawYOfCursorDrag: %d, rawYOfHandleDragWithTouchThrough: %d",
+                rawYOfHandleDrag, rawYOfCursorDrag, rawYOfHandleDragWithTouchThrough);
+        final int max = Math.max(
+                rawYOfCursorDrag, Math.max(rawYOfHandleDrag, rawYOfHandleDragWithTouchThrough));
+        final int min = Math.min(
+                rawYOfCursorDrag, Math.min(rawYOfHandleDrag, rawYOfHandleDragWithTouchThrough));
+        // The drag step is 5 pixels in dragDownUntilLineChange().
+        // The difference among the 3 raw Y values should be no bigger than the drag step.
+        assertWithMessage(msg).that(max - min).isLessThan(6);
     }
 
-    private static MotionEvent upEvent(long downTime, long eventTime, float x, float y) {
-        return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
+    private void dispatchTouchEvent(View view, MotionEvent event) {
+        mInstrumentation.runOnMainSync(() -> view.dispatchTouchEvent(event));
     }
 
-    private static MotionEvent moveEvent(long downTime, long eventTime, float x, float y) {
-        return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, x, y, 0);
+    private void tapAtPoint(TextView tv, final float x, final float y) {
+        long downTime = sTicker.addAndGet(10_000);
+        dispatchTouchEvent(tv, downEvent(tv, downTime, downTime, x, y));
+        dispatchTouchEvent(tv, upEvent(tv, downTime, downTime + 1, x, y));
     }
 
-    private static MotionEvent mouseDownEvent(long downTime, long eventTime, float x, float y) {
-        MotionEvent event = downEvent(downTime, eventTime, x, y);
-        event.setSource(InputDevice.SOURCE_MOUSE);
-        event.setButtonState(MotionEvent.BUTTON_PRIMARY);
+    private int dragDownUntilLineChange(View view, final float startX, final float startY,
+            final int startOffset) {
+        TextView tv = mActivity.findViewById(R.id.textview);
+        final int startLine = tv.getLayout().getLineForOffset(startOffset);
+
+        int rawY = 0;
+        long downTime = sTicker.addAndGet(10_000);
+        long eventTime = downTime;
+        // Move horizontally first to initiate the cursor drag.
+        dispatchTouchEvent(view, downEvent(view, downTime, eventTime++, startX, startY));
+        dispatchTouchEvent(view, moveEvent(view, downTime, eventTime++, startX + 50, startY));
+        dispatchTouchEvent(view, moveEvent(view, downTime, eventTime++, startX, startY));
+        // Move downwards 5 pixels at a time until a line change occurs.
+        for (int i = 0; i < 200; i++) {
+            MotionEvent ev = moveEvent(view, downTime, eventTime++, startX, startY + i * 5);
+            rawY = (int) ev.getRawY();
+            dispatchTouchEvent(view, ev);
+            if (tv.getLayout().getLineForOffset(tv.getSelectionStart()) > startLine) {
+                break;
+            }
+        }
+        String msg = String.format("The cursor didn't jump from %d!", startOffset);
+        assertWithMessage(msg).that(
+                tv.getLayout().getLineForOffset(tv.getSelectionStart())).isGreaterThan(startLine);
+        dispatchTouchEvent(view, upEvent(view, downTime, eventTime, startX, startY));
+        return rawY;
+    }
+
+    private MotionEvent obtainTouchEvent(
+            View view, int action, long downTime, long eventTime, float x, float y) {
+        Rect r = new Rect();
+        view.getBoundsOnScreen(r);
+        float rawX = x + r.left;
+        float rawY = y + r.top;
+        MotionEvent event =
+                MotionEvent.obtain(downTime, eventTime, action, rawX, rawY, 0);
+        view.toLocalMotionEvent(event);
+        mMotionEvents.add(event);
         return event;
     }
 
-    private static MotionEvent mouseUpEvent(long downTime, long eventTime, float x, float y) {
-        MotionEvent event = upEvent(downTime, eventTime, x, y);
+    private MotionEvent obtainMouseEvent(
+            View view, int action, long downTime, long eventTime, float x, float y) {
+        MotionEvent event = obtainTouchEvent(view, action, downTime, eventTime, x, y);
         event.setSource(InputDevice.SOURCE_MOUSE);
-        event.setButtonState(0);
+        if (action != MotionEvent.ACTION_UP) {
+            event.setButtonState(MotionEvent.BUTTON_PRIMARY);
+        }
         return event;
     }
 
-    private static MotionEvent mouseMoveEvent(long downTime, long eventTime, float x, float y) {
-        MotionEvent event = moveEvent(downTime, eventTime, x, y);
-        event.setSource(InputDevice.SOURCE_MOUSE);
-        event.setButtonState(MotionEvent.BUTTON_PRIMARY);
-        return event;
+    private MotionEvent downEvent(View view, long downTime, long eventTime, float x, float y) {
+        return obtainTouchEvent(view, MotionEvent.ACTION_DOWN, downTime, eventTime, x, y);
+    }
+
+    private MotionEvent moveEvent(View view, long downTime, long eventTime, float x, float y) {
+        return obtainTouchEvent(view, MotionEvent.ACTION_MOVE, downTime, eventTime, x, y);
+    }
+
+    private MotionEvent upEvent(View view, long downTime, long eventTime, float x, float y) {
+        return obtainTouchEvent(view, MotionEvent.ACTION_UP, downTime, eventTime, x, y);
+    }
+
+    private MotionEvent mouseDownEvent(View view, long downTime, long eventTime, float x, float y) {
+        return obtainMouseEvent(view, MotionEvent.ACTION_DOWN, downTime, eventTime, x, y);
+    }
+
+    private MotionEvent mouseMoveEvent(View view, long downTime, long eventTime, float x, float y) {
+        return obtainMouseEvent(view, MotionEvent.ACTION_MOVE, downTime, eventTime, x, y);
+    }
+
+    private MotionEvent mouseUpEvent(View view, long downTime, long eventTime, float x, float y) {
+        return obtainMouseEvent(view, MotionEvent.ACTION_UP, downTime, eventTime, x, y);
     }
 
     public static MotionEventInfo motionEventInfo(int index, float ratioToLineTop) {
@@ -543,14 +658,15 @@
 
         float[] downCoords = events[0].getCoordinates(tv);
         long downEventTime = sTicker.addAndGet(10_000);
-        MotionEvent downEvent = downEvent(downEventTime, downEventTime,
+        MotionEvent downEvent = downEvent(tv,  downEventTime, downEventTime,
                 downCoords[0], downCoords[1]);
         mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(downEvent));
 
         for (int i = 1; i < events.length; i++) {
             float[] moveCoords = events[i].getCoordinates(tv);
             long eventTime = downEventTime + i;
-            MotionEvent event = moveEvent(downEventTime, eventTime, moveCoords[0], moveCoords[1]);
+            MotionEvent event = moveEvent(tv, downEventTime, eventTime, moveCoords[0],
+                    moveCoords[1]);
             mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event));
             assertCursorPosition(tv, events[i].expectedCursorIndex, runAssertions);
         }
@@ -558,7 +674,7 @@
         MotionEventInfo lastEvent = events[events.length - 1];
         float[] upCoords = lastEvent.getCoordinates(tv);
         long upEventTime = downEventTime + events.length;
-        MotionEvent upEvent = upEvent(downEventTime, upEventTime, upCoords[0], upCoords[1]);
+        MotionEvent upEvent = upEvent(tv, downEventTime, upEventTime, upCoords[0], upCoords[1]);
         mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(upEvent));
     }
 
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 88a6f9e..a72be25 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -497,7 +497,7 @@
 
     @Test
     public void testInsertionHandle_multiLine() {
-        final String text = "abcd\n" + "efg\n" + "hijk\n";
+        final String text = "abcd\n" + "efg\n" + "hijk\n" + "lmn\n";
         onView(withId(R.id.textview)).perform(replaceText(text));
 
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
@@ -506,12 +506,12 @@
         final TextView textView = mActivity.findViewById(R.id.textview);
 
         onHandleView(com.android.internal.R.id.insertion_handle)
-                .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('a')));
-        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("a")));
-
-        onHandleView(com.android.internal.R.id.insertion_handle)
                 .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('f')));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("f")));
+
+        onHandleView(com.android.internal.R.id.insertion_handle)
+                .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('i')));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("i")));
     }
 
     private void enableFlagsForInsertionHandleGestures() {
diff --git a/data/etc/com.android.documentsui.xml b/data/etc/com.android.documentsui.xml
index b6671db..1e570ba 100644
--- a/data/etc/com.android.documentsui.xml
+++ b/data/etc/com.android.documentsui.xml
@@ -20,6 +20,7 @@
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
         <!-- Permissions required for reading and logging compat changes -->
         <permission name="android.permission.LOG_COMPAT_CHANGE"/>
+        <permission name="android.permission.MODIFY_QUIET_MODE"/>
         <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
     </privapp-permissions>
 </permissions>
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index 38e18a9..72827a9 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -63,5 +63,6 @@
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
         <permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"/>
         <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
+        <permission name="android.permission.CAMERA_OPEN_CLOSE_LISTENER" />
     </privapp-permissions>
 </permissions>
diff --git a/identity/java/android/security/identity/CredstoreResultData.java b/identity/java/android/security/identity/CredstoreResultData.java
index ef7afca..2ef735e 100644
--- a/identity/java/android/security/identity/CredstoreResultData.java
+++ b/identity/java/android/security/identity/CredstoreResultData.java
@@ -66,7 +66,7 @@
     }
 
     @Override
-    public @NonNull Collection<String> getNamespaceNames() {
+    public @NonNull Collection<String> getNamespaces() {
         return Collections.unmodifiableCollection(mData.keySet());
     }
 
diff --git a/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java b/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java
index 335636c..725e3d8 100644
--- a/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java
+++ b/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java
@@ -105,11 +105,11 @@
             n++;
         }
 
-        Collection<String> namespaceNames = personalizationData.getNamespaceNames();
+        Collection<String> namespaces = personalizationData.getNamespaces();
 
-        EntryNamespaceParcel[] ensParcels  = new EntryNamespaceParcel[namespaceNames.size()];
+        EntryNamespaceParcel[] ensParcels  = new EntryNamespaceParcel[namespaces.size()];
         n = 0;
-        for (String namespaceName : namespaceNames) {
+        for (String namespaceName : namespaces) {
             PersonalizationData.NamespaceData nsd =
                     personalizationData.getNamespaceData(namespaceName);
 
diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java
index bd43919..1db2f63 100644
--- a/identity/java/android/security/identity/IdentityCredential.java
+++ b/identity/java/android/security/identity/IdentityCredential.java
@@ -209,6 +209,11 @@
      * <p>Note that only items referenced in {@code entriesToRequest} are returned - the
      * {@code requestMessage} parameter is only used to for enforcing reader authentication.
      *
+     * <p>The reason for having {@code requestMessage} and {@code entriesToRequest} as separate
+     * parameters is that the former represents a request from the remote verifier device
+     * (optionally signed) and this allows the application to filter the request to not include
+     * data elements which the user has not consented to sharing.
+     *
      * @param requestMessage         If not {@code null}, must contain CBOR data conforming to
      *                               the schema mentioned above.
      * @param entriesToRequest       The entries to request, organized as a map of namespace
diff --git a/identity/java/android/security/identity/PersonalizationData.java b/identity/java/android/security/identity/PersonalizationData.java
index 44370a1..b34f250 100644
--- a/identity/java/android/security/identity/PersonalizationData.java
+++ b/identity/java/android/security/identity/PersonalizationData.java
@@ -46,7 +46,7 @@
         return Collections.unmodifiableCollection(mProfiles);
     }
 
-    Collection<String> getNamespaceNames() {
+    Collection<String> getNamespaces() {
         return Collections.unmodifiableCollection(mNamespaces.keySet());
     }
 
@@ -120,7 +120,7 @@
          * @param value                   The value to add, in CBOR encoding.
          * @return The builder.
          */
-        public @NonNull Builder setEntry(@NonNull String namespace, @NonNull String name,
+        public @NonNull Builder putEntry(@NonNull String namespace, @NonNull String name,
                 @NonNull Collection<AccessControlProfileId> accessControlProfileIds,
                 @NonNull byte[] value) {
             NamespaceData namespaceData = mData.mNamespaces.get(namespace);
diff --git a/identity/java/android/security/identity/ResultData.java b/identity/java/android/security/identity/ResultData.java
index 0982c8a..13552d6 100644
--- a/identity/java/android/security/identity/ResultData.java
+++ b/identity/java/android/security/identity/ResultData.java
@@ -152,7 +152,7 @@
      * @return collection of name of namespaces containing retrieved entries. May be empty if no
      *     data was retrieved.
      */
-    public abstract @NonNull Collection<String> getNamespaceNames();
+    public abstract @NonNull Collection<String> getNamespaces();
 
     /**
      * Get the names of all entries.
@@ -196,8 +196,7 @@
      * @param name the name of the entry to get the value for.
      * @return the status indicating whether the value was retrieved and if not, why.
      */
-    @Status
-    public abstract int getStatus(@NonNull String namespaceName, @NonNull String name);
+    public abstract @Status int getStatus(@NonNull String namespaceName, @NonNull String name);
 
     /**
      * Gets the raw CBOR data for the value of an entry.
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index 84c07d7..39900e6 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -146,12 +146,11 @@
     }
 
     Layer layer(mRenderThread.renderState(), nullptr, 255, SkBlendMode::kSrc);
-    bool disableFilter = MathUtils::areEqual(skiaSrcRect.width(), skiaDestRect.width()) &&
-                         MathUtils::areEqual(skiaSrcRect.height(), skiaDestRect.height());
-    layer.setForceFilter(!disableFilter);
     layer.setSize(displayedWidth, displayedHeight);
     texTransform.copyTo(layer.getTexTransform());
     layer.setImage(image);
+    // Scaling filter is not explicitly set here, because it is done inside copyLayerInfo
+    // after checking the necessity based on the src/dest rect size and the transformation.
     if (copyLayerInto(&layer, &skiaSrcRect, &skiaDestRect, bitmap)) {
         copyResult = CopyResult::Success;
     }
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 45b849f..37e2ab5 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -377,7 +377,9 @@
      * @see TransferCallback#onTransferred
      * @see TransferCallback#onTransferFailed
      */
-    public void transferTo(@Nullable MediaRoute2Info route) {
+    public void transferTo(@NonNull MediaRoute2Info route) {
+        Objects.requireNonNull(route, "route must not be null");
+
         List<RoutingController> controllers = getControllers();
         RoutingController controller = controllers.get(controllers.size() - 1);
 
@@ -385,19 +387,25 @@
     }
 
     /**
+     * Stops the current media routing. If the {@link #getSystemController() system controller}
+     * controls the media routing, this method is a no-op.
+     */
+    public void stop() {
+        List<RoutingController> controllers = getControllers();
+        RoutingController controller = controllers.get(controllers.size() - 1);
+
+        controller.release();
+    }
+
+    /**
      * Transfers the media of a routing controller to the given route.
      * @param controller a routing controller controlling media routing.
-     * @param route the route you want to transfer the media to. Pass {@code null} to stop
-     *              routing controlled by the given controller.
+     * @param route the route you want to transfer the media to.
      * @hide
      */
-    void transfer(@NonNull RoutingController controller, @Nullable MediaRoute2Info route) {
+    void transfer(@NonNull RoutingController controller, @NonNull MediaRoute2Info route) {
         Objects.requireNonNull(controller, "controller must not be null");
-
-        if (route == null) {
-            controller.release();
-            return;
-        }
+        Objects.requireNonNull(route, "route must not be null");
 
         // TODO: Check thread-safety
         if (!mRoutes.containsKey(route.getId())) {
@@ -681,7 +689,7 @@
 
         if (removed) {
             matchingController.release();
-            notifyControllerReleased(matchingController);
+            notifyStopped(matchingController);
         }
     }
 
@@ -738,16 +746,16 @@
         }
     }
 
-    private void notifyControllerUpdated(RoutingController controller) {
-        for (ControllerCallbackRecord record: mControllerCallbackRecords) {
-            record.mExecutor.execute(() -> record.mCallback.onControllerUpdated(controller));
+    private void notifyStopped(RoutingController controller) {
+        for (TransferCallbackRecord record: mTransferCallbackRecords) {
+            record.mExecutor.execute(
+                    () -> record.mTransferCallback.onStopped(controller));
         }
     }
 
-    private void notifyControllerReleased(RoutingController controller) {
-        for (TransferCallbackRecord record: mTransferCallbackRecords) {
-            record.mExecutor.execute(
-                    () -> record.mTransferCallback.onTransferred(controller, null));
+    private void notifyControllerUpdated(RoutingController controller) {
+        for (ControllerCallbackRecord record: mControllerCallbackRecords) {
+            record.mExecutor.execute(() -> record.mCallback.onControllerUpdated(controller));
         }
     }
 
@@ -788,20 +796,26 @@
          * This can happen by calling {@link #transferTo(MediaRoute2Info)} or
          * {@link RoutingController#release()}.
          *
-         * @param oldController the previous controller that controlled routing.
-         * @param newController the new controller to control routing or {@code null} if the
-         *                      previous controller is released.
+         * @param oldController the previous controller that controlled routing
+         * @param newController the new controller to control routing
          * @see #transferTo(MediaRoute2Info)
          */
         public void onTransferred(@NonNull RoutingController oldController,
-                @Nullable RoutingController newController) {}
+                @NonNull RoutingController newController) {}
 
         /**
          * Called when {@link #transferTo(MediaRoute2Info)} failed.
          *
-         * @param requestedRoute the route info which was used for the transfer.
+         * @param requestedRoute the route info which was used for the transfer
          */
         public void onTransferFailed(@NonNull MediaRoute2Info requestedRoute) {}
+
+        /**
+         * Called when a media routing stops. It can be stopped by a user or a provider.
+         *
+         * @param controller the controller that controlled the stopped media routing.
+         */
+        public void onStopped(@NonNull RoutingController controller) { }
     }
 
     /**
@@ -1186,7 +1200,7 @@
             }
 
             if (removed) {
-                mHandler.post(() -> notifyControllerReleased(RoutingController.this));
+                mHandler.post(() -> notifyStopped(RoutingController.this));
             }
 
             if (stub != null) {
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/filter/AlpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java
index 7b29494..8a29442 100644
--- a/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.content.Context;
@@ -33,7 +34,7 @@
  * @hide
  */
 @SystemApi
-public class AlpFilterConfiguration extends FilterConfiguration {
+public final class AlpFilterConfiguration extends FilterConfiguration {
     /**
      * IPv4 packet type.
      */
@@ -123,9 +124,10 @@
     /**
      * Builder for {@link AlpFilterConfiguration}.
      */
-    public static class Builder extends FilterConfiguration.Builder<Builder> {
+    public static final class Builder {
         private int mPacketType;
         private int mLengthType;
+        private Settings mSettings;
 
         private Builder() {
         }
@@ -150,16 +152,20 @@
         }
 
         /**
+         * Sets filter settings.
+         */
+        @NonNull
+        public Builder setSettings(@Nullable Settings settings) {
+            mSettings = settings;
+            return this;
+        }
+
+        /**
          * Builds a {@link AlpFilterConfiguration} object.
          */
         @NonNull
         public AlpFilterConfiguration build() {
             return new AlpFilterConfiguration(mSettings, mPacketType, mLengthType);
         }
-
-        @Override
-        Builder self() {
-            return this;
-        }
     }
 }
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index 52bdb59..4777fe8 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -225,6 +225,7 @@
         mCallback = cb;
         mExecutor = executor;
     }
+
     /** @hide */
     public FilterCallback getCallback() {
         return mCallback;
diff --git a/media/java/android/media/tv/tuner/filter/FilterConfiguration.java b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java
index a8c9356..dd7e5fc 100644
--- a/media/java/android/media/tv/tuner/filter/FilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java
@@ -16,7 +16,6 @@
 
 package android.media.tv.tuner.filter;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 
@@ -48,26 +47,4 @@
     public Settings getSettings() {
         return mSettings;
     }
-
-    /**
-     * Builder for {@link FilterConfiguration}.
-     *
-     * @param <T> The subclass to be built.
-     */
-    public abstract static class Builder<T extends Builder<T>> {
-        /* package */ Settings mSettings;
-
-        /* package */ Builder() {
-        }
-
-        /**
-         * Sets filter settings.
-         */
-        @NonNull
-        public T setSettings(@Nullable Settings settings) {
-            mSettings = settings;
-            return self();
-        }
-        /* package */ abstract T self();
-    }
 }
diff --git a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
index a8dbfa5..04f3410 100644
--- a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
@@ -17,6 +17,7 @@
 package android.media.tv.tuner.filter;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.Size;
 import android.annotation.SystemApi;
@@ -29,7 +30,7 @@
  * @hide
  */
 @SystemApi
-public class IpFilterConfiguration extends FilterConfiguration {
+public final class IpFilterConfiguration extends FilterConfiguration {
     private final byte[] mSrcIpAddress;
     private final byte[] mDstIpAddress;
     private final int mSrcPort;
@@ -104,12 +105,13 @@
     /**
      * Builder for {@link IpFilterConfiguration}.
      */
-    public static class Builder extends FilterConfiguration.Builder<Builder> {
+    public static final class Builder {
         private byte[] mSrcIpAddress;
         private byte[] mDstIpAddress;
         private int mSrcPort;
         private int mDstPort;
         private boolean mPassthrough;
+        private Settings mSettings;
 
         private Builder() {
         }
@@ -156,6 +158,15 @@
         }
 
         /**
+         * Sets filter settings.
+         */
+        @NonNull
+        public Builder setSettings(@Nullable Settings settings) {
+            mSettings = settings;
+            return this;
+        }
+
+        /**
          * Builds a {@link IpFilterConfiguration} object.
          */
         @NonNull
@@ -169,10 +180,5 @@
             return new IpFilterConfiguration(
                     mSettings, mSrcIpAddress, mDstIpAddress, mSrcPort, mDstPort, mPassthrough);
         }
-
-        @Override
-        Builder self() {
-            return this;
-        }
     }
 }
diff --git a/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java
index 0601829..c0453b4 100644
--- a/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java
@@ -17,6 +17,7 @@
 package android.media.tv.tuner.filter;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.content.Context;
@@ -28,7 +29,7 @@
  * @hide
  */
 @SystemApi
-public class MmtpFilterConfiguration extends FilterConfiguration {
+public final class MmtpFilterConfiguration extends FilterConfiguration {
     private final int mMmtpPid;
 
     private MmtpFilterConfiguration(Settings settings, int mmtpPid) {
@@ -65,8 +66,9 @@
     /**
      * Builder for {@link IpFilterConfiguration}.
      */
-    public static class Builder extends FilterConfiguration.Builder<Builder> {
+    public static final class Builder {
         private int mMmtpPid;
+        private Settings mSettings;
 
         private Builder() {
         }
@@ -81,16 +83,20 @@
         }
 
         /**
+         * Sets filter settings.
+         */
+        @NonNull
+        public Builder setSettings(@Nullable Settings settings) {
+            mSettings = settings;
+            return this;
+        }
+
+        /**
          * Builds a {@link IpFilterConfiguration} object.
          */
         @NonNull
         public MmtpFilterConfiguration build() {
             return new MmtpFilterConfiguration(mSettings, mMmtpPid);
         }
-
-        @Override
-        Builder self() {
-            return this;
-        }
     }
 }
diff --git a/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java
index ac4fc83..c5191bf 100644
--- a/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java
@@ -17,6 +17,7 @@
 package android.media.tv.tuner.filter;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.content.Context;
@@ -28,7 +29,7 @@
  * @hide
  */
 @SystemApi
-public class TlvFilterConfiguration extends FilterConfiguration {
+public final class TlvFilterConfiguration extends FilterConfiguration {
     /**
      * IPv4 packet type.
      */
@@ -108,10 +109,11 @@
     /**
      * Builder for {@link TlvFilterConfiguration}.
      */
-    public static class Builder extends FilterConfiguration.Builder<Builder> {
+    public static final class Builder {
         private int mPacketType;
         private boolean mIsCompressedIpPacket;
         private boolean mPassthrough;
+        private Settings mSettings;
 
         private Builder() {
         }
@@ -144,6 +146,15 @@
         }
 
         /**
+         * Sets filter settings.
+         */
+        @NonNull
+        public Builder setSettings(@Nullable Settings settings) {
+            mSettings = settings;
+            return this;
+        }
+
+        /**
          * Builds a {@link TlvFilterConfiguration} object.
          */
         @NonNull
@@ -151,10 +162,5 @@
             return new TlvFilterConfiguration(
                     mSettings, mPacketType, mIsCompressedIpPacket, mPassthrough);
         }
-
-        @Override
-        Builder self() {
-            return this;
-        }
     }
 }
diff --git a/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java
index 6a8b6da..a7140eb 100644
--- a/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java
@@ -17,6 +17,7 @@
 package android.media.tv.tuner.filter;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.content.Context;
@@ -28,7 +29,7 @@
  * @hide
  */
 @SystemApi
-public class TsFilterConfiguration extends FilterConfiguration {
+public final class TsFilterConfiguration extends FilterConfiguration {
     private final int mTpid;
 
     private TsFilterConfiguration(Settings settings, int tpid) {
@@ -63,8 +64,9 @@
     /**
      * Builder for {@link TsFilterConfiguration}.
      */
-    public static class Builder extends FilterConfiguration.Builder<Builder> {
+    public static final class Builder {
         private int mTpid;
+        private Settings mSettings;
 
         private Builder() {
         }
@@ -81,16 +83,20 @@
         }
 
         /**
+         * Sets filter settings.
+         */
+        @NonNull
+        public Builder setSettings(@Nullable Settings settings) {
+            mSettings = settings;
+            return this;
+        }
+
+        /**
          * Builds a {@link TsFilterConfiguration} object.
          */
         @NonNull
         public TsFilterConfiguration build() {
             return new TsFilterConfiguration(mSettings, mTpid);
         }
-
-        @Override
-        Builder self() {
-            return this;
-        }
     }
 }
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 737c95d..893e516 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -242,13 +242,6 @@
     });
 }
 
-void JMediaCodec::releaseAsync() {
-    if (mCodec != NULL) {
-        mCodec->releaseAsync();
-    }
-    mInitStatus = NO_INIT;
-}
-
 JMediaCodec::~JMediaCodec() {
     if (mLooper != NULL) {
         /* MediaCodec and looper should have been released explicitly already
@@ -1131,10 +1124,7 @@
 }
 
 static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) {
-    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
-    if (codec != NULL) {
-        codec->releaseAsync();
-    }
+    setMediaCodec(env, thiz, NULL);
 }
 
 static void throwCodecException(JNIEnv *env, status_t err, int32_t actionCode, const char *msg) {
@@ -2807,7 +2797,7 @@
 
 static void android_media_MediaCodec_native_finalize(
         JNIEnv *env, jobject thiz) {
-    setMediaCodec(env, thiz, NULL);
+    android_media_MediaCodec_release(env, thiz);
 }
 
 // MediaCodec.LinearBlock
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 400ce1b..8899fee 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -61,7 +61,6 @@
 
     void registerSelf();
     void release();
-    void releaseAsync();
 
     status_t enableOnFrameRenderedListener(jboolean enable);
 
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 273c776..679f18a 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -34,19 +34,28 @@
 using ::android::hardware::Void;
 using ::android::hardware::hidl_bitfield;
 using ::android::hardware::hidl_vec;
+using ::android::hardware::tv::tuner::V1_0::AudioExtraMetaData;
+using ::android::hardware::tv::tuner::V1_0::Constant;
 using ::android::hardware::tv::tuner::V1_0::DataFormat;
 using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterSettings;
 using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterType;
 using ::android::hardware::tv::tuner::V1_0::DemuxAlpLengthType;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterAvSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterDownloadEvent;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterDownloadSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterIpPayloadEvent;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterMediaEvent;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterMmtpRecordEvent;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterPesDataSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterPesEvent;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterRecordSettings;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterSectionBits;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterSectionEvent;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterSectionSettings;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterTemiEvent;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterTsRecordEvent;
 using ::android::hardware::tv::tuner::V1_0::DemuxIpAddress;
 using ::android::hardware::tv::tuner::V1_0::DemuxIpFilterSettings;
 using ::android::hardware::tv::tuner::V1_0::DemuxIpFilterType;
@@ -136,6 +145,7 @@
     jmethodID dvrInitID;
     jmethodID onFrontendEventID;
     jmethodID onFilterStatusID;
+    jmethodID onFilterEventID;
     jmethodID lnbInitID;
     jmethodID onLnbEventID;
     jmethodID descramblerInitID;
@@ -248,48 +258,287 @@
     return linearBlock;
 }
 
-jobject FilterCallback::getMediaEvent(const DemuxFilterEvent::Event& event) {
+jobjectArray FilterCallback::getSectionEvent(
+        jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) {
     JNIEnv *env = AndroidRuntime::getJNIEnv();
-    jclass clazz = env->FindClass("android/media/tv/tuner/filter/MediaEvent");
-    jmethodID eventInit = env->GetMethodID(clazz,
+    jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/SectionEvent");
+    jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIII)V");
+
+    for (int i = 0; i < events.size(); i++) {
+        auto event = events[i];
+        DemuxFilterSectionEvent sectionEvent = event.section();
+
+        jint tableId = static_cast<jint>(sectionEvent.tableId);
+        jint version = static_cast<jint>(sectionEvent.version);
+        jint sectionNum = static_cast<jint>(sectionEvent.sectionNum);
+        jint dataLength = static_cast<jint>(sectionEvent.dataLength);
+
+        jobject obj =
+                env->NewObject(eventClazz, eventInit, tableId, version, sectionNum, dataLength);
+        env->SetObjectArrayElement(arr, i, obj);
+    }
+    return arr;
+}
+
+jobjectArray FilterCallback::getMediaEvent(
+        jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/MediaEvent");
+    jmethodID eventInit = env->GetMethodID(eventClazz,
             "<init>",
             "(IZJJJLandroid/media/MediaCodec$LinearBlock;"
             "ZJIZLandroid/media/tv/tuner/filter/AudioDescriptor;)V");
 
-    DemuxFilterMediaEvent mediaEvent = event.media();
-    uint32_t dataLength = mediaEvent.dataLength;
-    const native_handle_t* h = mediaEvent.avMemory.getNativeHandle();
-    jobject block = handleToLinearBlock(h, dataLength);
-    // TODO: handle other fields
+    for (int i = 0; i < events.size(); i++) {
+        auto event = events[i];
+        DemuxFilterMediaEvent mediaEvent = event.media();
 
-    return env->NewObject(clazz, eventInit, (jint) 0, (jboolean) 0, (jlong) 0, (jlong) 0, (jlong) 0,
-            block, (jboolean) 0, (jlong) 0, (jint) 0, (jboolean) 0, NULL);
+        jobject audioDescriptor = NULL;
+        if (mediaEvent.extraMetaData.getDiscriminator()
+                == DemuxFilterMediaEvent::ExtraMetaData::hidl_discriminator::audio) {
+            jclass adClazz = env->FindClass("android/media/tv/tuner/filter/AudioDescriptor");
+            jmethodID adInit = env->GetMethodID(adClazz, "<init>", "(BBCBBB)V");
+
+            AudioExtraMetaData ad = mediaEvent.extraMetaData.audio();
+            jbyte adFade = static_cast<jbyte>(ad.adFade);
+            jbyte adPan = static_cast<jbyte>(ad.adPan);
+            jchar versionTextTag = static_cast<jchar>(ad.versionTextTag);
+            jbyte adGainCenter = static_cast<jbyte>(ad.adGainCenter);
+            jbyte adGainFront = static_cast<jbyte>(ad.adGainFront);
+            jbyte adGainSurround = static_cast<jbyte>(ad.adGainSurround);
+
+            audioDescriptor =
+                    env->NewObject(adClazz, adInit, adFade, adPan, versionTextTag, adGainCenter,
+                            adGainFront, adGainSurround);
+        }
+
+        jlong dataLength = static_cast<jlong>(mediaEvent.dataLength);
+        const native_handle_t* h = NULL;
+        jobject block = NULL;
+        if (mediaEvent.avMemory != NULL) {
+            h = mediaEvent.avMemory.getNativeHandle();
+            block = handleToLinearBlock(h, dataLength);
+        }
+
+        jint streamId = static_cast<jint>(mediaEvent.streamId);
+        jboolean isPtsPresent = static_cast<jboolean>(mediaEvent.isPtsPresent);
+        jlong pts = static_cast<jlong>(mediaEvent.pts);
+        jlong offset = static_cast<jlong>(mediaEvent.offset);
+        jboolean isSecureMemory = static_cast<jboolean>(mediaEvent.isSecureMemory);
+        jlong avDataId = static_cast<jlong>(mediaEvent.avDataId);
+        jint mpuSequenceNumber = static_cast<jint>(mediaEvent.mpuSequenceNumber);
+        jboolean isPesPrivateData = static_cast<jboolean>(mediaEvent.isPesPrivateData);
+
+        jobject obj =
+                env->NewObject(eventClazz, eventInit, streamId, isPtsPresent, pts, dataLength,
+                offset, block, isSecureMemory, avDataId, mpuSequenceNumber, isPesPrivateData,
+                audioDescriptor);
+        env->SetObjectArrayElement(arr, i, obj);
+    }
+    return arr;
+}
+
+jobjectArray FilterCallback::getPesEvent(
+        jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/PesEvent");
+    jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(III)V");
+
+    for (int i = 0; i < events.size(); i++) {
+        auto event = events[i];
+        DemuxFilterPesEvent pesEvent = event.pes();
+
+        jint streamId = static_cast<jint>(pesEvent.streamId);
+        jint dataLength = static_cast<jint>(pesEvent.dataLength);
+        jint mpuSequenceNumber = static_cast<jint>(pesEvent.mpuSequenceNumber);
+
+        jobject obj =
+                env->NewObject(eventClazz, eventInit, streamId, dataLength, mpuSequenceNumber);
+        env->SetObjectArrayElement(arr, i, obj);
+    }
+    return arr;
+}
+
+jobjectArray FilterCallback::getTsRecordEvent(
+        jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/TsRecordEvent");
+    jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIJ)V");
+
+    for (int i = 0; i < events.size(); i++) {
+        auto event = events[i];
+        DemuxFilterTsRecordEvent tsRecordEvent = event.tsRecord();
+        DemuxPid pid = tsRecordEvent.pid;
+
+        jint jpid = static_cast<jint>(Constant::INVALID_TS_PID);
+
+        if (pid.getDiscriminator() == DemuxPid::hidl_discriminator::tPid) {
+            jpid = static_cast<jint>(pid.tPid());
+        } else if (pid.getDiscriminator() == DemuxPid::hidl_discriminator::mmtpPid) {
+            jpid = static_cast<jint>(pid.mmtpPid());
+        }
+
+        jint sc = 0;
+
+        if (tsRecordEvent.scIndexMask.getDiscriminator()
+                == DemuxFilterTsRecordEvent::ScIndexMask::hidl_discriminator::sc) {
+            sc = static_cast<jint>(tsRecordEvent.scIndexMask.sc());
+        } else if (tsRecordEvent.scIndexMask.getDiscriminator()
+                == DemuxFilterTsRecordEvent::ScIndexMask::hidl_discriminator::scHevc) {
+            sc = static_cast<jint>(tsRecordEvent.scIndexMask.scHevc());
+        }
+
+        jint ts = static_cast<jint>(tsRecordEvent.tsIndexMask);
+
+        jlong byteNumber = static_cast<jlong>(tsRecordEvent.byteNumber);
+
+        jobject obj =
+                env->NewObject(eventClazz, eventInit, jpid, ts, sc, byteNumber);
+        env->SetObjectArrayElement(arr, i, obj);
+    }
+    return arr;
+}
+
+jobjectArray FilterCallback::getMmtpRecordEvent(
+        jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/MmtpRecordEvent");
+    jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IJ)V");
+
+    for (int i = 0; i < events.size(); i++) {
+        auto event = events[i];
+        DemuxFilterMmtpRecordEvent mmtpRecordEvent = event.mmtpRecord();
+
+        jint scHevcIndexMask = static_cast<jint>(mmtpRecordEvent.scHevcIndexMask);
+        jlong byteNumber = static_cast<jlong>(mmtpRecordEvent.byteNumber);
+
+        jobject obj =
+                env->NewObject(eventClazz, eventInit, scHevcIndexMask, byteNumber);
+        env->SetObjectArrayElement(arr, i, obj);
+    }
+    return arr;
+}
+
+jobjectArray FilterCallback::getDownloadEvent(
+        jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/DownloadEvent");
+    jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIII)V");
+
+    for (int i = 0; i < events.size(); i++) {
+        auto event = events[i];
+        DemuxFilterDownloadEvent downloadEvent = event.download();
+
+        jint itemId = static_cast<jint>(downloadEvent.itemId);
+        jint mpuSequenceNumber = static_cast<jint>(downloadEvent.mpuSequenceNumber);
+        jint itemFragmentIndex = static_cast<jint>(downloadEvent.itemFragmentIndex);
+        jint lastItemFragmentIndex = static_cast<jint>(downloadEvent.lastItemFragmentIndex);
+        jint dataLength = static_cast<jint>(downloadEvent.dataLength);
+
+        jobject obj =
+                env->NewObject(eventClazz, eventInit, itemId, mpuSequenceNumber, itemFragmentIndex,
+                        lastItemFragmentIndex, dataLength);
+        env->SetObjectArrayElement(arr, i, obj);
+    }
+    return arr;
+}
+
+jobjectArray FilterCallback::getIpPayloadEvent(
+        jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/IpPayloadEvent");
+    jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(I)V");
+
+    for (int i = 0; i < events.size(); i++) {
+        auto event = events[i];
+        DemuxFilterIpPayloadEvent ipPayloadEvent = event.ipPayload();
+        jint dataLength = static_cast<jint>(ipPayloadEvent.dataLength);
+        jobject obj = env->NewObject(eventClazz, eventInit, dataLength);
+        env->SetObjectArrayElement(arr, i, obj);
+    }
+    return arr;
+}
+
+jobjectArray FilterCallback::getTemiEvent(
+        jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/TemiEvent");
+    jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(JB[B)V");
+
+    for (int i = 0; i < events.size(); i++) {
+        auto event = events[i];
+        DemuxFilterTemiEvent temiEvent = event.temi();
+        jlong pts = static_cast<jlong>(temiEvent.pts);
+        jbyte descrTag = static_cast<jbyte>(temiEvent.descrTag);
+        std::vector<uint8_t> descrData = temiEvent.descrData;
+
+        jbyteArray array = env->NewByteArray(descrData.size());
+        env->SetByteArrayRegion(
+                array, 0, descrData.size(), reinterpret_cast<jbyte*>(&descrData[0]));
+
+        jobject obj = env->NewObject(eventClazz, eventInit, pts, descrTag, array);
+        env->SetObjectArrayElement(arr, i, obj);
+    }
+    return arr;
 }
 
 Return<void> FilterCallback::onFilterEvent(const DemuxFilterEvent& filterEvent) {
     ALOGD("FilterCallback::onFilterEvent");
 
     JNIEnv *env = AndroidRuntime::getJNIEnv();
-    jclass clazz = env->FindClass("android/media/tv/tuner/filter/Filter");
 
     std::vector<DemuxFilterEvent::Event> events = filterEvent.events;
     jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/FilterEvent");
     jobjectArray array = env->NewObjectArray(events.size(), eventClazz, NULL);
 
-    for (int i = 0; i < events.size(); i++) {
-        auto event = events[i];
-        if (event.getDiscriminator() == DemuxFilterEvent::Event::hidl_discriminator::media) {
-            env->SetObjectArrayElement(array, i, getMediaEvent(event));
+    if (!events.empty()) {
+        auto event = events[0];
+        switch (event.getDiscriminator()) {
+            case DemuxFilterEvent::Event::hidl_discriminator::media: {
+                array = getMediaEvent(array, events);
+                break;
+            }
+            case DemuxFilterEvent::Event::hidl_discriminator::section: {
+                array = getSectionEvent(array, events);
+                break;
+            }
+            case DemuxFilterEvent::Event::hidl_discriminator::pes: {
+                array = getPesEvent(array, events);
+                break;
+            }
+            case DemuxFilterEvent::Event::hidl_discriminator::tsRecord: {
+                array = getTsRecordEvent(array, events);
+                break;
+            }
+            case DemuxFilterEvent::Event::hidl_discriminator::mmtpRecord: {
+                array = getMmtpRecordEvent(array, events);
+                break;
+            }
+            case DemuxFilterEvent::Event::hidl_discriminator::download: {
+                array = getDownloadEvent(array, events);
+                break;
+            }
+            case DemuxFilterEvent::Event::hidl_discriminator::ipPayload: {
+                array = getIpPayloadEvent(array, events);
+                break;
+            }
+            case DemuxFilterEvent::Event::hidl_discriminator::temi: {
+                array = getTemiEvent(array, events);
+                break;
+            }
+            default: {
+                break;
+            }
         }
     }
     env->CallVoidMethod(
             mFilter,
-            env->GetMethodID(clazz, "onFilterEvent",
-                    "([Landroid/media/tv/tuner/filter/FilterEvent;)V"),
+            gFields.onFilterEventID,
             array);
     return Void();
 }
 
+
 Return<void> FilterCallback::onFilterStatus(const DemuxFilterStatus status) {
     ALOGD("FilterCallback::onFilterStatus");
     JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -308,9 +557,16 @@
 
 /////////////// Filter ///////////////////////
 
-Filter::Filter(sp<IFilter> sp, jweak obj) : mFilterSp(sp), mFilterObj(obj) {}
+Filter::Filter(sp<IFilter> sp, jobject obj) : mFilterSp(sp) {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    mFilterObj = env->NewWeakGlobalRef(obj);
+}
 
 Filter::~Filter() {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+    env->DeleteWeakGlobalRef(mFilterObj);
+    mFilterObj = NULL;
     EventFlag::deleteEventFlag(&mFilterMQEventFlag);
 }
 
@@ -816,6 +1072,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");
@@ -1616,8 +1903,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");
@@ -1625,6 +1911,9 @@
             env->GetMethodID(filterClazz, "<init>", "(I)V");
     gFields.onFilterStatusID =
             env->GetMethodID(filterClazz, "onFilterStatus", "(I)V");
+    gFields.onFilterEventID =
+            env->GetMethodID(filterClazz, "onFilterEvent",
+                    "([Landroid/media/tv/tuner/filter/FilterEvent;)V");
 
     jclass timeFilterClazz = env->FindClass("android/media/tv/tuner/filter/TimeFilter");
     gFields.timeFilterContext = env->GetFieldID(timeFilterClazz, "mNativeContext", "J");
@@ -1737,6 +2026,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);
@@ -2629,6 +2924,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;",
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index fec4cd8..3b8682f 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -114,7 +114,22 @@
     jobject handleToLinearBlock(const native_handle_t* handle, uint32_t size);
 private:
     jweak mFilter;
-    jobject getMediaEvent(const DemuxFilterEvent::Event& event);
+    jobjectArray getSectionEvent(
+            jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
+    jobjectArray getMediaEvent(
+            jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
+    jobjectArray getPesEvent(
+            jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
+    jobjectArray getTsRecordEvent(
+            jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
+    jobjectArray getMmtpRecordEvent(
+            jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
+    jobjectArray getDownloadEvent(
+            jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
+    jobjectArray getIpPayloadEvent(
+            jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
+    jobjectArray getTemiEvent(
+            jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
 };
 
 struct FrontendCallback : public IFrontendCallback {
@@ -129,7 +144,7 @@
 };
 
 struct Filter : public RefBase {
-    Filter(sp<IFilter> sp, jweak obj);
+    Filter(sp<IFilter> sp, jobject obj);
     ~Filter();
     int close();
     sp<IFilter> getIFilter();
@@ -165,6 +180,7 @@
     int setLna(bool enable);
     jobject getLnbIds();
     jobject openLnbById(int id);
+    jobject openLnbByName(jstring name);
     jobject openFilter(DemuxFilterType type, int bufferSize);
     jobject openTimeFilter();
     jobject openDescrambler();
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 c529952..6354ccd 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -320,6 +320,15 @@
         public void onCameraAccessPrioritiesChanged() {
             Log.v(TAG, "Camera access permission change");
         }
+        @Override
+        public void onCameraOpened(String cameraId, String clientPackageName) {
+            Log.v(TAG, String.format("Camera %s is opened by client package %s",
+                    cameraId, clientPackageName));
+        }
+        @Override
+        public void onCameraClosed(String cameraId) {
+            Log.v(TAG, String.format("Camera %s is closed", cameraId));
+        }
     }
 
     /**
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 22c3acf..140c075 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -24,6 +24,7 @@
 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;
@@ -40,6 +41,7 @@
 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;
@@ -47,6 +49,8 @@
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.BatteryControllerImpl;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -63,6 +67,10 @@
 @Module(includes = {DividerModule.class})
 abstract class CarSystemUIModule {
 
+    @Binds
+    abstract NotificationInterruptionStateProvider bindNotificationInterruptionStateProvider(
+            CarNotificationInterruptionStateProvider notificationInterruptionStateProvider);
+
     @Singleton
     @Provides
     @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME)
@@ -100,6 +108,11 @@
             NotificationLockscreenUserManagerImpl notificationLockscreenUserManager);
 
     @Binds
+    @Singleton
+    public abstract BatteryController provideBatteryController(
+            BatteryControllerImpl controllerImpl);
+
+    @Binds
     abstract DockManager bindDockManager(DockManagerImpl dockManager);
 
     @Binds
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
new file mode 100644
index 0000000..447e579
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
@@ -0,0 +1,54 @@
+/*
+ * 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 de768cb..b2e2104 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -95,14 +95,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.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.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;
@@ -250,9 +249,10 @@
             RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
             NotificationGutsManager notificationGutsManager,
             NotificationLogger notificationLogger,
-            NotificationInterruptStateProvider notificationInterruptStateProvider,
+            NotificationInterruptionStateProvider notificationInterruptionStateProvider,
             NotificationViewHierarchyManager notificationViewHierarchyManager,
             KeyguardViewMediator keyguardViewMediator,
+            NotificationAlertingManager notificationAlertingManager,
             DisplayMetrics displayMetrics,
             MetricsLogger metricsLogger,
             @UiBackground Executor uiBgExecutor,
@@ -335,9 +335,10 @@
                 remoteInputQuickSettingsDisabler,
                 notificationGutsManager,
                 notificationLogger,
-                notificationInterruptStateProvider,
+                notificationInterruptionStateProvider,
                 notificationViewHierarchyManager,
                 keyguardViewMediator,
+                notificationAlertingManager,
                 displayMetrics,
                 metricsLogger,
                 uiBgExecutor,
@@ -487,22 +488,6 @@
                                 .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 9a53584..4754118 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
@@ -62,12 +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.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;
@@ -145,9 +146,10 @@
             RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
             NotificationGutsManager notificationGutsManager,
             NotificationLogger notificationLogger,
-            NotificationInterruptStateProvider notificationInterruptionStateProvider,
+            NotificationInterruptionStateProvider notificationInterruptionStateProvider,
             NotificationViewHierarchyManager notificationViewHierarchyManager,
             KeyguardViewMediator keyguardViewMediator,
+            NotificationAlertingManager notificationAlertingManager,
             DisplayMetrics displayMetrics,
             MetricsLogger metricsLogger,
             @UiBackground Executor uiBgExecutor,
@@ -232,6 +234,7 @@
                 notificationInterruptionStateProvider,
                 notificationViewHierarchyManager,
                 keyguardViewMediator,
+                notificationAlertingManager,
                 displayMetrics,
                 metricsLogger,
                 uiBgExecutor,
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 7e78a78..eb0ddfb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -24,6 +24,7 @@
 import static android.media.MediaRoute2Info.TYPE_UNKNOWN;
 import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
 import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
+import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;
 
 import android.app.Notification;
 import android.bluetooth.BluetoothAdapter;
@@ -50,6 +51,7 @@
 public class InfoMediaManager extends MediaManager {
 
     private static final String TAG = "InfoMediaManager";
+    private static final boolean DEBUG = false;
 
     @VisibleForTesting
     final RouterManagerCallback mMediaRouterCallback = new RouterManagerCallback();
@@ -339,6 +341,9 @@
 
     private void buildAllRoutes() {
         for (MediaRoute2Info route : mRouterManager.getAllRoutes()) {
+            if (DEBUG) {
+                Log.d(TAG, "buildAllRoutes() route : " + route.getName());
+            }
             if (route.isSystemRoute()) {
                 addMediaDevice(route);
             }
@@ -347,6 +352,9 @@
 
     private void buildAvailableRoutes() {
         for (MediaRoute2Info route : mRouterManager.getAvailableRoutes(mPackageName)) {
+            if (DEBUG) {
+                Log.d(TAG, "buildAvailableRoutes() route : " + route.getName());
+            }
             addMediaDevice(route);
         }
     }
@@ -363,7 +371,8 @@
                 mediaDevice = new InfoMediaDevice(mContext, mRouterManager, route,
                         mPackageName);
                 if (!TextUtils.isEmpty(mPackageName)
-                        && TextUtils.equals(route.getClientPackageName(), mPackageName)) {
+                        && TextUtils.equals(route.getClientPackageName(), mPackageName)
+                        && mCurrentConnectedDevice == null) {
                     mCurrentConnectedDevice = mediaDevice;
                 }
                 break;
@@ -409,12 +418,41 @@
 
         @Override
         public void onRoutesChanged(List<MediaRoute2Info> routes) {
-            refreshDevices();
+            mMediaDevices.clear();
+            mCurrentConnectedDevice = null;
+            if (TextUtils.isEmpty(mPackageName)) {
+                buildAllRoutes();
+            } else {
+                buildAvailableRoutes();
+            }
+
+            final String id = mCurrentConnectedDevice != null
+                    ? mCurrentConnectedDevice.getId()
+                    : null;
+            dispatchConnectedDeviceChanged(id);
         }
 
         @Override
         public void onRoutesRemoved(List<MediaRoute2Info> routes) {
             refreshDevices();
         }
+
+        @Override
+        public void onTransferred(RoutingSessionInfo oldSession, RoutingSessionInfo newSession) {
+            if (DEBUG) {
+                Log.d(TAG, "onTransferred() oldSession : " + oldSession.getName()
+                        + ", newSession : " + newSession.getName());
+            }
+        }
+
+        @Override
+        public void onTransferFailed(RoutingSessionInfo session, MediaRoute2Info route) {
+            dispatchOnRequestFailed(REASON_UNKNOWN_ERROR);
+        }
+
+        @Override
+        public void onRequestFailed(int reason) {
+            dispatchOnRequestFailed(reason);
+        }
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index adb3c11..90f55dd6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -142,20 +142,11 @@
             mCurrentConnectedDevice.disconnect();
         }
 
-        boolean isConnected = false;
         if (TextUtils.isEmpty(mPackageName)) {
-            isConnected = mInfoMediaManager.connectDeviceWithoutPackageName(device);
+            mInfoMediaManager.connectDeviceWithoutPackageName(device);
         } else {
-            isConnected = device.connect();
+            device.connect();
         }
-        if (isConnected) {
-            mCurrentConnectedDevice = device;
-        }
-
-        final int state = isConnected
-                ? MediaDeviceState.STATE_CONNECTED
-                : MediaDeviceState.STATE_DISCONNECTED;
-        dispatchSelectedDeviceStateChanged(device, state);
     }
 
     void dispatchSelectedDeviceStateChanged(MediaDevice device, @MediaDeviceState int state) {
@@ -186,6 +177,12 @@
         }
     }
 
+    void dispatchOnRequestFailed(int reason) {
+        for (DeviceCallback callback : getCallbacks()) {
+            callback.onRequestFailed(reason);
+        }
+    }
+
     /**
      * Stop scan MediaDevice
      */
@@ -337,7 +334,7 @@
         MediaDevice phoneMediaDevice = null;
         for (MediaDevice device : mMediaDevices) {
             if (device instanceof  BluetoothMediaDevice) {
-                if (isConnected(((BluetoothMediaDevice) device).getCachedDevice())) {
+                if (isActiveDevice(((BluetoothMediaDevice) device).getCachedDevice())) {
                     return device;
                 }
             } else if (device instanceof PhoneMediaDevice) {
@@ -347,7 +344,7 @@
         return mMediaDevices.contains(phoneMediaDevice) ? phoneMediaDevice : null;
     }
 
-    private boolean isConnected(CachedBluetoothDevice device) {
+    private boolean isActiveDevice(CachedBluetoothDevice device) {
         return device.isActiveDevice(BluetoothProfile.A2DP)
                 || device.isActiveDevice(BluetoothProfile.HEARING_AID);
     }
@@ -423,20 +420,28 @@
 
         @Override
         public void onConnectedDeviceChanged(String id) {
-            final MediaDevice connectDevice = getMediaDeviceById(mMediaDevices, id);
+            MediaDevice connectDevice = getMediaDeviceById(mMediaDevices, id);
+            connectDevice = connectDevice != null
+                    ? connectDevice : updateCurrentConnectedDevice();
 
             if (connectDevice == mCurrentConnectedDevice) {
                 Log.d(TAG, "onConnectedDeviceChanged() this device all ready connected!");
                 return;
             }
             mCurrentConnectedDevice = connectDevice;
-            dispatchDeviceAttributesChanged();
+            dispatchSelectedDeviceStateChanged(mCurrentConnectedDevice,
+                    MediaDeviceState.STATE_CONNECTED);
         }
 
         @Override
         public void onDeviceAttributesChanged() {
             dispatchDeviceAttributesChanged();
         }
+
+        @Override
+        public void onRequestFailed(int reason) {
+            dispatchOnRequestFailed(reason);
+        }
     }
 
 
@@ -467,6 +472,18 @@
          * Callback for notifying the device attributes is changed.
          */
         default void onDeviceAttributesChanged() {};
+
+        /**
+         * Callback for notifying that transferring is failed.
+         *
+         * @param reason the reason that the request has failed. Can be one of followings:
+         * {@link android.media.MediaRoute2ProviderService#REASON_UNKNOWN_ERROR},
+         * {@link android.media.MediaRoute2ProviderService#REASON_REJECTED},
+         * {@link android.media.MediaRoute2ProviderService#REASON_NETWORK_ERROR},
+         * {@link android.media.MediaRoute2ProviderService#REASON_ROUTE_NOT_AVAILABLE},
+         * {@link android.media.MediaRoute2ProviderService#REASON_INVALID_COMMAND},
+         */
+        default void onRequestFailed(int reason){};
     }
 
     /**
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java
index 73551f6..e8cbab8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java
@@ -110,6 +110,12 @@
         }
     }
 
+    protected void dispatchOnRequestFailed(int reason) {
+        for (MediaDeviceCallback callback : getCallbacks()) {
+            callback.onRequestFailed(reason);
+        }
+    }
+
     private Collection<MediaDeviceCallback> getCallbacks() {
         return new CopyOnWriteArrayList<>(mCallbacks);
     }
@@ -158,5 +164,17 @@
          * (e.g: device name, connection state, subtitle) is changed.
          */
         void onDeviceAttributesChanged();
+
+        /**
+         * Callback for notifying that transferring is failed.
+         *
+         * @param reason the reason that the request has failed. Can be one of followings:
+         * {@link android.media.MediaRoute2ProviderService#REASON_UNKNOWN_ERROR},
+         * {@link android.media.MediaRoute2ProviderService#REASON_REJECTED},
+         * {@link android.media.MediaRoute2ProviderService#REASON_NETWORK_ERROR},
+         * {@link android.media.MediaRoute2ProviderService#REASON_ROUTE_NOT_AVAILABLE},
+         * {@link android.media.MediaRoute2ProviderService#REASON_INVALID_COMMAND},
+         */
+        void onRequestFailed(int reason);
     }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 7cd0a7c..7f93f69 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -16,6 +16,9 @@
 
 package com.android.settingslib.media;
 
+import static android.media.MediaRoute2ProviderService.REASON_NETWORK_ERROR;
+import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.mock;
@@ -54,6 +57,8 @@
     private MediaRouter2Manager mRouterManager;
     @Mock
     private LocalBluetoothManager mLocalBluetoothManager;
+    @Mock
+    private MediaManager.MediaDeviceCallback mCallback;
 
     private InfoMediaManager mInfoMediaManager;
     private Context mContext;
@@ -144,6 +149,8 @@
     @Test
     public void onRoutesChanged_getAvailableRoutes_shouldAddMediaDevice() {
         final MediaRoute2Info info = mock(MediaRoute2Info.class);
+        mInfoMediaManager.registerCallback(mCallback);
+
         when(info.getId()).thenReturn(TEST_ID);
         when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
 
@@ -160,11 +167,14 @@
         assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
         assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(infoDevice);
         assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
+        verify(mCallback).onConnectedDeviceChanged(TEST_ID);
     }
 
     @Test
     public void onRoutesChanged_buildAllRoutes_shouldAddMediaDevice() {
         final MediaRoute2Info info = mock(MediaRoute2Info.class);
+        mInfoMediaManager.registerCallback(mCallback);
+
         when(info.getId()).thenReturn(TEST_ID);
         when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
         when(info.isSystemRoute()).thenReturn(true);
@@ -182,6 +192,7 @@
         final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
         assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
         assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
+        verify(mCallback).onConnectedDeviceChanged(null);
     }
 
     @Test
@@ -493,4 +504,22 @@
 
         assertThat(mInfoMediaManager.getSessionName()).isEqualTo(TEST_NAME);
     }
+
+    @Test
+    public void onTransferFailed_shouldDispatchOnRequestFailed() {
+        mInfoMediaManager.registerCallback(mCallback);
+
+        mInfoMediaManager.mMediaRouterCallback.onTransferFailed(null, null);
+
+        verify(mCallback).onRequestFailed(REASON_UNKNOWN_ERROR);
+    }
+
+    @Test
+    public void onRequestFailed_shouldDispatchOnRequestFailed() {
+        mInfoMediaManager.registerCallback(mCallback);
+
+        mInfoMediaManager.mMediaRouterCallback.onRequestFailed(REASON_NETWORK_ERROR);
+
+        verify(mCallback).onRequestFailed(REASON_NETWORK_ERROR);
+    }
 }
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 3611dfe..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
@@ -477,4 +475,13 @@
         assertThat(mLocalMediaManager.mMediaDevices).hasSize(3);
         verify(mCallback).onDeviceListUpdate(any());
     }
+
+    @Test
+    public void onRequestFailed_shouldDispatchOnRequestFailed() {
+        mLocalMediaManager.registerCallback(mCallback);
+
+        mLocalMediaManager.mMediaDeviceCallback.onRequestFailed(1);
+
+        verify(mCallback).onRequestFailed(1);
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaManagerTest.java
index ead2be4..a50965a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaManagerTest.java
@@ -136,4 +136,14 @@
 
         assertThat(device).isNull();
     }
+
+    @Test
+    public void dispatchOnRequestFailed_registerCallback_shouldDispatchCallback() {
+        mMediaManager.registerCallback(mCallback);
+
+        mMediaManager.dispatchOnRequestFailed(1);
+
+        verify(mCallback).onRequestFailed(1);
+    }
+
 }
diff --git a/packages/SettingsProvider/res/values-ta/strings.xml b/packages/SettingsProvider/res/values-ta/strings.xml
index fa6b8cd..54d2242 100644
--- a/packages/SettingsProvider/res/values-ta/strings.xml
+++ b/packages/SettingsProvider/res/values-ta/strings.xml
@@ -20,8 +20,6 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4567566098528588863">"அமைப்புகளின் சேமிப்பிடம்"</string>
-    <!-- no translation found for wifi_softap_config_change (5688373762357941645) -->
-    <skip />
-    <!-- no translation found for wifi_softap_config_change_summary (8946397286141531087) -->
-    <skip />
+    <string name="wifi_softap_config_change" msgid="5688373762357941645">"ஹாட்ஸ்பாட் அமைப்புகள் மாற்றப்பட்டன"</string>
+    <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"விவரங்களைப் பார்க்க, தட்டவும்"</string>
 </resources>
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/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 e13e49f..8f859b2 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -204,7 +204,6 @@
 
     <!-- Permission needed to run network tests in CTS -->
     <uses-permission android:name="android.permission.MANAGE_TEST_NETWORKS" />
-    <uses-permission android:name="android.permission.NETWORK_STACK" />
     <!-- Permission needed to test tcp keepalive offload. -->
     <uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" />
 
@@ -276,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/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 30b461d..da93db7 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -147,6 +147,7 @@
     <uses-permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY" />
 
     <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="android.permission.CAMERA_OPEN_CLOSE_LISTENER" />
 
     <!-- Screen Capturing -->
     <uses-permission android:name="android.permission.MANAGE_MEDIA_PROJECTION" />
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/layout/global_actions_grid_v2.xml b/packages/SystemUI/res/layout/global_actions_grid_v2.xml
index ff0c6a7..92ae1b9 100644
--- a/packages/SystemUI/res/layout/global_actions_grid_v2.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid_v2.xml
@@ -1,71 +1,75 @@
 <?xml version="1.0" encoding="utf-8"?>
-<ScrollView
+<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/global_actions_container"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-  <androidx.constraintlayout.widget.ConstraintLayout
-      android:id="@+id/global_actions_grid_root"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+>
+  <com.android.systemui.globalactions.GlobalActionsFlatLayout
+      android:id="@id/global_actions_view"
       android:layout_width="match_parent"
-      android:layout_height="match_parent"
+      android:layout_height="wrap_content"
+      android:orientation="horizontal"
+      android:theme="@style/qs_theme"
+      android:gravity="top | center_horizontal"
       android:clipChildren="false"
       android:clipToPadding="false"
-      android:paddingBottom="@dimen/global_actions_grid_container_shadow_offset"
-      android:layout_marginBottom="@dimen/global_actions_grid_container_negative_shadow_offset">
-
-    <com.android.systemui.globalactions.GlobalActionsFlatLayout
-        android:id="@id/global_actions_view"
-        android:layout_width="match_parent"
+      android:layout_marginTop="@dimen/global_actions_top_margin"
+  >
+    <LinearLayout
+        android:id="@android:id/list"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:layout_marginLeft="@dimen/global_actions_grid_side_margin"
+        android:layout_marginRight="@dimen/global_actions_grid_side_margin"
+        android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+        android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+        android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+        android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
         android:orientation="horizontal"
-        android:theme="@style/qs_theme"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintLeft_toLeftOf="parent"
-        app:layout_constraintRight_toRightOf="parent"
-        android:gravity="top | center_horizontal"
+        android:gravity="left"
+        android:translationZ="@dimen/global_actions_translate"
+    />
+  </com.android.systemui.globalactions.GlobalActionsFlatLayout>
+
+  <com.android.systemui.globalactions.MinHeightScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingBottom="@dimen/global_actions_grid_container_shadow_offset"
+    android:layout_marginBottom="@dimen/global_actions_grid_container_negative_shadow_offset"
+    android:orientation="vertical"
+  >
+    <LinearLayout
+        android:id="@+id/global_actions_grid_root"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
         android:clipChildren="false"
+        android:orientation="vertical"
         android:clipToPadding="false"
-        android:layout_marginTop="@dimen/global_actions_top_margin">
+    >
       <LinearLayout
-          android:id="@android:id/list"
-          android:layout_width="wrap_content"
-          android:layout_height="wrap_content"
-          android:layout_marginLeft="@dimen/global_actions_grid_side_margin"
-          android:layout_marginRight="@dimen/global_actions_grid_side_margin"
-          android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
-          android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
-          android:paddingTop="@dimen/global_actions_grid_vertical_padding"
-          android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
-          android:orientation="horizontal"
-          android:gravity="left"
-          android:translationZ="@dimen/global_actions_translate" />
-    </com.android.systemui.globalactions.GlobalActionsFlatLayout>
-
-    <LinearLayout
-        android:id="@+id/global_actions_panel"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical"
-        app:layout_constraintLeft_toLeftOf="parent"
-        app:layout_constraintRight_toRightOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/global_actions_view">
-
-      <FrameLayout
-          android:id="@+id/global_actions_panel_container"
+          android:id="@+id/global_actions_panel"
           android:layout_width="match_parent"
-          android:layout_height="wrap_content" />
-    </LinearLayout>
+          android:layout_height="wrap_content"
+          android:orientation="vertical"
+      >
+        <FrameLayout
+            android:id="@+id/global_actions_panel_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+        />
+      </LinearLayout>
 
-    <LinearLayout
-        android:id="@+id/global_actions_controls"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical"
-        app:layout_constraintLeft_toLeftOf="parent"
-        app:layout_constraintRight_toRightOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/global_actions_panel"
-        app:layout_constraintBottom_toBottomOf="parent" />
-  </androidx.constraintlayout.widget.ConstraintLayout>
-</ScrollView>
+      <LinearLayout
+          android:id="@+id/global_actions_controls"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+          android:orientation="vertical"
+          android:layout_marginRight="@dimen/global_actions_grid_horizontal_padding"
+          android:layout_marginLeft="@dimen/global_actions_grid_horizontal_padding"
+      />
+    </LinearLayout>
+  </com.android.systemui.globalactions.MinHeightScrollView>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/global_actions_wrapped.xml b/packages/SystemUI/res/layout/global_actions_wrapped.xml
deleted file mode 100644
index d441070..0000000
--- a/packages/SystemUI/res/layout/global_actions_wrapped.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<com.android.systemui.HardwareUiLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@id/global_actions_view"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:layout_gravity="top|right"
-    android:layout_marginBottom="0dp"
-    android:orientation="vertical"
-    android:paddingTop="@dimen/global_actions_top_padding"
-    android:clipToPadding="false"
-    android:theme="@style/qs_theme"
-    android:clipChildren="false">
-
-    <!-- Global actions is right-aligned to be physically near power button -->
-    <LinearLayout
-        android:id="@android:id/list"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="top|right"
-        android:gravity="center"
-        android:orientation="vertical"
-        android:padding="@dimen/global_actions_padding"
-        android:translationZ="@dimen/global_actions_translate" />
-
-    <!-- For separated button-->
-    <FrameLayout
-        android:id="@+id/separated_button"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="top|right"
-        android:layout_marginTop="6dp"
-        android:gravity="center"
-        android:orientation="vertical"
-        android:padding="@dimen/global_actions_padding"
-        android:translationZ="@dimen/global_actions_translate" />
-
-</com.android.systemui.HardwareUiLayout>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index caf22fe..18fec29 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2653,4 +2653,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..4770910 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">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index ca88f13..aed7c21 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -44,7 +44,9 @@
     public final Rect clipRect;
     public final int prefixOrderIndex;
     public final Point position;
+    public final Rect localBounds;
     public final Rect sourceContainerBounds;
+    public final Rect screenSpaceBounds;
     public final boolean isNotInRecents;
     public final Rect contentInsets;
 
@@ -57,7 +59,9 @@
         isTranslucent = app.isTranslucent;
         clipRect = app.clipRect;
         position = app.position;
+        localBounds = app.localBounds;
         sourceContainerBounds = app.sourceContainerBounds;
+        screenSpaceBounds = app.screenSpaceBounds;
         prefixOrderIndex = app.prefixOrderIndex;
         isNotInRecents = app.isNotInRecents;
         contentInsets = app.contentInsets;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java
index 7cfa289..d33c653 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java
@@ -20,6 +20,7 @@
 import android.graphics.PixelFormat;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.util.Size;
 import android.view.SurfaceControl;
 import android.view.SurfaceControlViewHost;
 import android.view.View;
@@ -43,13 +44,27 @@
         mOpacity = opacity;
     }
 
-    /** Called whenever a surface view request is received. */
+    /** See {@link #onReceive(Context, Bundle, View, Size)}. */
     public void onReceive(Context context, Bundle bundle, View view) {
+        onReceive(context, bundle, view, null);
+    }
+
+    /**
+     * Called whenever a surface view request is received.
+     * @param view     the view rendering content, on the receiver end of the surface request.
+     * @param viewSize when {@param viewSize} is not specified, we will use the surface control size
+     *                 to attach the view to the window.
+     */
+    public void onReceive(Context context, Bundle bundle, View view, Size viewSize) {
         if (mSurfaceControlViewHost != null) {
             mSurfaceControlViewHost.die();
         }
         SurfaceControl surfaceControl = SurfaceViewRequestUtils.getSurfaceControl(bundle);
         if (surfaceControl != null) {
+            if (viewSize == null) {
+                viewSize = new Size(surfaceControl.getWidth(), surfaceControl.getHeight());
+            }
+
             IBinder hostToken = SurfaceViewRequestUtils.getHostToken(bundle);
 
             WindowlessWindowManager windowlessWindowManager =
@@ -59,12 +74,22 @@
                     context.getDisplayNoVerify(), windowlessWindowManager);
             WindowManager.LayoutParams layoutParams =
                     new WindowManager.LayoutParams(
-                            surfaceControl.getWidth(),
-                            surfaceControl.getHeight(),
+                            viewSize.getWidth(),
+                            viewSize.getHeight(),
                             WindowManager.LayoutParams.TYPE_APPLICATION,
                             WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
                             mOpacity);
 
+            // This aspect scales the view to fit in the surface and centers it
+            final float scale = Math.min(surfaceControl.getWidth() / (float) viewSize.getWidth(),
+                    surfaceControl.getHeight() / (float) viewSize.getHeight());
+            view.setScaleX(scale);
+            view.setScaleY(scale);
+            view.setPivotX(0);
+            view.setPivotY(0);
+            view.setTranslationX((surfaceControl.getWidth() - scale * viewSize.getWidth()) / 2);
+            view.setTranslationY((surfaceControl.getHeight() - scale * viewSize.getHeight()) / 2);
+
             mSurfaceControlViewHost.addView(view, layoutParams);
         }
     }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestUtils.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestUtils.java
index 0cbd541..4409276 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestUtils.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestUtils.java
@@ -36,7 +36,7 @@
     }
 
     /**
-     * Retrieves the SurfaceControl from an Intent created by
+     * Retrieves the SurfaceControl from a bundle created by
      * {@link #createSurfaceBundle(SurfaceView)}.
      **/
     public static SurfaceControl getSurfaceControl(Bundle bundle) {
@@ -44,7 +44,7 @@
     }
 
     /**
-     * Retrieves the input token from an Intent created by
+     * Retrieves the input token from a bundle created by
      * {@link #createSurfaceBundle(SurfaceView)}.
      **/
     public static @Nullable IBinder getHostToken(Bundle bundle) {
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/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
index 0367464..2200b22 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
@@ -43,6 +43,7 @@
 import com.android.systemui.util.InjectionInflationController;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -74,8 +75,8 @@
     private final ContentObserver mContentObserver =
             new ContentObserver(mMainHandler) {
                 @Override
-                public void onChange(boolean selfChange, Uri uri, int userId) {
-                    super.onChange(selfChange, uri, userId);
+                public void onChange(boolean selfChange, Collection<Uri> uris,
+                        int flags, int userId) {
                     if (Objects.equals(userId,
                             mCurrentUserObservable.getCurrentUser().getValue())) {
                         reload();
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index b6152da..a868cf5 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -71,11 +71,12 @@
 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;
@@ -288,6 +289,7 @@
     @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;
@@ -487,6 +489,8 @@
         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/HardwareUiLayout.java b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
deleted file mode 100644
index ad2e002..0000000
--- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
+++ /dev/null
@@ -1,570 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui;
-
-import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
-import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
-import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.provider.Settings;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
-import android.view.ViewTreeObserver;
-import android.widget.LinearLayout;
-
-import com.android.systemui.tuner.TunerService;
-import com.android.systemui.tuner.TunerService.Tunable;
-import com.android.systemui.util.leak.RotationUtils;
-
-/**
- * Layout for placing two containers at a specific physical position on the device, relative to the
- * device's hardware, regardless of screen rotation.
- */
-public class HardwareUiLayout extends MultiListLayout implements Tunable {
-
-    private static final String EDGE_BLEED = "sysui_hwui_edge_bleed";
-    private static final String ROUNDED_DIVIDER = "sysui_hwui_rounded_divider";
-    private final int[] mTmp2 = new int[2];
-    private ViewGroup mList;
-    private ViewGroup mSeparatedView;
-    private int mOldHeight;
-    private boolean mAnimating;
-    private AnimatorSet mAnimation;
-    private View mDivision;
-    private HardwareBgDrawable mListBackground;
-    private HardwareBgDrawable mSeparatedViewBackground;
-    private Animator mAnimator;
-    private boolean mCollapse;
-    private int mEndPoint;
-    private boolean mEdgeBleed;
-    private boolean mRoundedDivider;
-    private boolean mRotatedBackground;
-    private boolean mSwapOrientation = true;
-
-    public HardwareUiLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        // Manually re-initialize mRotation to portrait-mode, since this view must always
-        // be constructed in portrait mode and rotated into the correct initial position.
-        mRotation = ROTATION_NONE;
-        updateSettings();
-    }
-
-    @Override
-    protected ViewGroup getSeparatedView() {
-        return findViewById(com.android.systemui.R.id.separated_button);
-    }
-
-    @Override
-    protected ViewGroup getListView() {
-        return findViewById(android.R.id.list);
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        updateSettings();
-        Dependency.get(TunerService.class).addTunable(this, EDGE_BLEED, ROUNDED_DIVIDER);
-        getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsListener);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsListener);
-        Dependency.get(TunerService.class).removeTunable(this);
-    }
-
-    @Override
-    public void onTuningChanged(String key, String newValue) {
-        updateSettings();
-    }
-
-    private void updateSettings() {
-        mEdgeBleed = Settings.Secure.getInt(getContext().getContentResolver(),
-                EDGE_BLEED, 0) != 0;
-        mRoundedDivider = Settings.Secure.getInt(getContext().getContentResolver(),
-                ROUNDED_DIVIDER, 0) != 0;
-        updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding());
-        mListBackground = new HardwareBgDrawable(mRoundedDivider, !mEdgeBleed, getContext());
-        mSeparatedViewBackground = new HardwareBgDrawable(mRoundedDivider, !mEdgeBleed,
-                getContext());
-        if (mList != null) {
-            mList.setBackground(mListBackground);
-            mSeparatedView.setBackground(mSeparatedViewBackground);
-            requestLayout();
-        }
-    }
-
-    private void updateEdgeMargin(int edge) {
-        if (mList != null) {
-            MarginLayoutParams params = (MarginLayoutParams) mList.getLayoutParams();
-            if (mRotation == ROTATION_LANDSCAPE) {
-                params.topMargin = edge;
-            } else if (mRotation == ROTATION_SEASCAPE) {
-                params.bottomMargin = edge;
-            } else {
-                params.rightMargin = edge;
-            }
-            mList.setLayoutParams(params);
-        }
-
-        if (mSeparatedView != null) {
-            MarginLayoutParams params = (MarginLayoutParams) mSeparatedView.getLayoutParams();
-            if (mRotation == ROTATION_LANDSCAPE) {
-                params.topMargin = edge;
-            } else if (mRotation == ROTATION_SEASCAPE) {
-                params.bottomMargin = edge;
-            } else {
-                params.rightMargin = edge;
-            }
-            mSeparatedView.setLayoutParams(params);
-        }
-    }
-
-    private int getEdgePadding() {
-        return getContext().getResources().getDimensionPixelSize(R.dimen.edge_margin);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        if (mList == null) {
-            if (getChildCount() != 0) {
-                mList = getListView();
-                mList.setBackground(mListBackground);
-                mSeparatedView = getSeparatedView();
-                mSeparatedView.setBackground(mSeparatedViewBackground);
-                updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding());
-                mOldHeight = mList.getMeasuredHeight();
-
-                // Must be called to initialize view rotation correctly.
-                // Requires LayoutParams, hence why this isn't called during the constructor.
-                updateRotation();
-            } else {
-                return;
-            }
-        }
-        int newHeight = mList.getMeasuredHeight();
-        if (newHeight != mOldHeight) {
-            animateChild(mOldHeight, newHeight);
-        }
-
-        post(() -> updatePaddingAndGravityIfTooTall());
-        post(() -> updatePosition());
-    }
-
-    public void setSwapOrientation(boolean swapOrientation) {
-        mSwapOrientation = swapOrientation;
-    }
-
-    private void updateRotation() {
-        int rotation = RotationUtils.getRotation(getContext());
-        if (rotation != mRotation) {
-            rotate(mRotation, rotation);
-            mRotation = rotation;
-        }
-    }
-
-    /**
-     *  Requires LayoutParams to be set to work correctly, and therefore must be run after after
-     *  the HardwareUILayout has been added to the view hierarchy.
-     */
-    protected void rotate(int from, int to) {
-        super.rotate(from, to);
-        if (from != ROTATION_NONE && to != ROTATION_NONE) {
-            // Rather than handling this confusing case, just do 2 rotations.
-            rotate(from, ROTATION_NONE);
-            rotate(ROTATION_NONE, to);
-            return;
-        }
-        if (from == ROTATION_LANDSCAPE || to == ROTATION_SEASCAPE) {
-            rotateRight();
-        } else {
-            rotateLeft();
-        }
-        if (mAdapter.hasSeparatedItems()) {
-            if (from == ROTATION_SEASCAPE || to == ROTATION_SEASCAPE) {
-                // Separated view has top margin, so seascape separated view need special rotation,
-                // not a full left or right rotation.
-                swapLeftAndTop(mSeparatedView);
-            } else if (from == ROTATION_LANDSCAPE) {
-                rotateRight(mSeparatedView);
-            } else {
-                rotateLeft(mSeparatedView);
-            }
-        }
-        if (to != ROTATION_NONE) {
-            if (mList instanceof LinearLayout) {
-                mRotatedBackground = true;
-                mListBackground.setRotatedBackground(true);
-                mSeparatedViewBackground.setRotatedBackground(true);
-                LinearLayout linearLayout = (LinearLayout) mList;
-                if (mSwapOrientation) {
-                    linearLayout.setOrientation(LinearLayout.HORIZONTAL);
-                    setOrientation(LinearLayout.HORIZONTAL);
-                }
-                swapDimens(mList);
-                swapDimens(mSeparatedView);
-            }
-        } else {
-            if (mList instanceof LinearLayout) {
-                mRotatedBackground = false;
-                mListBackground.setRotatedBackground(false);
-                mSeparatedViewBackground.setRotatedBackground(false);
-                LinearLayout linearLayout = (LinearLayout) mList;
-                if (mSwapOrientation) {
-                    linearLayout.setOrientation(LinearLayout.VERTICAL);
-                    setOrientation(LinearLayout.VERTICAL);
-                }
-                swapDimens(mList);
-                swapDimens(mSeparatedView);
-            }
-        }
-    }
-
-    @Override
-    public void onUpdateList() {
-        super.onUpdateList();
-
-        for (int i = 0; i < mAdapter.getCount(); i++) {
-            ViewGroup parent;
-            boolean separated = mAdapter.shouldBeSeparated(i);
-            if (separated) {
-                parent = getSeparatedView();
-            } else {
-                parent = getListView();
-            }
-            View v = mAdapter.getView(i, null, parent);
-            parent.addView(v);
-        }
-    }
-
-    private void rotateRight() {
-        rotateRight(this);
-        rotateRight(mList);
-        swapDimens(this);
-
-        LayoutParams p = (LayoutParams) mList.getLayoutParams();
-        p.gravity = rotateGravityRight(p.gravity);
-        mList.setLayoutParams(p);
-
-        LayoutParams separatedViewLayoutParams = (LayoutParams) mSeparatedView.getLayoutParams();
-        separatedViewLayoutParams.gravity = rotateGravityRight(separatedViewLayoutParams.gravity);
-        mSeparatedView.setLayoutParams(separatedViewLayoutParams);
-
-        setGravity(rotateGravityRight(getGravity()));
-    }
-
-    private void swapDimens(View v) {
-        ViewGroup.LayoutParams params = v.getLayoutParams();
-        int h = params.width;
-        params.width = params.height;
-        params.height = h;
-        v.setLayoutParams(params);
-    }
-
-    private int rotateGravityRight(int gravity) {
-        int retGravity = 0;
-        int layoutDirection = getLayoutDirection();
-        final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
-        final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
-
-        switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
-            case Gravity.CENTER_HORIZONTAL:
-                retGravity |= Gravity.CENTER_VERTICAL;
-                break;
-            case Gravity.RIGHT:
-                retGravity |= Gravity.BOTTOM;
-                break;
-            case Gravity.LEFT:
-            default:
-                retGravity |= Gravity.TOP;
-                break;
-        }
-
-        switch (verticalGravity) {
-            case Gravity.CENTER_VERTICAL:
-                retGravity |= Gravity.CENTER_HORIZONTAL;
-                break;
-            case Gravity.BOTTOM:
-                retGravity |= Gravity.LEFT;
-                break;
-            case Gravity.TOP:
-            default:
-                retGravity |= Gravity.RIGHT;
-                break;
-        }
-        return retGravity;
-    }
-
-    private void rotateLeft() {
-        rotateLeft(this);
-        rotateLeft(mList);
-        swapDimens(this);
-
-        LayoutParams p = (LayoutParams) mList.getLayoutParams();
-        p.gravity = rotateGravityLeft(p.gravity);
-        mList.setLayoutParams(p);
-
-        LayoutParams separatedViewLayoutParams = (LayoutParams) mSeparatedView.getLayoutParams();
-        separatedViewLayoutParams.gravity = rotateGravityLeft(separatedViewLayoutParams.gravity);
-        mSeparatedView.setLayoutParams(separatedViewLayoutParams);
-
-        setGravity(rotateGravityLeft(getGravity()));
-    }
-
-    private int rotateGravityLeft(int gravity) {
-        if (gravity == -1) {
-            gravity = Gravity.TOP | Gravity.START;
-        }
-        int retGravity = 0;
-        int layoutDirection = getLayoutDirection();
-        final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
-        final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
-
-        switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
-            case Gravity.CENTER_HORIZONTAL:
-                retGravity |= Gravity.CENTER_VERTICAL;
-                break;
-            case Gravity.RIGHT:
-                retGravity |= Gravity.TOP;
-                break;
-            case Gravity.LEFT:
-            default:
-                retGravity |= Gravity.BOTTOM;
-                break;
-        }
-
-        switch (verticalGravity) {
-            case Gravity.CENTER_VERTICAL:
-                retGravity |= Gravity.CENTER_HORIZONTAL;
-                break;
-            case Gravity.BOTTOM:
-                retGravity |= Gravity.RIGHT;
-                break;
-            case Gravity.TOP:
-            default:
-                retGravity |= Gravity.LEFT;
-                break;
-        }
-        return retGravity;
-    }
-
-    private void rotateLeft(View v) {
-        v.setPadding(v.getPaddingTop(), v.getPaddingRight(), v.getPaddingBottom(),
-                v.getPaddingLeft());
-        MarginLayoutParams params = (MarginLayoutParams) v.getLayoutParams();
-        params.setMargins(params.topMargin, params.rightMargin, params.bottomMargin,
-                params.leftMargin);
-        v.setLayoutParams(params);
-    }
-
-    private void rotateRight(View v) {
-        v.setPadding(v.getPaddingBottom(), v.getPaddingLeft(), v.getPaddingTop(),
-                v.getPaddingRight());
-        MarginLayoutParams params = (MarginLayoutParams) v.getLayoutParams();
-        params.setMargins(params.bottomMargin, params.leftMargin, params.topMargin,
-                params.rightMargin);
-        v.setLayoutParams(params);
-    }
-
-    private void swapLeftAndTop(View v) {
-        v.setPadding(v.getPaddingTop(), v.getPaddingLeft(), v.getPaddingBottom(),
-                v.getPaddingRight());
-        MarginLayoutParams params = (MarginLayoutParams) v.getLayoutParams();
-        params.setMargins(params.topMargin, params.leftMargin, params.bottomMargin,
-                params.rightMargin);
-        v.setLayoutParams(params);
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-
-        post(() -> updatePosition());
-
-    }
-
-    private void animateChild(int oldHeight, int newHeight) {
-        if (true) return;
-        if (mAnimating) {
-            mAnimation.cancel();
-        }
-        mAnimating = true;
-        mAnimation = new AnimatorSet();
-        mAnimation.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mAnimating = false;
-            }
-        });
-        int fromTop = mList.getTop();
-        int fromBottom = mList.getBottom();
-        int toTop = fromTop - ((newHeight - oldHeight) / 2);
-        int toBottom = fromBottom + ((newHeight - oldHeight) / 2);
-        ObjectAnimator top = ObjectAnimator.ofInt(mList, "top", fromTop, toTop);
-        top.addUpdateListener(animation -> mListBackground.invalidateSelf());
-        mAnimation.playTogether(top,
-                ObjectAnimator.ofInt(mList, "bottom", fromBottom, toBottom));
-    }
-
-    public void setDivisionView(View v) {
-        mDivision = v;
-        if (mDivision != null) {
-            mDivision.addOnLayoutChangeListener(
-                    (v1, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
-                            updatePosition());
-        }
-        updatePosition();
-    }
-
-    private void updatePosition() {
-        if (mList == null) return;
-        // If got separated button, setRotatedBackground to false,
-        // all items won't get white background.
-        boolean separated = mAdapter.hasSeparatedItems();
-        mListBackground.setRotatedBackground(separated);
-        mSeparatedViewBackground.setRotatedBackground(separated);
-        if (mDivision != null && mDivision.getVisibility() == VISIBLE) {
-            int index = mRotatedBackground ? 0 : 1;
-            mDivision.getLocationOnScreen(mTmp2);
-            float trans = mRotatedBackground ? mDivision.getTranslationX()
-                    : mDivision.getTranslationY();
-            int viewTop = (int) (mTmp2[index] + trans);
-            mList.getLocationOnScreen(mTmp2);
-            viewTop -= mTmp2[index];
-            setCutPoint(viewTop);
-        } else {
-            setCutPoint(mList.getMeasuredHeight());
-        }
-    }
-
-    private void setCutPoint(int point) {
-        int curPoint = mListBackground.getCutPoint();
-        if (curPoint == point) return;
-        if (getAlpha() == 0 || curPoint == 0) {
-            mListBackground.setCutPoint(point);
-            return;
-        }
-        if (mAnimator != null) {
-            if (mEndPoint == point) {
-                return;
-            }
-            mAnimator.cancel();
-        }
-        mEndPoint = point;
-        mAnimator = ObjectAnimator.ofInt(mListBackground, "cutPoint", curPoint, point);
-        if (mCollapse) {
-            mAnimator.setStartDelay(300);
-            mCollapse = false;
-        }
-        mAnimator.start();
-    }
-
-    // If current power menu height larger then screen height, remove padding to break power menu
-    // alignment and set menu center vertical within the screen.
-    private void updatePaddingAndGravityIfTooTall() {
-        int defaultTopPadding;
-        int viewsTotalHeight;
-        int separatedViewTopMargin;
-        int screenHeight;
-        int totalHeight;
-        int targetGravity;
-        boolean separated = mAdapter.hasSeparatedItems();
-        MarginLayoutParams params = (MarginLayoutParams) mSeparatedView.getLayoutParams();
-        switch (RotationUtils.getRotation(getContext())) {
-            case RotationUtils.ROTATION_LANDSCAPE:
-                defaultTopPadding = getPaddingLeft();
-                viewsTotalHeight = mList.getMeasuredWidth() + mSeparatedView.getMeasuredWidth();
-                separatedViewTopMargin = separated ? params.leftMargin : 0;
-                screenHeight = getMeasuredWidth();
-                targetGravity = Gravity.CENTER_HORIZONTAL|Gravity.TOP;
-                break;
-            case RotationUtils.ROTATION_SEASCAPE:
-                defaultTopPadding = getPaddingRight();
-                viewsTotalHeight = mList.getMeasuredWidth() + mSeparatedView.getMeasuredWidth();
-                separatedViewTopMargin = separated ? params.leftMargin : 0;
-                screenHeight = getMeasuredWidth();
-                targetGravity = Gravity.CENTER_HORIZONTAL|Gravity.BOTTOM;
-                break;
-            default: // Portrait
-                defaultTopPadding = getPaddingTop();
-                viewsTotalHeight = mList.getMeasuredHeight() + mSeparatedView.getMeasuredHeight();
-                separatedViewTopMargin = separated ? params.topMargin : 0;
-                screenHeight = getMeasuredHeight();
-                targetGravity = Gravity.CENTER_VERTICAL|Gravity.RIGHT;
-                break;
-        }
-        totalHeight = defaultTopPadding + viewsTotalHeight + separatedViewTopMargin;
-        if (totalHeight >= screenHeight) {
-            setPadding(0, 0, 0, 0);
-            setGravity(targetGravity);
-        }
-    }
-
-    @Override
-    public ViewOutlineProvider getOutlineProvider() {
-        return super.getOutlineProvider();
-    }
-
-    public void setCollapse() {
-        mCollapse = true;
-    }
-
-    private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener = inoutInfo -> {
-        if (mHasOutsideTouch || (mList == null)) {
-            inoutInfo.setTouchableInsets(
-                    ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
-            return;
-        }
-        inoutInfo.setTouchableInsets(
-                ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT);
-        inoutInfo.contentInsets.set(mList.getLeft(), mList.getTop(),
-                0, getBottom() - mList.getBottom());
-    };
-
-    private float getAnimationDistance() {
-        return getContext().getResources().getDimension(
-                com.android.systemui.R.dimen.global_actions_panel_width) / 2;
-    }
-
-    @Override
-    public float getAnimationOffsetX() {
-        if (RotationUtils.getRotation(mContext) == ROTATION_NONE) {
-            return getAnimationDistance();
-        }
-        return 0;
-    }
-
-    @Override
-    public float getAnimationOffsetY() {
-        switch (RotationUtils.getRotation(getContext())) {
-            case RotationUtils.ROTATION_LANDSCAPE:
-                return -getAnimationDistance();
-            case RotationUtils.ROTATION_SEASCAPE:
-                return getAnimationDistance();
-            default: // Portrait
-                return 0;
-        }
-    }
-}
\ No newline at end of file
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/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 7ca2308..4240209c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -279,7 +279,8 @@
     /**
      * @return the display id of the virtual display on which bubble contents is drawn.
      */
-    int getDisplayId() {
+    @Override
+    public int getDisplayId() {
         return mExpandedView != null ? mExpandedView.getVirtualDisplayId() : INVALID_DISPLAY;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index f873f42..ae1438e 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 NotificationInterruptStateProvider mNotificationInterruptStateProvider;
+    private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
     private IStatusBarService mBarService;
 
     // Used for determining view rect for touch interaction
@@ -279,7 +279,7 @@
             ShadeController shadeController,
             BubbleData data,
             ConfigurationController configurationController,
-            NotificationInterruptStateProvider interruptionStateProvider,
+            NotificationInterruptionStateProvider interruptionStateProvider,
             ZenModeController zenModeController,
             NotificationLockscreenUserManager notifUserManager,
             NotificationGroupManager groupManager,
@@ -304,7 +304,7 @@
             BubbleData data,
             @Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
             ConfigurationController configurationController,
-            NotificationInterruptStateProvider interruptionStateProvider,
+            NotificationInterruptionStateProvider interruptionStateProvider,
             ZenModeController zenModeController,
             NotificationLockscreenUserManager notifUserManager,
             NotificationGroupManager groupManager,
@@ -316,7 +316,7 @@
         dumpManager.registerDumpable(TAG, this);
         mContext = context;
         mShadeController = shadeController;
-        mNotificationInterruptStateProvider = interruptionStateProvider;
+        mNotificationInterruptionStateProvider = interruptionStateProvider;
         mNotifUserManager = notifUserManager;
         mZenModeController = zenModeController;
         mFloatingContentCoordinator = floatingContentCoordinator;
@@ -632,7 +632,7 @@
         for (NotificationEntry e :
                 mNotificationEntryManager.getActiveNotificationsForCurrentUser()) {
             if (savedBubbleKeys.contains(e.getKey())
-                    && mNotificationInterruptStateProvider.shouldBubbleUp(e)
+                    && mNotificationInterruptionStateProvider.shouldBubbleUp(e)
                     && canLaunchInActivityView(mContext, e)) {
                 updateBubble(e, /* suppressFlyout= */ true);
             }
@@ -894,7 +894,7 @@
         boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
                 mContext, entry, previouslyUserCreated, userBlocked);
 
-        if (mNotificationInterruptStateProvider.shouldBubbleUp(entry)
+        if (mNotificationInterruptionStateProvider.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 = mNotificationInterruptStateProvider.shouldBubbleUp(entry)
+        boolean shouldBubble = mNotificationInterruptionStateProvider.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
@@ -1175,23 +1175,17 @@
      * status bar, otherwise returns {@link Display#INVALID_DISPLAY}.
      */
     public int getExpandedDisplayId(Context context) {
-        final Bubble bubble = getExpandedBubble(context);
-        return bubble != null ? bubble.getDisplayId() : INVALID_DISPLAY;
-    }
-
-    @Nullable
-    private Bubble getExpandedBubble(Context context) {
         if (mStackView == null) {
-            return null;
+            return INVALID_DISPLAY;
         }
         final boolean defaultDisplay = context.getDisplay() != null
                 && context.getDisplay().getDisplayId() == DEFAULT_DISPLAY;
-        final Bubble expandedBubble = mStackView.getExpandedBubble();
-        if (defaultDisplay && expandedBubble != null && isStackExpanded()
+        final BubbleViewProvider expandedViewProvider = mStackView.getExpandedBubble();
+        if (defaultDisplay && expandedViewProvider != null && isStackExpanded()
                 && !mNotificationShadeWindowController.getPanelExpanded()) {
-            return expandedBubble;
+            return expandedViewProvider.getDisplayId();
         }
-        return null;
+        return INVALID_DISPLAY;
     }
 
     @VisibleForTesting
@@ -1233,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);
@@ -1256,7 +1261,7 @@
 
         @Override
         public void onSingleTaskDisplayEmpty(int displayId) {
-            final Bubble expandedBubble = mStackView != null
+            final BubbleViewProvider expandedBubble = mStackView != null
                     ? mStackView.getExpandedBubble()
                     : null;
             int expandedId = expandedBubble != null ? expandedBubble.getDisplayId() : -1;
@@ -1311,7 +1316,7 @@
     private class BubblesImeListener extends PinnedStackListenerForwarder.PinnedStackListener {
         @Override
         public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
-            if (mStackView != null && mStackView.getBubbleCount() > 0) {
+            if (mStackView != null) {
                 mStackView.post(() -> mStackView.onImeVisibilityChanged(imeVisible, imeHeight));
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java
index e800011..19733a5 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java
@@ -59,13 +59,15 @@
         return FORCE_SHOW_USER_EDUCATION || forceShow;
     }
 
-    static String formatBubblesString(List<Bubble> bubbles, Bubble selected) {
+    static String formatBubblesString(List<Bubble> bubbles, BubbleViewProvider selected) {
         StringBuilder sb = new StringBuilder();
         for (Bubble bubble : bubbles) {
             if (bubble == null) {
                 sb.append("   <null> !!!!!\n");
             } else {
-                boolean isSelected = (selected != null && bubble == selected);
+                boolean isSelected = (selected != null
+                        && selected.getKey() != BubbleOverflow.KEY
+                        && bubble == selected);
                 String arrow = isSelected ? "=>" : "  ";
                 sb.append(String.format("%s Bubble{act=%12d, ongoing=%d, key=%s}\n",
                         arrow,
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
index 313bb42..4fb2d08 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.bubbles;
 
+import static android.view.Display.INVALID_DISPLAY;
 import static android.view.View.GONE;
 
 import static com.android.systemui.bubbles.BadgedImageView.DEFAULT_PATH_SIZE;
@@ -45,7 +46,7 @@
     public static final String KEY = "Overflow";
 
     private BadgedImageView mOverflowBtn;
-    private BubbleExpandedView mOverflowExpandedView;
+    private BubbleExpandedView mExpandedView;
     private LayoutInflater mInflater;
     private Context mContext;
     private Bitmap mIcon;
@@ -63,11 +64,11 @@
     }
 
     void setUpOverflow(ViewGroup parentViewGroup, BubbleStackView stackView) {
-        mOverflowExpandedView = (BubbleExpandedView) mInflater.inflate(
+        mExpandedView = (BubbleExpandedView) mInflater.inflate(
                 R.layout.bubble_expanded_view, parentViewGroup /* root */,
                 false /* attachToRoot */);
-        mOverflowExpandedView.setOverflow(true);
-        mOverflowExpandedView.setStackView(stackView);
+        mExpandedView.setOverflow(true);
+        mExpandedView.setStackView(stackView);
 
         updateIcon(mContext, parentViewGroup);
     }
@@ -124,7 +125,7 @@
 
     @Override
     public BubbleExpandedView getExpandedView() {
-        return mOverflowExpandedView;
+        return mExpandedView;
     }
 
     @Override
@@ -149,7 +150,7 @@
 
     @Override
     public void setContentVisibility(boolean visible) {
-        mOverflowExpandedView.setContentVisibility(visible);
+        mExpandedView.setContentVisibility(visible);
     }
 
     @Override
@@ -167,4 +168,9 @@
     public String getKey() {
         return BubbleOverflow.KEY;
     }
+
+    @Override
+    public int getDisplayId() {
+        return mExpandedView != null ? mExpandedView.getVirtualDisplayId() : INVALID_DISPLAY;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 8cc10d9..aedd2db 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -126,6 +126,11 @@
     @VisibleForTesting
     static final int FLYOUT_HIDE_AFTER = 5000;
 
+    private static final PhysicsAnimator.SpringConfig FLYOUT_IME_ANIMATION_SPRING_CONFIG =
+            new PhysicsAnimator.SpringConfig(
+                    StackAnimationController.IME_ANIMATION_STIFFNESS,
+                    StackAnimationController.DEFAULT_BOUNCINESS);
+
     /**
      * Interface to synchronize {@link View} state and the screen.
      *
@@ -903,14 +908,8 @@
      * The {@link Bubble} that is expanded, null if one does not exist.
      */
     @Nullable
-    Bubble getExpandedBubble() {
-        if (mExpandedBubble == null
-                || (BubbleExperimentConfig.allowBubbleOverflow(mContext)
-                    && mExpandedBubble.getIconView() == mBubbleOverflow.getBtn()
-                    && BubbleOverflow.KEY.equals(mExpandedBubble.getKey()))) {
-            return null;
-        }
-        return (Bubble) mExpandedBubble;
+    BubbleViewProvider getExpandedBubble() {
+        return mExpandedBubble;
     }
 
     // via BubbleData.Listener
@@ -1240,7 +1239,8 @@
     }
 
     /**
-     * @deprecated use {@link #setExpanded(boolean)} and {@link #setSelectedBubble(Bubble)}
+     * @deprecated use {@link #setExpanded(boolean)} and
+     * {@link BubbleData#setSelectedBubble(Bubble)}
      */
     @Deprecated
     @MainThread
@@ -1282,7 +1282,7 @@
         if (DEBUG_BUBBLE_STACK_VIEW) {
             Log.d(TAG, "animateCollapse");
             Log.d(TAG, BubbleDebugConfig.formatBubblesString(getBubblesOnScreen(),
-                    getExpandedBubble()));
+                    mExpandedBubble));
         }
         updateOverflowBtnVisibility(/* apply */ false);
         mBubbleContainer.cancelAllAnimations();
@@ -1354,8 +1354,23 @@
     public void onImeVisibilityChanged(boolean visible, int height) {
         mStackAnimationController.setImeHeight(visible ? height + mImeOffset : 0);
 
-        if (!mIsExpanded) {
-            mStackAnimationController.animateForImeVisibility(visible);
+        if (!mIsExpanded && getBubbleCount() > 0) {
+            final float stackDestinationY =
+                    mStackAnimationController.animateForImeVisibility(visible);
+
+            // How far the stack is animating due to IME, we'll just animate the flyout by that
+            // much too.
+            final float stackDy =
+                    stackDestinationY - mStackAnimationController.getStackPosition().y;
+
+            // If the flyout is visible, translate it along with the bubble stack.
+            if (mFlyout.getVisibility() == VISIBLE) {
+                PhysicsAnimator.getInstance(mFlyout)
+                        .spring(DynamicAnimation.TRANSLATION_Y,
+                                mFlyout.getTranslationY() + stackDy,
+                                FLYOUT_IME_ANIMATION_SPRING_CONFIG)
+                        .start();
+            }
         }
     }
 
@@ -1371,13 +1386,14 @@
         if (DEBUG_BUBBLE_STACK_VIEW) {
             Log.d(TAG, "onBubbleDragStart: bubble=" + bubble);
         }
-        maybeShowManageEducation(false);
         mExpandedAnimationController.prepareForBubbleDrag(bubble, mMagneticTarget);
 
         // We're dragging an individual bubble, so set the magnetized object to the magnetized
         // bubble.
         mMagnetizedObject = mExpandedAnimationController.getMagnetizedBubbleDraggingOut();
         mMagnetizedObject.setMagnetListener(mIndividualBubbleMagnetListener);
+
+        maybeShowManageEducation(false);
     }
 
     /** Called with the coordinates to which an individual bubble has been dragged. */
@@ -1841,17 +1857,16 @@
     }
 
     private void updatePointerPosition() {
-        Bubble expandedBubble = getExpandedBubble();
-        if (expandedBubble == null) {
+        if (mExpandedBubble == null) {
             return;
         }
-        int index = getBubbleIndex(expandedBubble);
+        int index = getBubbleIndex(mExpandedBubble);
         float bubbleLeftFromScreenLeft = mExpandedAnimationController.getBubbleLeft(index);
         float halfBubble = mBubbleSize / 2f;
         float bubbleCenter = bubbleLeftFromScreenLeft + halfBubble;
         // Padding might be adjusted for insets, so get it directly from the view
         bubbleCenter -= mExpandedViewContainer.getPaddingLeft();
-        expandedBubble.getExpandedView().setPointerPosition(bubbleCenter);
+        mExpandedBubble.getExpandedView().setPointerPosition(bubbleCenter);
     }
 
     /**
@@ -1873,11 +1888,10 @@
      * is between 0 and the bubble count minus 1.
      */
     int getBubbleIndex(@Nullable BubbleViewProvider provider) {
-        if (provider == null || provider.getKey() == BubbleOverflow.KEY) {
+        if (provider == null) {
             return 0;
         }
-        Bubble b = (Bubble) provider;
-        return mBubbleContainer.indexOfChild(b.getIconView());
+        return mBubbleContainer.indexOfChild(provider.getIconView());
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
index f04933a..ef84c73 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
@@ -41,4 +41,6 @@
     Path getDotPath();
 
     boolean showDot();
+
+    int getDisplayId();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index b81665c..86387f1 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -68,9 +68,10 @@
     /**
      * Values to use for the default {@link SpringForce} provided to the physics animation layout.
      */
-    private static final int DEFAULT_STIFFNESS = 12000;
+    public static final int DEFAULT_STIFFNESS = 12000;
+    public static final float IME_ANIMATION_STIFFNESS = SpringForce.STIFFNESS_LOW;
     private static final int FLING_FOLLOW_STIFFNESS = 20000;
-    private static final float DEFAULT_BOUNCINESS = 0.9f;
+    public static final float DEFAULT_BOUNCINESS = 0.9f;
 
     /**
      * Friction applied to fling animations. Since the stack must land on one of the sides of the
@@ -118,8 +119,11 @@
     /** Whether or not the stack's start position has been set. */
     private boolean mStackMovedToStartPosition = false;
 
-    /** The most recent position in which the stack was resting on the edge of the screen. */
-    @Nullable private PointF mRestingStackPosition;
+    /**
+     * The stack's most recent position along the edge of the screen. This is saved when the last
+     * bubble is removed, so that the stack can be restored in its previous position.
+     */
+    private PointF mRestingStackPosition;
 
     /** The height of the most recently visible IME. */
     private float mImeHeight = 0f;
@@ -465,7 +469,6 @@
 
                 .addEndListener((animation, canceled, endValue, endVelocity) -> {
                     if (!canceled) {
-                        mRestingStackPosition = new PointF();
                         mRestingStackPosition.set(mStackPosition);
 
                         springFirstBubbleWithStackFollowing(property, spring, endVelocity,
@@ -501,8 +504,10 @@
     /**
      * Animates the stack either away from the newly visible IME, or back to its original position
      * due to the IME going away.
+     *
+     * @return The destination Y value of the stack due to the IME movement.
      */
-    public void animateForImeVisibility(boolean imeVisible) {
+    public float animateForImeVisibility(boolean imeVisible) {
         final float maxBubbleY = getAllowableStackPositionRegion().bottom;
         float destinationY = Float.MIN_VALUE;
 
@@ -523,12 +528,14 @@
             springFirstBubbleWithStackFollowing(
                     DynamicAnimation.TRANSLATION_Y,
                     getSpringForce(DynamicAnimation.TRANSLATION_Y, /* view */ null)
-                            .setStiffness(SpringForce.STIFFNESS_LOW),
+                            .setStiffness(IME_ANIMATION_STIFFNESS),
                     /* startVel */ 0f,
                     destinationY);
 
             notifyFloatingCoordinatorStackAnimatingTo(mStackPosition.x, destinationY);
         }
+
+        return destinationY;
     }
 
     /**
@@ -583,7 +590,7 @@
                             - mBubblePaddingTop
                             - (mImeHeight > Float.MIN_VALUE ? mImeHeight + mBubblePaddingTop : 0f)
                             - Math.max(
-                            insets.getSystemWindowInsetBottom(),
+                            insets.getStableInsetBottom(),
                             insets.getDisplayCutout() != null
                                     ? insets.getDisplayCutout().getSafeInsetBottom()
                                     : 0);
@@ -853,7 +860,12 @@
     public void setStackPosition(PointF pos) {
         Log.d(TAG, String.format("Setting position to (%f, %f).", pos.x, pos.y));
         mStackPosition.set(pos.x, pos.y);
-        mRestingStackPosition = mStackPosition;
+
+        if (mRestingStackPosition == null) {
+            mRestingStackPosition = new PointF();
+        }
+
+        mRestingStackPosition.set(mStackPosition);
 
         // If we're not the active controller, we don't want to physically move the bubble views.
         if (isActiveController()) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
index 27c9e98..ac97d8a 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,
-            NotificationInterruptStateProvider interruptionStateProvider,
+            NotificationInterruptionStateProvider interruptionStateProvider,
             ZenModeController zenModeController,
             NotificationLockscreenUserManager notifUserManager,
             NotificationGroupManager groupManager,
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt
index 49a16d8..dec6007 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt
@@ -16,10 +16,12 @@
 
 package com.android.systemui.controls
 
+import android.content.ComponentName
 import android.service.controls.Control
 
 data class ControlStatus(
     val control: Control,
+    val component: ComponentName,
     var favorite: Boolean,
     val removed: Boolean = false
-)
\ No newline at end of file
+)
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/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index 5e1ed58..e5d36f9 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -127,7 +127,7 @@
     internal val settingObserver = object : ContentObserver(null) {
         override fun onChange(
             selfChange: Boolean,
-            uris: MutableIterable<Uri>,
+            uris: Collection<Uri>,
             flags: Int,
             userId: Int
         ) {
@@ -238,7 +238,11 @@
                             }
                             val removed = findRemoved(favoritesForComponentKeys.toSet(), controls)
                             val controlsWithFavorite = controls.map {
-                                ControlStatus(it, it.controlId in favoritesForComponentKeys)
+                                ControlStatus(
+                                    it,
+                                    componentName,
+                                    it.controlId in favoritesForComponentKeys
+                                )
                             }
                             val loadData = createLoadDataObject(
                                 Favorites.getControlsForComponent(componentName)
@@ -290,7 +294,7 @@
                 .setTitle(controlInfo.controlTitle)
                 .setDeviceType(controlInfo.deviceType)
                 .build()
-        return ControlStatus(control, true, setRemoved)
+        return ControlStatus(control, componentName, true, setRemoved)
     }
 
     private fun findRemoved(favoriteKeys: Set<String>, list: List<Control>): Set<String> {
@@ -489,10 +493,12 @@
                 updatedStructure
             } else { s }
 
-            structures.add(newStructure)
+            if (!newStructure.controls.isEmpty()) {
+                structures.add(newStructure)
+            }
         }
 
-        if (!replaced) {
+        if (!replaced && !updatedStructure.controls.isEmpty()) {
             structures.add(updatedStructure)
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
index 179e9fb..563c2f6 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
@@ -16,8 +16,8 @@
 
 package com.android.systemui.controls.management
 
+import android.content.ComponentName
 import android.graphics.Rect
-import android.graphics.drawable.Icon
 import android.service.controls.DeviceTypes
 import android.view.LayoutInflater
 import android.view.View
@@ -147,7 +147,7 @@
     override fun bindData(wrapper: ElementWrapper) {
         wrapper as ControlWrapper
         val data = wrapper.controlStatus
-        val renderInfo = getRenderInfo(data.control.deviceType)
+        val renderInfo = getRenderInfo(data.component, data.control.deviceType)
         title.text = data.control.title
         subtitle.text = data.control.subtitle
         favorite.isChecked = data.favorite
@@ -160,16 +160,17 @@
     }
 
     private fun getRenderInfo(
+        component: ComponentName,
         @DeviceTypes.DeviceType deviceType: Int
     ): RenderInfo {
-        return RenderInfo.lookup(deviceType, true)
+        return RenderInfo.lookup(itemView.context, component, deviceType, true)
     }
 
     private fun applyRenderInfo(ri: RenderInfo) {
         val context = itemView.context
         val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme())
 
-        icon.setImageIcon(Icon.createWithResource(context, ri.iconResourceId))
+        icon.setImageDrawable(ri.icon)
         icon.setImageTintList(fg)
     }
 }
@@ -191,4 +192,4 @@
             right = sideMargins
         }
     }
-}
\ No newline at end of file
+}
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..502354a 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,8 @@
     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 var listOfStructures = emptyList<StructureContainer>()
 
     private lateinit var comparator: Comparator<StructureContainer>
@@ -172,9 +180,48 @@
             layoutResource = R.layout.controls_management_favorites
             inflate()
         }
-
         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 +231,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()
     }
 
@@ -207,11 +260,25 @@
         }
     }
 
+    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)
         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/ControlsListingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
index 9b108cf..94487e5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
@@ -88,6 +88,8 @@
 
     init {
         serviceListing.addCallback(serviceListingCallback)
+        serviceListing.setListening(true)
+        serviceListing.reload()
     }
 
     override fun changeUser(newUser: UserHandle) {
@@ -95,11 +97,12 @@
             callbacks.clear()
             availableServices = emptyList()
             serviceListing.setListening(false)
-            serviceListing.removeCallback(serviceListingCallback)
             currentUserId = newUser.identifier
             val contextForUser = context.createContextAsUser(newUser, 0)
             serviceListing = serviceListingBuilder(contextForUser)
             serviceListing.addCallback(serviceListingCallback)
+            serviceListing.setListening(true)
+            serviceListing.reload()
         }
     }
 
@@ -118,12 +121,7 @@
         backgroundExecutor.execute {
             Log.d(TAG, "Subscribing callback")
             callbacks.add(listener)
-            if (callbacks.size == 1) {
-                serviceListing.setListening(true)
-                serviceListing.reload()
-            } else {
-                listener.onServicesUpdated(getCurrentServices())
-            }
+            listener.onServicesUpdated(getCurrentServices())
         }
     }
 
@@ -136,9 +134,6 @@
         backgroundExecutor.execute {
             Log.d(TAG, "Unsubscribing callback")
             callbacks.remove(listener)
-            if (callbacks.size == 0) {
-                serviceListing.setListening(false)
-            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt
index 463632b..a7fc2ac8 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt
@@ -21,7 +21,6 @@
 import android.content.ComponentName
 import android.content.DialogInterface
 import android.content.Intent
-import android.graphics.drawable.Icon
 import android.os.Bundle
 import android.os.UserHandle
 import android.service.controls.Control
@@ -137,11 +136,10 @@
     }
 
     fun createDialog(label: CharSequence): Dialog {
-
-        val renderInfo = RenderInfo.lookup(control.deviceType, true)
+        val renderInfo = RenderInfo.lookup(this, component, control.deviceType, true)
         val frame = LayoutInflater.from(this).inflate(R.layout.controls_dialog, null).apply {
             requireViewById<ImageView>(R.id.icon).apply {
-                setImageIcon(Icon.createWithResource(context, renderInfo.iconResourceId))
+                setImageDrawable(renderInfo.icon)
                 setImageTintList(
                         context.resources.getColorStateList(renderInfo.foreground, context.theme))
             }
@@ -176,4 +174,4 @@
         }
         finish()
     }
-}
\ No newline at end of file
+}
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/ControlActionCoordinator.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
index eb84a8b..680d006 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
@@ -20,6 +20,7 @@
 import android.content.Intent
 import android.provider.Settings
 import android.service.controls.actions.BooleanAction
+import android.service.controls.actions.CommandAction
 import android.util.Log
 import android.view.HapticFeedbackConstants
 
@@ -36,6 +37,10 @@
         cvh.clipLayer.setLevel(nextLevel)
     }
 
+    fun touch(cvh: ControlViewHolder, templateId: String) {
+        cvh.action(CommandAction(templateId))
+    }
+
     fun longPress(cvh: ControlViewHolder) {
         // Long press snould only be called when there is valid control state, otherwise ignore
         cvh.cws.control?.let {
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 b1b98bc..fc5663f 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -19,11 +19,11 @@
 import android.content.Context
 import android.graphics.BlendMode
 import android.graphics.drawable.ClipDrawable
-import android.graphics.drawable.Icon
 import android.graphics.drawable.LayerDrawable
 import android.service.controls.Control
 import android.service.controls.actions.ControlAction
 import android.service.controls.templates.ControlTemplate
+import android.service.controls.templates.StatelessTemplate
 import android.service.controls.templates.TemperatureControlTemplate
 import android.service.controls.templates.ToggleRangeTemplate
 import android.service.controls.templates.ToggleTemplate
@@ -122,19 +122,25 @@
         return when {
             status == Control.STATUS_UNKNOWN -> UnknownBehavior::class
             template is ToggleTemplate -> ToggleBehavior::class
+            template is StatelessTemplate -> TouchBehavior::class
             template is ToggleRangeTemplate -> ToggleRangeBehavior::class
             template is TemperatureControlTemplate -> TemperatureControlBehavior::class
             else -> DefaultBehavior::class
         }
     }
 
-    internal fun applyRenderInfo(ri: RenderInfo) {
+    internal fun applyRenderInfo(enabled: Boolean, offset: Int = 0) {
+        setEnabled(enabled)
+
+        val deviceType = cws.control?.let { it.getDeviceType() } ?: cws.ci.deviceType
+        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())
         status.setTextColor(fg)
         statusExtra.setTextColor(fg)
 
-        icon.setImageIcon(Icon.createWithResource(context, ri.iconResourceId))
+        icon.setImageDrawable(ri.icon)
         icon.setImageTintList(fg)
 
         clipLayer.getDrawable().apply {
@@ -143,7 +149,7 @@
         }
     }
 
-    fun setEnabled(enabled: Boolean) {
+    private fun setEnabled(enabled: Boolean) {
         status.setEnabled(enabled)
         icon.setEnabled(enabled)
     }
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 ca6619b..bde966c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -16,19 +16,14 @@
 
 package com.android.systemui.controls.ui
 
-import android.accounts.Account
-import android.accounts.AccountManager
 import android.app.Dialog
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
-import android.content.ServiceConnection
 import android.content.SharedPreferences
 import android.graphics.drawable.Drawable
 import android.graphics.drawable.LayerDrawable
-import android.os.IBinder
 import android.service.controls.Control
-import android.service.controls.TokenProvider
 import android.service.controls.actions.ControlAction
 import android.util.Log
 import android.view.ContextThemeWrapper
@@ -61,68 +56,6 @@
 import javax.inject.Inject
 import javax.inject.Singleton
 
-// TEMP CODE for MOCK
-private const val TOKEN = "https://www.googleapis.com/auth/assistant"
-private const val SCOPE = "oauth2:" + TOKEN
-private var tokenProviderConnection: TokenProviderConnection? = null
-class TokenProviderConnection(
-    val cc: ControlsController,
-    val context: Context,
-    val structure: StructureInfo?
-) : ServiceConnection {
-    private var mTokenProvider: TokenProvider? = null
-
-    override fun onServiceConnected(cName: ComponentName, binder: IBinder) {
-        Thread({
-            Log.i(ControlsUiController.TAG, "TokenProviderConnection connected")
-            mTokenProvider = TokenProvider.Stub.asInterface(binder)
-
-            val mLastAccountName = mTokenProvider?.getAccountName()
-
-            if (mLastAccountName == null || mLastAccountName.isEmpty()) {
-                Log.e(ControlsUiController.TAG, "NO ACCOUNT IS SET. Open HomeMock app")
-            } else {
-                mTokenProvider?.setAuthToken(getAuthToken(mLastAccountName))
-                structure?.let {
-                    cc.subscribeToFavorites(it)
-                }
-            }
-        }, "TokenProviderThread").start()
-    }
-
-    override fun onServiceDisconnected(cName: ComponentName) {
-        mTokenProvider = null
-    }
-
-    fun getAuthToken(accountName: String): String? {
-        val am = AccountManager.get(context)
-        val accounts = am.getAccountsByType("com.google")
-        if (accounts == null || accounts.size == 0) {
-            Log.w(ControlsUiController.TAG, "No com.google accounts found")
-            return null
-        }
-
-        var account: Account? = null
-        for (a in accounts) {
-            if (a.name.equals(accountName)) {
-                account = a
-                break
-            }
-        }
-
-        if (account == null) {
-            account = accounts[0]
-        }
-
-        try {
-            return am.blockingGetAuthToken(account!!, SCOPE, true)
-        } catch (e: Throwable) {
-            Log.e(ControlsUiController.TAG, "Error getting auth token", e)
-            return null
-        }
-    }
-}
-
 private data class ControlKey(val componentName: ComponentName, val controlId: String)
 
 @Singleton
@@ -215,21 +148,10 @@
                 ControlKey(selectedStructure.componentName, it.ci.controlId)
             }
             listingCallback = createCallback(::showControlsView)
+            controlsController.get().subscribeToFavorites(selectedStructure)
         }
 
         controlsListingController.get().addCallback(listingCallback)
-
-        // Temp code to pass auth
-        tokenProviderConnection = TokenProviderConnection(controlsController.get(), context,
-                selectedStructure)
-
-        val serviceIntent = Intent()
-        serviceIntent.setComponent(ComponentName("com.android.systemui.home.mock",
-                "com.android.systemui.home.mock.AuthService"))
-        if (!context.bindService(serviceIntent, tokenProviderConnection!!,
-                Context.BIND_AUTO_CREATE)) {
-            controlsController.get().subscribeToFavorites(selectedStructure)
-        }
     }
 
     private fun showInitialSetupView(items: List<SelectionItem>) {
@@ -266,36 +188,23 @@
         parent.removeAllViews()
         controlViewsById.clear()
 
-        val inflater = LayoutInflater.from(context)
-        inflater.inflate(R.layout.controls_with_favorites, parent, true)
+        createListView()
+        createDropDown(items)
+    }
 
-        val listView = parent.requireViewById(R.id.global_actions_controls_list) as ViewGroup
-        var lastRow: ViewGroup = createRow(inflater, listView)
-        selectedStructure.controls.forEach {
-            if (lastRow.getChildCount() == 2) {
-                lastRow = createRow(inflater, listView)
-            }
-            val item = inflater.inflate(
-                R.layout.controls_base_item, lastRow, false) as ViewGroup
-            lastRow.addView(item)
-            val cvh = ControlViewHolder(item, controlsController.get(), uiExecutor, bgExecutor)
-            val key = ControlKey(selectedStructure.componentName, it.controlId)
-            cvh.bindData(controlsById.getValue(key))
-            controlViewsById.put(key, cvh)
-        }
-
-        // add spacer if necessary to keep control size consistent
-        if ((selectedStructure.controls.size % 2) == 1) {
-            lastRow.addView(Space(context), LinearLayout.LayoutParams(0, 0, 1f))
+    private fun createDropDown(items: List<SelectionItem>) {
+        items.forEach {
+            RenderInfo.registerComponentIcon(it.componentName, it.icon)
         }
 
         val itemsByComponent = items.associateBy { it.componentName }
-        var adapter = ItemAdapter(context, R.layout.controls_spinner_item).apply {
-            val listItems = allStructures.mapNotNull {
-                itemsByComponent.get(it.componentName)?.copy(structure = it.structure)
-            }
+        val itemsWithStructure = allStructures.mapNotNull {
+            itemsByComponent.get(it.componentName)?.copy(structure = it.structure)
+        }
+        val selectionItem = findSelectionItem(selectedStructure, itemsWithStructure) ?: items[0]
 
-            addAll(listItems + addControlsItem)
+        var adapter = ItemAdapter(context, R.layout.controls_spinner_item).apply {
+            addAll(itemsWithStructure + addControlsItem)
         }
 
         /*
@@ -303,16 +212,15 @@
          * for this dialog. Use a textView with the ListPopupWindow to achieve
          * a similar effect
          */
-        val item = adapter.findSelectionItem(selectedStructure) ?: adapter.getItem(0)
         parent.requireViewById<TextView>(R.id.app_or_structure_spinner).apply {
-            setText(item.getTitle())
+            setText(selectionItem.getTitle())
             // override the default color on the dropdown drawable
             (getBackground() as LayerDrawable).getDrawable(1)
                 .setTint(context.resources.getColor(R.color.control_spinner_dropdown, null))
         }
         parent.requireViewById<ImageView>(R.id.app_icon).apply {
-            setContentDescription(item.getTitle())
-            setImageDrawable(item.icon)
+            setContentDescription(selectionItem.getTitle())
+            setImageDrawable(selectionItem.icon)
         }
         val anchor = parent.requireViewById<ViewGroup>(R.id.controls_header)
         anchor.setOnClickListener(object : View.OnClickListener {
@@ -350,6 +258,36 @@
         })
     }
 
+    private fun createListView() {
+        val inflater = LayoutInflater.from(context)
+        inflater.inflate(R.layout.controls_with_favorites, parent, true)
+
+        val listView = parent.requireViewById(R.id.global_actions_controls_list) as ViewGroup
+        var lastRow: ViewGroup = createRow(inflater, listView)
+        selectedStructure.controls.forEach {
+            if (lastRow.getChildCount() == 2) {
+                lastRow = createRow(inflater, listView)
+            }
+            val baseLayout = inflater.inflate(
+                R.layout.controls_base_item, lastRow, false) as ViewGroup
+            lastRow.addView(baseLayout)
+            val cvh = ControlViewHolder(
+                baseLayout,
+                controlsController.get(),
+                uiExecutor,
+                bgExecutor
+            )
+            val key = ControlKey(selectedStructure.componentName, it.controlId)
+            cvh.bindData(controlsById.getValue(key))
+            controlViewsById.put(key, cvh)
+        }
+
+        // add spacer if necessary to keep control size consistent
+        if ((selectedStructure.controls.size % 2) == 1) {
+            lastRow.addView(Space(context), LinearLayout.LayoutParams(0, 0, 1f))
+        }
+    }
+
     private fun loadPreference(structures: List<StructureInfo>): StructureInfo {
         if (structures.isEmpty()) return EMPTY_STRUCTURE
 
@@ -393,13 +331,13 @@
         activeDialog?.dismiss()
 
         controlsController.get().unsubscribe()
-        context.unbindService(tokenProviderConnection)
-        tokenProviderConnection = null
 
         parent.removeAllViews()
         controlsById.clear()
         controlViewsById.clear()
         controlsListingController.get().removeCallback(listingCallback)
+
+        RenderInfo.clearCache()
     }
 
     override fun onRefreshState(componentName: ComponentName, controls: List<Control>) {
@@ -438,6 +376,11 @@
         listView.addView(row)
         return row
     }
+
+    private fun findSelectionItem(si: StructureInfo, items: List<SelectionItem>): SelectionItem? =
+        items.firstOrNull {
+            it.componentName == si.componentName && it.structure == si.structure
+        }
 }
 
 private data class SelectionItem(
@@ -468,17 +411,4 @@
         }
         return view
     }
-
-    fun findSelectionItem(si: StructureInfo): SelectionItem? {
-        var i = 0
-        while (i < getCount()) {
-            val item = getItem(i)
-            if (item.componentName == si.componentName &&
-                item.structure == si.structure) {
-                return item
-            }
-            i++
-        }
-        return null
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt
index 1747929..e850a6a 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt
@@ -25,7 +25,6 @@
 
     override fun bind(cws: ControlWithState) {
         cvh.status.setText(cws.control?.getStatusText() ?: "")
-        cvh.setEnabled(false)
-        cvh.applyRenderInfo(RenderInfo.lookup(cws.ci.deviceType, false))
+        cvh.applyRenderInfo(false)
     }
 }
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 da52c6f..56267be 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
@@ -16,8 +16,14 @@
 
 package com.android.systemui.controls.ui
 
+import android.annotation.MainThread
+import android.content.ComponentName
+import android.content.Context
+import android.graphics.drawable.Drawable
 import android.service.controls.DeviceTypes
 import android.service.controls.templates.TemperatureControlTemplate
+import android.util.ArrayMap
+import android.util.SparseArray
 
 import com.android.systemui.R
 
@@ -31,18 +37,54 @@
     }
 }
 
-data class RenderInfo(val iconResourceId: Int, val foreground: Int, val background: Int) {
+data class RenderInfo(val icon: Drawable, val foreground: Int, val background: Int) {
 
     companion object {
-        fun lookup(deviceType: Int, enabled: Boolean): RenderInfo {
-            val iconState = deviceIconMap.getValue(deviceType)
+        const val APP_ICON_ID = -1
+        private val iconMap = SparseArray<Drawable>()
+        private val appIconMap = ArrayMap<ComponentName, Drawable>()
+
+        @MainThread
+        fun lookup(
+            context: Context,
+            componentName: ComponentName,
+            deviceType: Int,
+            enabled: Boolean,
+            offset: Int = 0
+        ): RenderInfo {
             val (fg, bg) = deviceColorMap.getValue(deviceType)
-            return RenderInfo(iconState[enabled], fg, bg)
+
+            val iconKey = if (offset > 0) {
+                deviceType * BUCKET_SIZE + offset
+            } else deviceType
+
+            val iconState = deviceIconMap.getValue(iconKey)
+            val resourceId = iconState[enabled]
+            var icon: Drawable? = null
+            if (resourceId == APP_ICON_ID) {
+                icon = appIconMap.get(componentName)
+                if (icon == null) {
+                    icon = context.resources
+                        .getDrawable(R.drawable.ic_device_unknown_gm2_24px, null)
+                    appIconMap.put(componentName, icon)
+                }
+            } else {
+                icon = iconMap.get(resourceId)
+                if (icon == null) {
+                    icon = context.resources.getDrawable(resourceId, null)
+                    iconMap.put(resourceId, icon)
+                }
+            }
+            return RenderInfo(icon!!, fg, bg)
         }
 
-        fun lookup(deviceType: Int, offset: Int, enabled: Boolean): RenderInfo {
-            val key = deviceType * BUCKET_SIZE + offset
-            return lookup(key, enabled)
+        fun registerComponentIcon(componentName: ComponentName, icon: Drawable) {
+            appIconMap.put(componentName, icon)
+        }
+
+        fun clearCache() {
+            iconMap.clear()
+            appIconMap.clear()
         }
     }
 }
@@ -116,6 +158,10 @@
     DeviceTypes.TYPE_MOP to IconState(
         R.drawable.ic_vacuum_gm2_24px,
         R.drawable.ic_vacuum_gm2_24px
+    ),
+    DeviceTypes.TYPE_ROUTINE to IconState(
+        RenderInfo.APP_ICON_ID,
+        RenderInfo.APP_ICON_ID
     )
 ).withDefault {
     IconState(
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
index 239d2e5..15c1dab 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
@@ -47,10 +47,7 @@
 
         val activeMode = template.getCurrentActiveMode()
         val enabled = activeMode != 0 && activeMode != TemperatureControlTemplate.MODE_OFF
-        val deviceType = control.getDeviceType()
-
         clipLayer.setLevel(if (enabled) MAX_LEVEL else MIN_LEVEL)
-        cvh.setEnabled(enabled)
-        cvh.applyRenderInfo(RenderInfo.lookup(deviceType, activeMode, enabled))
+        cvh.applyRenderInfo(enabled, activeMode)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
index d306d7c..a3368ef 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
@@ -34,7 +34,7 @@
 
     override fun initialize(cvh: ControlViewHolder) {
         this.cvh = cvh
-        cvh.setEnabled(false)
+        cvh.applyRenderInfo(false)
 
         cvh.layout.setOnClickListener(View.OnClickListener() {
             ControlActionCoordinator.toggle(cvh, template.getTemplateId(), template.isChecked())
@@ -51,10 +51,7 @@
         clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
 
         val checked = template.isChecked()
-        val deviceType = control.getDeviceType()
-
         clipLayer.setLevel(if (checked) MAX_LEVEL else MIN_LEVEL)
-        cvh.setEnabled(checked)
-        cvh.applyRenderInfo(RenderInfo.lookup(deviceType, checked))
+        cvh.applyRenderInfo(checked)
     }
 }
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 cca56c2..6595b55 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
@@ -56,7 +56,7 @@
         status = cvh.status
         context = status.getContext()
 
-        cvh.setEnabled(false)
+        cvh.applyRenderInfo(false)
 
         val gestureListener = ToggleRangeGestureListener(cvh.layout)
         val gestureDetector = GestureDetector(context, gestureListener)
@@ -89,14 +89,11 @@
         rangeTemplate = template.getRange()
 
         val checked = template.isChecked()
-        val deviceType = control.getDeviceType()
-
         val currentRatio = rangeTemplate.getCurrentValue() /
                 (rangeTemplate.getMaxValue() - rangeTemplate.getMinValue())
         updateRange(currentRatio, checked)
 
-        cvh.setEnabled(checked)
-        cvh.applyRenderInfo(RenderInfo.lookup(deviceType, checked))
+        cvh.applyRenderInfo(checked)
     }
 
     fun beginUpdateRange() {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt
new file mode 100644
index 0000000..d64a5f0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.ui
+
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.LayerDrawable
+import android.view.View
+import android.service.controls.Control
+import android.service.controls.templates.StatelessTemplate
+
+import com.android.systemui.R
+import com.android.systemui.controls.ui.ControlActionCoordinator.MIN_LEVEL
+
+/**
+ * Supports touch events, but has no notion of state as the {@link ToggleBehavior} does. Must be
+ * used with {@link StatelessTemplate}.
+ */
+class TouchBehavior : Behavior {
+    lateinit var clipLayer: Drawable
+    lateinit var template: StatelessTemplate
+    lateinit var control: Control
+    lateinit var cvh: ControlViewHolder
+
+    override fun initialize(cvh: ControlViewHolder) {
+        this.cvh = cvh
+        cvh.applyRenderInfo(false)
+
+        cvh.layout.setOnClickListener(View.OnClickListener() {
+            ControlActionCoordinator.touch(cvh, template.getTemplateId())
+        })
+    }
+
+    override fun bind(cws: ControlWithState) {
+        this.control = cws.control!!
+        cvh.status.setText(control.getStatusText())
+        template = control.getControlTemplate() as StatelessTemplate
+
+        val ld = cvh.layout.getBackground() as LayerDrawable
+        clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
+        clipLayer.setLevel(MIN_LEVEL)
+
+        cvh.applyRenderInfo(false)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt
index 1f33b85..c357249 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt
@@ -25,7 +25,6 @@
 
     override fun bind(cws: ControlWithState) {
         cvh.status.setText(cvh.context.getString(com.android.internal.R.string.loading))
-        cvh.setEnabled(false)
-        cvh.applyRenderInfo(RenderInfo.lookup(cws.ci.deviceType, false))
+        cvh.applyRenderInfo(false)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
index 2877ed0..5b3d5c5 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
@@ -44,8 +44,6 @@
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
 import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.BatteryControllerImpl;
 import com.android.systemui.statusbar.policy.BluetoothController;
 import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
 import com.android.systemui.statusbar.policy.CastController;
@@ -179,12 +177,6 @@
     /**
      */
     @Binds
-    public abstract BatteryController provideBatteryController(
-            BatteryControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
     public abstract ManagedProfileController provideManagedProfileController(
             ManagedProfileControllerImpl controllerImpl);
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 3a4b273..6c502d2 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 provideAmbientDisplayConfiguration(Context context) {
+    public AmbientDisplayConfiguration provideAmbientDispalyConfiguration(Context context) {
         return new AmbientDisplayConfiguration(context);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index b4e5125..956b4aa 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -43,6 +43,8 @@
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.ShadeControllerImpl;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.BatteryControllerImpl;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
@@ -78,6 +80,11 @@
             NotificationLockscreenUserManagerImpl notificationLockscreenUserManager);
 
     @Binds
+    @Singleton
+    public abstract BatteryController provideBatteryController(
+            BatteryControllerImpl controllerImpl);
+
+    @Binds
     abstract DockManager bindDockManager(DockManagerImpl dockManager);
 
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index c28a719..700a861 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -49,6 +49,7 @@
 import com.android.systemui.util.wakelock.WakeLock;
 
 import java.io.PrintWriter;
+import java.util.Collection;
 import java.util.List;
 import java.util.function.Consumer;
 
@@ -261,7 +262,7 @@
 
     private final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
         @Override
-        public void onChange(boolean selfChange, Iterable<Uri> uris, int flags, int userId) {
+        public void onChange(boolean selfChange, Collection<Uri> uris, int flags, int userId) {
             if (userId != ActivityManager.getCurrentUser()) {
                 return;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 24f505d..786ad2c 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -1574,7 +1574,6 @@
 
         private ControlsUiController mControlsUiController;
         private ViewGroup mControlsView;
-        private ViewGroup mContainerView;
 
         ActionsDialog(Context context, MyAdapter adapter,
                 GlobalActionsPanelPlugin.PanelViewController plugin, BlurUtils blurUtils,
@@ -1671,7 +1670,6 @@
             mControlsView = findViewById(com.android.systemui.R.id.global_actions_controls);
             mGlobalActionsLayout = findViewById(com.android.systemui.R.id.global_actions_view);
             mGlobalActionsLayout.setOutsideTouchListener(view -> dismiss());
-            ((View) mGlobalActionsLayout.getParent()).setOnClickListener(view -> dismiss());
             mGlobalActionsLayout.setListViewAccessibilityDelegate(new View.AccessibilityDelegate() {
                 @Override
                 public boolean dispatchPopulateAccessibilityEvent(
@@ -1684,6 +1682,15 @@
             mGlobalActionsLayout.setRotationListener(this::onRotate);
             mGlobalActionsLayout.setAdapter(mAdapter);
 
+            View globalActionsParent = (View) mGlobalActionsLayout.getParent();
+            globalActionsParent.setOnClickListener(v -> dismiss());
+
+            // add fall-through dismiss handling to root view
+            View rootView = findViewById(com.android.systemui.R.id.global_actions_grid_root);
+            if (rootView != null) {
+                rootView.setOnClickListener(v -> dismiss());
+            }
+
             if (shouldUsePanel()) {
                 initializePanel();
             }
@@ -1692,14 +1699,6 @@
                 mScrimAlpha = ScrimController.BUSY_SCRIM_ALPHA;
             }
             getWindow().setBackgroundDrawable(mBackgroundDrawable);
-
-            if (mControlsView != null) {
-                mContainerView = findViewById(com.android.systemui.R.id.global_actions_container);
-                mContainerView.setOnTouchListener((v, e) -> {
-                    dismiss();
-                    return true;
-                });
-            }
         }
 
         private void fixNavBarClipping() {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/MinHeightScrollView.java b/packages/SystemUI/src/com/android/systemui/globalactions/MinHeightScrollView.java
new file mode 100644
index 0000000..622fa65
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/MinHeightScrollView.java
@@ -0,0 +1,43 @@
+/*
+ * 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.globalactions;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ScrollView;
+
+/**
+ * When measured, this view sets the minimum height of its first child to be equal to its own
+ * target height.
+ *
+ * This ensures fall-through click handlers can be placed on this view's child component.
+ */
+public class MinHeightScrollView extends ScrollView {
+    public MinHeightScrollView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        View firstChild = getChildAt(0);
+        if (firstChild != null) {
+            firstChild.setMinimumHeight(MeasureSpec.getSize(heightMeasureSpec));
+        }
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 3933af0..00b977e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -33,10 +33,10 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Log;
-import android.view.DisplayInfo;
 import android.view.ITaskOrganizer;
 import android.view.IWindowContainer;
 import android.view.SurfaceControl;
@@ -47,7 +47,9 @@
 import com.android.systemui.pip.phone.PipUpdateThread;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.function.Consumer;
 
@@ -76,9 +78,9 @@
     private final PipBoundsHandler mPipBoundsHandler;
     private final PipAnimationController mPipAnimationController;
     private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
-    private final Rect mDisplayBounds = new Rect();
     private final Rect mLastReportedBounds = new Rect();
     private final int mCornerRadius;
+    private final Map<IBinder, Rect> mBoundsToRestore = new HashMap<>();
 
     // These callbacks are called on the update thread
     private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
@@ -201,29 +203,6 @@
     }
 
     /**
-     * Updates the display dimension with given {@link DisplayInfo}
-     */
-    @SuppressWarnings("unchecked")
-    public void onDisplayInfoChanged(DisplayInfo displayInfo) {
-        final Rect newDisplayBounds = new Rect(0, 0,
-                displayInfo.logicalWidth, displayInfo.logicalHeight);
-        if (!mDisplayBounds.equals(newDisplayBounds)) {
-            // Updates the exiting PiP animation in case the screen rotation changes in the middle.
-            // It's a legit case that PiP window is in portrait mode on home screen and
-            // the application requests landscape once back to fullscreen mode.
-            final PipAnimationController.PipTransitionAnimator animator =
-                    mPipAnimationController.getCurrentAnimator();
-            if (animator != null
-                    && animator.getAnimationType() == ANIM_TYPE_BOUNDS
-                    && animator.getDestinationBounds().equals(mDisplayBounds)) {
-                animator.updateEndValue(newDisplayBounds);
-                animator.setDestinationBounds(newDisplayBounds);
-            }
-        }
-        mDisplayBounds.set(newDisplayBounds);
-    }
-
-    /**
      * Callback to issue the final {@link WindowContainerTransaction} on end of movements.
      * @param destinationBounds the final bounds.
      */
@@ -252,8 +231,9 @@
         } catch (RemoteException e) {
             throw new RuntimeException("Unable to get leash", e);
         }
+        final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
+        mBoundsToRestore.put(mToken.asBinder(), currentBounds);
         if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
-            final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
             scheduleAnimateResizePip(currentBounds, destinationBounds,
                     TRANSITION_DIRECTION_TO_PIP, DURATION_DEFAULT_MS, null);
         } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
@@ -271,13 +251,15 @@
     }
 
     @Override
-    public void taskVanished(IWindowContainer token) {
+    public void taskVanished(ActivityManager.RunningTaskInfo info) {
+        IWindowContainer token = info.token;
         Objects.requireNonNull(token, "Requires valid IWindowContainer");
         if (token.asBinder() != mToken.asBinder()) {
             Log.wtf(TAG, "Unrecognized token: " + token);
             return;
         }
-        scheduleAnimateResizePip(mLastReportedBounds, mDisplayBounds,
+        final Rect boundsToRestore = mBoundsToRestore.remove(mToken.asBinder());
+        scheduleAnimateResizePip(mLastReportedBounds, boundsToRestore,
                 TRANSITION_DIRECTION_TO_FULLSCREEN, DURATION_DEFAULT_MS, null);
         mInPip = false;
     }
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 4b97d13..1fdf92e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -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 */);
         }
     };
@@ -185,10 +190,7 @@
 
         @Override
         public void onDisplayInfoChanged(DisplayInfo displayInfo) {
-            mHandler.post(() -> {
-                mPipBoundsHandler.onDisplayInfoChanged(displayInfo);
-                mPipTaskOrganizer.onDisplayInfoChanged(displayInfo);
-            });
+            mHandler.post(() -> mPipBoundsHandler.onDisplayInfoChanged(displayInfo));
         }
 
         @Override
@@ -352,7 +354,6 @@
         mTouchHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
                 animatingBounds, fromImeAdjustment, fromShelfAdjustment,
                 mTmpDisplayInfo.rotation);
-        mPipTaskOrganizer.onDisplayInfoChanged(mTmpDisplayInfo);
     }
 
     public void dump(PrintWriter pw) {
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..0c5a4d7 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -680,7 +680,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/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 c6eecf26..fb68153 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -129,217 +129,213 @@
                 }
             };
 
-    private DisplayImeController.ImePositionProcessor mImePositionProcessor =
-            new DisplayImeController.ImePositionProcessor() {
-                /**
-                 * These are the y positions of the top of the IME surface when it is hidden and
-                 * when it is shown respectively. These are NOT necessarily the top of the visible
-                 * IME itself.
-                 */
-                private int mHiddenTop = 0;
-                private int mShownTop = 0;
+    private class DividerImeController implements DisplayImeController.ImePositionProcessor {
+        /**
+         * These are the y positions of the top of the IME surface when it is hidden and when it is
+         * shown respectively. These are NOT necessarily the top of the visible IME itself.
+         */
+        private int mHiddenTop = 0;
+        private int mShownTop = 0;
 
-                // The following are target states (what we are curretly animating towards).
-                /**
-                 * {@code true} if, at the end of the animation, the split task positions should be
-                 * adjusted by height of the IME. This happens when the secondary split is the IME
-                 * target.
-                 */
-                private boolean mTargetAdjusted = false;
-                /**
-                 * {@code true} if, at the end of the animation, the IME should be shown/visible
-                 * regardless of what has focus.
-                 */
-                private boolean mTargetShown = false;
+        // The following are target states (what we are curretly animating towards).
+        /**
+         * {@code true} if, at the end of the animation, the split task positions should be
+         * adjusted by height of the IME. This happens when the secondary split is the IME target.
+         */
+        private boolean mTargetAdjusted = false;
+        /**
+         * {@code true} if, at the end of the animation, the IME should be shown/visible
+         * regardless of what has focus.
+         */
+        private boolean mTargetShown = false;
 
-                // The following are the current (most recent) states set during animation
-                /**
-                 * {@code true} if the secondary split has IME focus.
-                 */
-                private boolean mSecondaryHasFocus = false;
-                /** The dimming currently applied to the primary/secondary splits. */
-                private float mLastPrimaryDim = 0.f;
-                private float mLastSecondaryDim = 0.f;
-                /** The most recent y position of the top of the IME surface */
-                private int mLastAdjustTop = -1;
+        // The following are the current (most recent) states set during animation
+        /** {@code true} if the secondary split has IME focus. */
+        private boolean mSecondaryHasFocus = false;
+        /** The dimming currently applied to the primary/secondary splits. */
+        private float mLastPrimaryDim = 0.f;
+        private float mLastSecondaryDim = 0.f;
+        /** The most recent y position of the top of the IME surface */
+        private int mLastAdjustTop = -1;
 
-                // The following are states reached last time an animation fully completed.
-                /** {@code true} if the IME was shown/visible by the last-completed animation. */
-                private boolean mImeWasShown = false;
-                /**
-                 * {@code true} if the split positions were adjusted by the last-completed
-                 * animation.
-                 */
-                private boolean mAdjusted = false;
+        // The following are states reached last time an animation fully completed.
+        /** {@code true} if the IME was shown/visible by the last-completed animation. */
+        private boolean mImeWasShown = false;
+        /** {@code true} if the split positions were adjusted by the last-completed animation. */
+        private boolean mAdjusted = false;
 
-                /**
-                 * When some aspect of split-screen needs to animate independent from the IME,
-                 * this will be non-null and control split animation.
-                 */
-                @Nullable
-                private ValueAnimator mAnimation = null;
+        /**
+         * When some aspect of split-screen needs to animate independent from the IME,
+         * this will be non-null and control split animation.
+         */
+        @Nullable
+        private ValueAnimator mAnimation = null;
 
-                private boolean getSecondaryHasFocus(int displayId) {
-                    try {
-                        IWindowContainer imeSplit = ActivityTaskManager.getTaskOrganizerController()
-                                .getImeTarget(displayId);
-                        return imeSplit != null
-                                && (imeSplit.asBinder() == mSplits.mSecondary.token.asBinder());
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, "Failed to get IME target", e);
-                    }
-                    return false;
-                }
+        private boolean getSecondaryHasFocus(int displayId) {
+            try {
+                IWindowContainer imeSplit = ActivityTaskManager.getTaskOrganizerController()
+                        .getImeTarget(displayId);
+                return imeSplit != null
+                        && (imeSplit.asBinder() == mSplits.mSecondary.token.asBinder());
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to get IME target", e);
+            }
+            return false;
+        }
 
+        @Override
+        public void onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
+                boolean imeShouldShow, SurfaceControl.Transaction t) {
+            mSecondaryHasFocus = getSecondaryHasFocus(displayId);
+            mTargetAdjusted = imeShouldShow && mSecondaryHasFocus
+                    && !mSplitLayout.mDisplayLayout.isLandscape();
+            mHiddenTop = hiddenTop;
+            mShownTop = shownTop;
+            mTargetShown = imeShouldShow;
+            if (mLastAdjustTop < 0) {
+                mLastAdjustTop = imeShouldShow ? hiddenTop : shownTop;
+            }
+            if (mAnimation != null || (mImeWasShown && imeShouldShow
+                    && mTargetAdjusted != mAdjusted)) {
+                // We need to animate adjustment independently of the IME position, so
+                // start our own animation to drive adjustment. This happens when a
+                // different split's editor has gained focus while the IME is still visible.
+                startAsyncAnimation();
+            }
+            updateImeAdjustState();
+        }
+
+        private void updateImeAdjustState() {
+            // Reposition the server's secondary split position so that it evaluates
+            // insets properly.
+            WindowContainerTransaction wct = new WindowContainerTransaction();
+            if (mTargetAdjusted) {
+                mSplitLayout.updateAdjustedBounds(mShownTop, mHiddenTop, mShownTop);
+                wct.setBounds(mSplits.mSecondary.token, mSplitLayout.mAdjustedSecondary);
+                // "Freeze" the configuration size so that the app doesn't get a config
+                // or relaunch. This is required because normally nav-bar contributes
+                // to configuration bounds (via nondecorframe).
+                Rect adjustAppBounds = new Rect(mSplits.mSecondary.configuration
+                        .windowConfiguration.getAppBounds());
+                adjustAppBounds.offset(0, mSplitLayout.mAdjustedSecondary.top
+                        - mSplitLayout.mSecondary.top);
+                wct.setAppBounds(mSplits.mSecondary.token, adjustAppBounds);
+                wct.setScreenSizeDp(mSplits.mSecondary.token,
+                        mSplits.mSecondary.configuration.screenWidthDp,
+                        mSplits.mSecondary.configuration.screenHeightDp);
+            } else {
+                wct.setBounds(mSplits.mSecondary.token, mSplitLayout.mSecondary);
+                wct.setAppBounds(mSplits.mSecondary.token, null);
+                wct.setScreenSizeDp(mSplits.mSecondary.token,
+                        SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
+            }
+            try {
+                ActivityTaskManager.getTaskOrganizerController()
+                        .applyContainerTransaction(wct, null /* organizer */);
+            } catch (RemoteException e) {
+            }
+
+            // Update all the adjusted-for-ime states
+            mView.setAdjustedForIme(mTargetShown, mTargetShown
+                    ? DisplayImeController.ANIMATION_DURATION_SHOW_MS
+                    : DisplayImeController.ANIMATION_DURATION_HIDE_MS);
+            setAdjustedForIme(mTargetShown);
+        }
+
+        @Override
+        public void onImePositionChanged(int displayId, int imeTop,
+                SurfaceControl.Transaction t) {
+            if (mAnimation != null) {
+                // Not synchronized with IME anymore, so return.
+                return;
+            }
+            final float fraction = ((float) imeTop - mHiddenTop) / (mShownTop - mHiddenTop);
+            final float progress = mTargetShown ? fraction : 1.f - fraction;
+            onProgress(progress, t);
+        }
+
+        @Override
+        public void onImeEndPositioning(int displayId, boolean cancelled,
+                SurfaceControl.Transaction t) {
+            if (mAnimation != null) {
+                // Not synchronized with IME anymore, so return.
+                return;
+            }
+            onEnd(cancelled, t);
+        }
+
+        private void onProgress(float progress, SurfaceControl.Transaction t) {
+            if (mTargetAdjusted != mAdjusted) {
+                final float fraction = mTargetAdjusted ? progress : 1.f - progress;
+                mLastAdjustTop = (int) (fraction * mShownTop + (1.f - fraction) * mHiddenTop);
+                mSplitLayout.updateAdjustedBounds(mLastAdjustTop, mHiddenTop, mShownTop);
+                mView.resizeSplitSurfaces(t, mSplitLayout.mAdjustedPrimary,
+                        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);
+            mView.setResizeDimLayer(t, false /* primary */,
+                    mLastSecondaryDim * invProg + progress * targetSecondaryDim);
+        }
+
+        private void onEnd(boolean cancelled, SurfaceControl.Transaction t) {
+            if (!cancelled) {
+                onProgress(1.f, t);
+                mAdjusted = mTargetAdjusted;
+                mImeWasShown = mTargetShown;
+                mLastAdjustTop = mAdjusted ? mShownTop : mHiddenTop;
+                mLastPrimaryDim =
+                        (mSecondaryHasFocus && mTargetShown) ? ADJUSTED_NONFOCUS_DIM : 0.f;
+                mLastSecondaryDim =
+                        (!mSecondaryHasFocus && mTargetShown) ? ADJUSTED_NONFOCUS_DIM : 0.f;
+            }
+        }
+
+        private void startAsyncAnimation() {
+            if (mAnimation != null) {
+                mAnimation.cancel();
+            }
+            mAnimation = ValueAnimator.ofFloat(0.f, 1.f);
+            mAnimation.setDuration(DisplayImeController.ANIMATION_DURATION_SHOW_MS);
+            if (mTargetAdjusted != mAdjusted) {
+                final float fraction =
+                        ((float) mLastAdjustTop - mHiddenTop) / (mShownTop - mHiddenTop);
+                final float progress = mTargetAdjusted ? fraction : 1.f - fraction;
+                mAnimation.setCurrentFraction(progress);
+            }
+
+            mAnimation.addUpdateListener(animation -> {
+                SurfaceControl.Transaction t = mTransactionPool.acquire();
+                float value = (float) animation.getAnimatedValue();
+                onProgress(value, t);
+                t.apply();
+                mTransactionPool.release(t);
+            });
+            mAnimation.setInterpolator(DisplayImeController.INTERPOLATOR);
+            mAnimation.addListener(new AnimatorListenerAdapter() {
+                private boolean mCancel = false;
                 @Override
-                public void onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
-                        boolean imeShouldShow, SurfaceControl.Transaction t) {
-                    mSecondaryHasFocus = getSecondaryHasFocus(displayId);
-                    mTargetAdjusted = imeShouldShow && mSecondaryHasFocus
-                            && !mSplitLayout.mDisplayLayout.isLandscape();
-                    mHiddenTop = hiddenTop;
-                    mShownTop = shownTop;
-                    mTargetShown = imeShouldShow;
-                    if (mLastAdjustTop < 0) {
-                        mLastAdjustTop = imeShouldShow ? hiddenTop : shownTop;
-                    }
-                    if (mAnimation != null || (mImeWasShown && imeShouldShow
-                            && mTargetAdjusted != mAdjusted)) {
-                        // We need to animate adjustment independently of the IME position, so
-                        // start our own animation to drive adjustment. This happens when a
-                        // different split's editor has gained focus while the IME is still visible.
-                        startAsyncAnimation();
-                    }
-                    // Reposition the server's secondary split position so that it evaluates
-                    // insets properly.
-                    WindowContainerTransaction wct = new WindowContainerTransaction();
-                    if (mTargetAdjusted) {
-                        mSplitLayout.updateAdjustedBounds(mShownTop, mHiddenTop, mShownTop);
-                        wct.setBounds(mSplits.mSecondary.token, mSplitLayout.mAdjustedSecondary);
-                        // "Freeze" the configuration size so that the app doesn't get a config
-                        // or relaunch. This is required because normally nav-bar contributes
-                        // to configuration bounds (via nondecorframe).
-                        Rect adjustAppBounds = new Rect(mSplits.mSecondary.configuration
-                                .windowConfiguration.getAppBounds());
-                        adjustAppBounds.offset(0, mSplitLayout.mAdjustedSecondary.top
-                                - mSplitLayout.mSecondary.top);
-                        wct.setAppBounds(mSplits.mSecondary.token, adjustAppBounds);
-                        wct.setScreenSizeDp(mSplits.mSecondary.token,
-                                mSplits.mSecondary.configuration.screenWidthDp,
-                                mSplits.mSecondary.configuration.screenHeightDp);
-                    } else {
-                        wct.setBounds(mSplits.mSecondary.token, mSplitLayout.mSecondary);
-                        wct.setAppBounds(mSplits.mSecondary.token, null);
-                        wct.setScreenSizeDp(mSplits.mSecondary.token,
-                                SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
-                    }
-                    try {
-                        ActivityTaskManager.getTaskOrganizerController()
-                                .applyContainerTransaction(wct, null /* organizer */);
-                    } catch (RemoteException e) {
-                    }
-
-                    // Update all the adjusted-for-ime states
-                    mView.setAdjustedForIme(mTargetShown, mTargetShown
-                            ? DisplayImeController.ANIMATION_DURATION_SHOW_MS
-                            : DisplayImeController.ANIMATION_DURATION_HIDE_MS);
-                    setAdjustedForIme(mTargetShown);
+                public void onAnimationCancel(Animator animation) {
+                    mCancel = true;
                 }
-
                 @Override
-                public void onImePositionChanged(int displayId, int imeTop,
-                        SurfaceControl.Transaction t) {
-                    if (mAnimation != null) {
-                        // Not synchronized with IME anymore, so return.
-                        return;
-                    }
-                    final float fraction = ((float) imeTop - mHiddenTop) / (mShownTop - mHiddenTop);
-                    final float progress = mTargetShown ? fraction : 1.f - fraction;
-                    onProgress(progress, t);
+                public void onAnimationEnd(Animator animation) {
+                    SurfaceControl.Transaction t = mTransactionPool.acquire();
+                    onEnd(mCancel, t);
+                    t.apply();
+                    mTransactionPool.release(t);
+                    mAnimation = null;
                 }
-
-                @Override
-                public void onImeEndPositioning(int displayId, boolean cancelled,
-                        SurfaceControl.Transaction t) {
-                    if (mAnimation != null) {
-                        // Not synchronized with IME anymore, so return.
-                        return;
-                    }
-                    onEnd(cancelled, t);
-                }
-
-                private void onProgress(float progress, SurfaceControl.Transaction t) {
-                    if (mTargetAdjusted != mAdjusted) {
-                        final float fraction = mTargetAdjusted ? progress : 1.f - progress;
-                        mLastAdjustTop =
-                                (int) (fraction * mShownTop + (1.f - fraction) * mHiddenTop);
-                        mSplitLayout.updateAdjustedBounds(mLastAdjustTop, mHiddenTop, mShownTop);
-                        mView.resizeSplitSurfaces(t, mSplitLayout.mAdjustedPrimary,
-                                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);
-                    mView.setResizeDimLayer(t, false /* primary */,
-                            mLastSecondaryDim * invProg + progress * targetSecondaryDim);
-                }
-
-                private void onEnd(boolean cancelled, SurfaceControl.Transaction t) {
-                    if (!cancelled) {
-                        onProgress(1.f, t);
-                        mAdjusted = mTargetAdjusted;
-                        mImeWasShown = mTargetShown;
-                        mLastAdjustTop = mAdjusted ? mShownTop : mHiddenTop;
-                        mLastPrimaryDim =
-                                (mSecondaryHasFocus && mTargetShown) ? ADJUSTED_NONFOCUS_DIM : 0.f;
-                        mLastSecondaryDim =
-                                (!mSecondaryHasFocus && mTargetShown) ? ADJUSTED_NONFOCUS_DIM : 0.f;
-                    }
-                }
-
-                private void startAsyncAnimation() {
-                    if (mAnimation != null) {
-                        mAnimation.cancel();
-                    }
-                    mAnimation = ValueAnimator.ofFloat(0.f, 1.f);
-                    mAnimation.setDuration(DisplayImeController.ANIMATION_DURATION_SHOW_MS);
-                    if (mTargetAdjusted != mAdjusted) {
-                        final float fraction =
-                                ((float) mLastAdjustTop - mHiddenTop) / (mShownTop - mHiddenTop);
-                        final float progress = mTargetAdjusted ? fraction : 1.f - fraction;
-                        mAnimation.setCurrentFraction(progress);
-                    }
-
-                    mAnimation.addUpdateListener(animation -> {
-                        SurfaceControl.Transaction t = mTransactionPool.acquire();
-                        float value = (float) animation.getAnimatedValue();
-                        onProgress(value, t);
-                        t.apply();
-                        mTransactionPool.release(t);
-                    });
-                    mAnimation.setInterpolator(DisplayImeController.INTERPOLATOR);
-                    mAnimation.addListener(new AnimatorListenerAdapter() {
-                        private boolean mCancel = false;
-                        @Override
-                        public void onAnimationCancel(Animator animation) {
-                            mCancel = true;
-                        }
-                        @Override
-                        public void onAnimationEnd(Animator animation) {
-                            SurfaceControl.Transaction t = mTransactionPool.acquire();
-                            onEnd(mCancel, t);
-                            t.apply();
-                            mTransactionPool.release(t);
-                            mAnimation = null;
-                        }
-                    });
-                    mAnimation.start();
-                }
-            };
+            });
+            mAnimation.start();
+        }
+    }
+    private final DividerImeController mImePositionProcessor = new DividerImeController();
 
     public Divider(Context context, Optional<Lazy<Recents>> recentsOptionalLazy,
             DisplayController displayController, SystemWindows systemWindows,
@@ -513,44 +509,39 @@
         }
     }
 
-    private void setHomeStackResizable(boolean resizable) {
-        if (mHomeStackResizable == resizable) {
-            return;
-        }
-        mHomeStackResizable = resizable;
-        if (!inSplitMode()) {
-            return;
-        }
-        WindowManagerProxy.applyHomeTasksMinimized(mSplitLayout, mSplits.mSecondary.token);
-    }
-
-    private void updateMinimizedDockedStack(final boolean minimized, final long animDuration,
-            final boolean isHomeStackResizable) {
-        setHomeStackResizable(isHomeStackResizable);
-        if (animDuration > 0) {
-            mView.setMinimizedDockStack(minimized, animDuration, isHomeStackResizable);
-        } else {
-            mView.setMinimizedDockStack(minimized, isHomeStackResizable);
-        }
-        updateTouchable();
-    }
-
     /** Switch to minimized state if appropriate */
     public void setMinimized(final boolean minimized) {
         mHandler.post(() -> {
-            if (!inSplitMode()) {
-                return;
-            }
-            if (mMinimized == minimized) {
-                return;
-            }
-            mMinimized = minimized;
-            WindowManagerProxy.applyPrimaryFocusable(mSplits, !mMinimized);
-            mView.setMinimizedDockStack(minimized, getAnimDuration(), mHomeStackResizable);
-            updateTouchable();
+            setHomeMinimized(minimized, mHomeStackResizable);
         });
     }
 
+    private void setHomeMinimized(final boolean minimized, boolean homeStackResizable) {
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        // Update minimized state
+        if (mMinimized != minimized) {
+            mMinimized = minimized;
+        }
+        // Always set this because we could be entering split when mMinimized is already true
+        wct.setFocusable(mSplits.mPrimary.token, !mMinimized);
+
+        // Update home-stack resizability
+        if (mHomeStackResizable != homeStackResizable) {
+            mHomeStackResizable = homeStackResizable;
+            if (inSplitMode()) {
+                WindowManagerProxy.applyHomeTasksMinimized(
+                        mSplitLayout, mSplits.mSecondary.token, wct);
+            }
+        }
+
+        // Sync state to DividerView if it exists.
+        if (mView != null) {
+            mView.setMinimizedDockStack(minimized, getAnimDuration(), homeStackResizable);
+        }
+        updateTouchable();
+        WindowManagerProxy.applyContainerTransaction(wct);
+    }
+
     void setAdjustedForIme(boolean adjustedForIme) {
         if (mAdjustedForIme == adjustedForIme) {
             return;
@@ -646,46 +637,24 @@
     }
 
     void ensureMinimizedSplit() {
-        final boolean wasMinimized = mMinimized;
-        mMinimized = true;
-        setHomeStackResizable(mSplits.mSecondary.isResizable());
-        WindowManagerProxy.applyPrimaryFocusable(mSplits, false /* focusable */);
+        setHomeMinimized(true /* minimized */, mSplits.mSecondary.isResizable());
         if (!inSplitMode()) {
             // Wasn't in split-mode yet, so enter now.
             if (DEBUG) {
                 Log.d(TAG, " entering split mode with minimized=true");
             }
             updateVisibility(true /* visible */);
-        } else if (!wasMinimized) {
-            if (DEBUG) {
-                Log.d(TAG, " in split mode, but minimizing ");
-            }
-            // Was already in split-mode, update just minimized state.
-            updateMinimizedDockedStack(mMinimized, getAnimDuration(),
-                    mHomeStackResizable);
         }
     }
 
     void ensureNormalSplit() {
-        if (mMinimized) {
-            WindowManagerProxy.applyPrimaryFocusable(mSplits, true /* focusable */);
-        }
+        setHomeMinimized(false /* minimized */, mHomeStackResizable);
         if (!inSplitMode()) {
             // Wasn't in split-mode, so enter now.
             if (DEBUG) {
                 Log.d(TAG, " enter split mode unminimized ");
             }
-            mMinimized = false;
             updateVisibility(true /* visible */);
         }
-        if (mMinimized) {
-            // Was in minimized state, so leave that.
-            if (DEBUG) {
-                Log.d(TAG, " in split mode already, but unminimizing ");
-            }
-            mMinimized = false;
-            updateMinimizedDockedStack(mMinimized, getAnimDuration(),
-                    mHomeStackResizable);
-        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
index 3020a25..729df38 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
@@ -88,6 +88,9 @@
     }
 
     public void setTouchable(boolean touchable) {
+        if (mView == null) {
+            return;
+        }
         boolean changed = false;
         if (!touchable && (mLp.flags & FLAG_NOT_TOUCHABLE) == 0) {
             mLp.flags |= FLAG_NOT_TOUCHABLE;
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
index 5cc8799..48ea4ae 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
@@ -88,7 +88,7 @@
     }
 
     @Override
-    public void taskVanished(IWindowContainer container) {
+    public void taskVanished(RunningTaskInfo taskInfo) {
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 167c33a..fea57a3 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -21,6 +21,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.graphics.Rect;
@@ -137,17 +138,13 @@
         return resizable;
     }
 
-    static void applyHomeTasksMinimized(SplitDisplayLayout layout, IWindowContainer parent) {
-        applyHomeTasksMinimized(layout, parent, null /* transaction */);
-    }
-
     /**
      * Assign a fixed override-bounds to home tasks that reflect their geometry while the primary
      * split is minimized. This actually "sticks out" of the secondary split area, but when in
      * minimized mode, the secondary split gets a 'negative' crop to expose it.
      */
     static boolean applyHomeTasksMinimized(SplitDisplayLayout layout, IWindowContainer parent,
-            WindowContainerTransaction t) {
+            @NonNull WindowContainerTransaction wct) {
         // Resize the home/recents stacks to the larger minimized-state size
         final Rect homeBounds;
         final ArrayList<IWindowContainer> homeStacks = new ArrayList<>();
@@ -158,19 +155,9 @@
             homeBounds = new Rect(0, 0, layout.mDisplayLayout.width(),
                     layout.mDisplayLayout.height());
         }
-        WindowContainerTransaction wct = t != null ? t : new WindowContainerTransaction();
         for (int i = homeStacks.size() - 1; i >= 0; --i) {
             wct.setBounds(homeStacks.get(i), homeBounds);
         }
-        if (t != null) {
-            return isHomeResizable;
-        }
-        try {
-            ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct,
-                    null /* organizer */);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to resize home stacks ", e);
-        }
         return isHomeResizable;
     }
 
@@ -301,10 +288,8 @@
         }
     }
 
-    static void applyPrimaryFocusable(SplitScreenTaskOrganizer splits, boolean focusable) {
+    static void applyContainerTransaction(WindowContainerTransaction wct) {
         try {
-            WindowContainerTransaction wct = new WindowContainerTransaction();
-            wct.setFocusable(splits.mPrimary.token, focusable);
             ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct,
                     null /* organizer */);
         } catch (RemoteException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 53605e5..7c06157 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -282,7 +282,7 @@
                     .withCornerRadius(mCornerRadius)
                     .withVisibility(true)
                     .build();
-            mSyncRtTransactionApplier.scheduleApply(params);
+            mSyncRtTransactionApplier.scheduleApply(true /* earlyWakeup */, params);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
index 88888d1..269a7a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
@@ -14,7 +14,7 @@
  * limitations under the License
  */
 
-package com.android.systemui.statusbar.notification.interruption
+package com.android.systemui.statusbar.notification
 
 import android.content.Context
 import android.media.MediaMetadata
@@ -24,7 +24,6 @@
 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/interruption/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationAlertingManager.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
index b572502..df21f0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationAlertingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.notification.interruption;
+package com.android.systemui.statusbar.notification;
 
 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,9 +27,6 @@
 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;
@@ -42,7 +39,7 @@
     private final NotificationRemoteInputManager mRemoteInputManager;
     private final VisualStabilityManager mVisualStabilityManager;
     private final StatusBarStateController mStatusBarStateController;
-    private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
+    private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
     private final NotificationListener mNotificationListener;
 
     private HeadsUpManager mHeadsUpManager;
@@ -55,13 +52,13 @@
             NotificationRemoteInputManager remoteInputManager,
             VisualStabilityManager visualStabilityManager,
             StatusBarStateController statusBarStateController,
-            NotificationInterruptStateProvider notificationInterruptionStateProvider,
+            NotificationInterruptionStateProvider notificationInterruptionStateProvider,
             NotificationListener notificationListener,
             HeadsUpManager headsUpManager) {
         mRemoteInputManager = remoteInputManager;
         mVisualStabilityManager = visualStabilityManager;
         mStatusBarStateController = statusBarStateController;
-        mNotificationInterruptStateProvider = notificationInterruptionStateProvider;
+        mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
         mNotificationListener = notificationListener;
         mHeadsUpManager = headsUpManager;
 
@@ -97,7 +94,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 (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
+            if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
                 mHeadsUpManager.showNotification(entry);
                 if (!mStatusBarStateController.isDozing()) {
                     // Mark as seen immediately
@@ -112,7 +109,7 @@
     private void updateAlertState(NotificationEntry entry) {
         boolean alertAgain = alertAgain(entry, entry.getSbn().getNotification());
         // includes check for whether this notification should be filtered:
-        boolean shouldAlert = mNotificationInterruptStateProvider.shouldHeadsUp(entry);
+        boolean shouldAlert = mNotificationInterruptionStateProvider.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/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
similarity index 63%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
index 46d5044..bbf2dde 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
@@ -14,35 +14,33 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.notification.interruption;
+package com.android.systemui.statusbar.notification;
 
 import static com.android.systemui.statusbar.StatusBarState.SHADE;
 
 import android.app.NotificationManager;
-import android.content.ContentResolver;
+import android.content.Context;
 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.dagger.qualifiers.Main;
+import com.android.systemui.Dependency;
 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;
 
@@ -50,84 +48,120 @@
  * Provides heads-up and pulsing state for notification entries.
  */
 @Singleton
-public class NotificationInterruptStateProviderImpl implements NotificationInterruptStateProvider {
+public class NotificationInterruptionStateProvider {
+
     private static final String TAG = "InterruptionStateProvider";
-    private static final boolean DEBUG = true; //false;
+    private static final boolean DEBUG = 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 ContentResolver mContentResolver;
+    private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
+
+    private final Context mContext;
     private final PowerManager mPowerManager;
     private final IDreamManager mDreamManager;
-    private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
     private final BatteryController mBatteryController;
-    private final ContentObserver mHeadsUpObserver;
-    private HeadsUpManager mHeadsUpManager;
 
+    private NotificationPresenter mPresenter;
+    private HeadsUpManager mHeadsUpManager;
+    private HeadsUpSuppressor mHeadsUpSuppressor;
+
+    private ContentObserver mHeadsUpObserver;
     @VisibleForTesting
     protected boolean mUseHeadsUp = false;
+    private boolean mDisableNotificationAlerts;
 
     @Inject
-    public NotificationInterruptStateProviderImpl(
-            ContentResolver contentResolver,
+    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,
             PowerManager powerManager,
             IDreamManager dreamManager,
             AmbientDisplayConfiguration ambientDisplayConfiguration,
             NotificationFilter notificationFilter,
             BatteryController batteryController,
-            StatusBarStateController statusBarStateController,
-            HeadsUpManager headsUpManager,
-            @Main Handler mainHandler) {
-        mContentResolver = contentResolver;
+            StatusBarStateController statusBarStateController) {
+        mContext = context;
         mPowerManager = powerManager;
         mDreamManager = dreamManager;
         mBatteryController = batteryController;
         mAmbientDisplayConfiguration = ambientDisplayConfiguration;
         mNotificationFilter = notificationFilter;
         mStatusBarStateController = statusBarStateController;
-        mHeadsUpManager = headsUpManager;
-        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();
+    }
+
+    /** 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;
 
         if (ENABLE_HEADS_UP) {
-            mContentResolver.registerContentObserver(
+            mContext.getContentResolver().registerContentObserver(
                     Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED),
                     true,
                     mHeadsUpObserver);
-            mContentResolver.registerContentObserver(
+            mContext.getContentResolver().registerContentObserver(
                     Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
                     mHeadsUpObserver);
         }
         mHeadsUpObserver.onChange(true); // set up
     }
 
-    @Override
-    public void addSuppressor(NotificationInterruptSuppressor suppressor) {
-        mSuppressors.add(suppressor);
-    }
-
-    @Override
+    /**
+     * 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
+     */
     public boolean shouldBubbleUp(NotificationEntry entry) {
         final StatusBarNotification sbn = entry.getSbn();
 
@@ -167,8 +201,12 @@
         return true;
     }
 
-
-    @Override
+    /**
+     * 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
+     */
     public boolean shouldHeadsUp(NotificationEntry entry) {
         if (mStatusBarStateController.isDozing()) {
             return shouldHeadsUpWhenDozing(entry);
@@ -177,17 +215,6 @@
         }
     }
 
-    /**
-     * 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();
 
@@ -244,15 +271,13 @@
             return false;
         }
 
-        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;
+        if (!mHeadsUpSuppressor.canHeadsUp(entry, sbn)) {
+            if (DEBUG_HEADS_UP) {
+                Log.d(TAG, "No heads up: aborted by suppressor: " + sbn.getKey());
             }
+            return false;
         }
+
         return true;
     }
 
@@ -300,7 +325,7 @@
             }
             return false;
         }
-        return true;
+         return true;
     }
 
     /**
@@ -309,7 +334,8 @@
      * @param entry the entry to check
      * @return true if these checks pass, false if the notification should not alert
      */
-    private boolean canAlertCommon(NotificationEntry entry) {
+    @VisibleForTesting
+    public boolean canAlertCommon(NotificationEntry entry) {
         StatusBarNotification sbn = entry.getSbn();
 
         if (mNotificationFilter.shouldFilterOut(entry)) {
@@ -326,16 +352,6 @@
             }
             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;
     }
 
@@ -345,17 +361,15 @@
      * @param entry the entry to check
      * @return true if these checks pass, false if the notification should not alert
      */
-    private boolean canAlertAwakeCommon(NotificationEntry entry) {
+    @VisibleForTesting
+    public boolean canAlertAwakeCommon(NotificationEntry entry) {
         StatusBarNotification sbn = entry.getSbn();
 
-        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;
+        if (mPresenter.isDeviceInVrMode()) {
+            if (DEBUG_HEADS_UP) {
+                Log.d(TAG, "No alerting: no huns or vr mode");
             }
+            return false;
         }
 
         if (isSnoozedPackage(sbn)) {
@@ -378,4 +392,54 @@
     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/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 2747696..8a23e37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -17,6 +17,7 @@
 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
@@ -25,10 +26,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
@@ -36,14 +37,15 @@
 
 @Singleton
 class NotificationWakeUpCoordinator @Inject constructor(
-    private val mHeadsUpManager: HeadsUpManager,
-    private val statusBarStateController: StatusBarStateController,
-    private val bypassController: KeyguardBypassController,
-    private val dozeParameters: DozeParameters
-) : OnHeadsUpChangedListener, StatusBarStateController.StateListener, PanelExpansionListener {
+        private val mHeadsUpManagerPhone: HeadsUpManagerPhone,
+        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)
@@ -76,10 +78,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
@@ -98,7 +100,7 @@
         }
 
     private var collapsedEnoughToHide: Boolean = false
-    lateinit var iconAreaController: NotificationIconAreaController
+    lateinit var iconAreaController : NotificationIconAreaController
 
     var pulsing: Boolean = false
         set(value) {
@@ -130,8 +132,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
@@ -141,7 +143,7 @@
         }
 
     init {
-        mHeadsUpManager.addListener(this)
+        mHeadsUpManagerPhone.addListener(this)
         statusBarStateController.addCallback(this)
         addListener(object : WakeUpListener {
             override fun onFullyHiddenChanged(isFullyHidden: Boolean) {
@@ -153,7 +155,7 @@
                             increaseSpeed = false)
                 }
             }
-        })
+        });
     }
 
     fun setStackScroller(stackScroller: NotificationStackScrollLayout) {
@@ -176,55 +178,46 @@
      * @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
-            mHeadsUpManager.releaseAllImmediately()
+            mHeadsUpManagerPhone.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 || mHeadsUpManager.hasNotifications()
+        var visible = mNotificationsVisibleForExpansion || mHeadsUpManagerPhone.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)
@@ -237,8 +230,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)
         }
@@ -252,17 +245,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)
@@ -273,23 +266,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)
-                mHeadsUpManager.releaseAllImmediately()
+                mHeadsUpManagerPhone.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
@@ -307,7 +300,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()
@@ -318,7 +311,7 @@
         mLinearVisibilityAmount = visibilityAmount
         mVisibilityAmount = mVisibilityInterpolator.getInterpolation(
                 visibilityAmount)
-        handleAnimationFinished()
+        handleAnimationFinished();
         updateHideAmount()
     }
 
@@ -329,7 +322,7 @@
         }
     }
 
-    fun getWakeUpHeight(): Float {
+    fun getWakeUpHeight() : Float {
         return mStackScroller.wakeUpHeight
     }
 
@@ -337,7 +330,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) {
@@ -368,7 +361,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 4beeede..e8a62e4 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 NotificationInterruptStateProvider mNotificationInterruptStateProvider;
+    private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
 
     private final Context mContext;
     private final NotifBindPipeline mNotifBindPipeline;
@@ -97,7 +97,7 @@
             StatusBarStateController statusBarStateController,
             NotificationGroupManager notificationGroupManager,
             NotificationGutsManager notificationGutsManager,
-            NotificationInterruptStateProvider notificationInterruptionStateProvider,
+            NotificationInterruptionStateProvider notificationInterruptionStateProvider,
             Provider<RowInflaterTask> rowInflaterTaskProvider,
             ExpandableNotificationRowComponent.Builder expandableNotificationRowComponentBuilder) {
         mContext = context;
@@ -106,7 +106,7 @@
         mMessagingUtil = notificationMessagingUtil;
         mNotificationRemoteInputManager = notificationRemoteInputManager;
         mNotificationLockscreenUserManager = notificationLockscreenUserManager;
-        mNotificationInterruptStateProvider = notificationInterruptionStateProvider;
+        mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
         mRowInflaterTaskProvider = rowInflaterTaskProvider;
         mExpandableNotificationRowComponentBuilder = expandableNotificationRowComponentBuilder;
     }
@@ -243,7 +243,7 @@
         params.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
         params.setUseLowPriority(entry.isAmbient());
 
-        if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
+        if (mNotificationInterruptionStateProvider.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 cd6affd..e425ee9 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,8 +31,10 @@
 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;
@@ -42,10 +44,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;
 import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -57,7 +58,6 @@
 
 import javax.inject.Singleton;
 
-import dagger.Binds;
 import dagger.Lazy;
 import dagger.Module;
 import dagger.Provides;
@@ -127,7 +127,7 @@
             NotificationRemoteInputManager remoteInputManager,
             VisualStabilityManager visualStabilityManager,
             StatusBarStateController statusBarStateController,
-            NotificationInterruptStateProvider notificationInterruptStateProvider,
+            NotificationInterruptionStateProvider notificationInterruptionStateProvider,
             NotificationListener notificationListener,
             HeadsUpManager headsUpManager) {
         return new NotificationAlertingManager(
@@ -135,7 +135,7 @@
                 remoteInputManager,
                 visualStabilityManager,
                 statusBarStateController,
-                notificationInterruptStateProvider,
+                notificationInterruptionStateProvider,
                 notificationListener,
                 headsUpManager);
     }
@@ -148,13 +148,22 @@
             @UiBackground Executor uiBgExecutor,
             NotificationEntryManager entryManager,
             StatusBarStateController statusBarStateController,
-            NotificationLogger.ExpansionStateLogger expansionStateLogger) {
+            NotificationLogger.ExpansionStateLogger expansionStateLogger,
+            NotificationPanelLogger notificationPanelLogger) {
         return new NotificationLogger(
                 notificationListener,
                 uiBgExecutor,
                 entryManager,
                 statusBarStateController,
-                expansionStateLogger);
+                expansionStateLogger,
+                notificationPanelLogger);
+    }
+
+    /** Provides an instance of {@link NotificationPanelLogger} */
+    @Singleton
+    @Provides
+    static NotificationPanelLogger provideNotificationPanelLogger() {
+        return new NotificationPanelLoggerImpl();
     }
 
     /** Provides an instance of {@link com.android.internal.logging.UiEventLogger} */
@@ -201,9 +210,4 @@
             NotificationEntryManager entryManager) {
         return featureFlags.isNewNotifPipelineRenderingEnabled() ? pipeline.get() : entryManager;
     }
-
-    /** */
-    @Binds
-    NotificationInterruptStateProvider bindNotificationInterruptStateProvider(
-            NotificationInterruptStateProviderImpl notificationInterruptStateProviderImpl);
 }
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
deleted file mode 100644
index 3292a8f..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
+++ /dev/null
@@ -1,59 +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.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/interruption/NotificationInterruptSuppressor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptSuppressor.java
deleted file mode 100644
index c19f8bd..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptSuppressor.java
+++ /dev/null
@@ -1,64 +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.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/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 6e161c9..ad04788 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -70,6 +70,7 @@
     private final NotificationListenerService mNotificationListener;
     private final Executor mUiBgExecutor;
     private final NotificationEntryManager mEntryManager;
+    private final NotificationPanelLogger mNotificationPanelLogger;
     private HeadsUpManager mHeadsUpManager;
     private final ExpansionStateLogger mExpansionStateLogger;
 
@@ -198,13 +199,15 @@
             @UiBackground Executor uiBgExecutor,
             NotificationEntryManager entryManager,
             StatusBarStateController statusBarStateController,
-            ExpansionStateLogger expansionStateLogger) {
+            ExpansionStateLogger expansionStateLogger,
+            NotificationPanelLogger notificationPanelLogger) {
         mNotificationListener = notificationListener;
         mUiBgExecutor = uiBgExecutor;
         mEntryManager = entryManager;
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
         mExpansionStateLogger = expansionStateLogger;
+        mNotificationPanelLogger = notificationPanelLogger;
         // Not expected to be destroyed, don't need to unsubscribe
         statusBarStateController.addCallback(this);
 
@@ -264,6 +267,8 @@
         // (Note that in cases where the scroller does emit events, this
         // additional event doesn't break anything.)
         mNotificationLocationsChangedListener.onChildLocationsChanged();
+        mNotificationPanelLogger.logPanelShown(mListContainer.hasPulsingNotifications(),
+                mEntryManager.getVisibleNotifications());
     }
 
     private void setDozing(boolean dozing) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java
new file mode 100644
index 0000000..9a25c48
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java
@@ -0,0 +1,95 @@
+/*
+ * 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.logging;
+
+import android.annotation.Nullable;
+import android.service.notification.StatusBarNotification;
+
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.logging.nano.Notifications;
+
+import java.util.List;
+/**
+ * Statsd logging for notification panel.
+ */
+public interface NotificationPanelLogger {
+
+    /**
+     * Log a NOTIFICATION_PANEL_REPORTED statsd event.
+     * @param visibleNotifications as provided by NotificationEntryManager.getVisibleNotifications()
+     */
+    void logPanelShown(boolean isLockscreen,
+            @Nullable List<NotificationEntry> visibleNotifications);
+
+    enum NotificationPanelEvent implements UiEventLogger.UiEventEnum {
+        @UiEvent(doc = "Notification panel shown from status bar.")
+        NOTIFICATION_PANEL_OPEN_STATUS_BAR(200),
+        @UiEvent(doc = "Notification panel shown from lockscreen.")
+        NOTIFICATION_PANEL_OPEN_LOCKSCREEN(201);
+
+        private final int mId;
+        NotificationPanelEvent(int id) {
+            mId = id;
+        }
+        @Override public int getId() {
+            return mId;
+        }
+
+        public static NotificationPanelEvent fromLockscreen(boolean isLockscreen) {
+            return isLockscreen ? NOTIFICATION_PANEL_OPEN_LOCKSCREEN :
+                    NOTIFICATION_PANEL_OPEN_STATUS_BAR;
+        }
+    }
+
+    /**
+     * Composes a NotificationsList proto from the list of visible notifications.
+     * @param visibleNotifications as provided by NotificationEntryManager.getVisibleNotifications()
+     * @return NotificationList proto suitable for SysUiStatsLog.write(NOTIFICATION_PANEL_REPORTED)
+     */
+    static Notifications.NotificationList toNotificationProto(
+            @Nullable List<NotificationEntry> visibleNotifications) {
+        Notifications.NotificationList notificationList = new Notifications.NotificationList();
+        if (visibleNotifications == null) {
+            return notificationList;
+        }
+        final Notifications.Notification[] proto_array =
+                new Notifications.Notification[visibleNotifications.size()];
+        int i = 0;
+        for (NotificationEntry ne : visibleNotifications) {
+            final StatusBarNotification n = ne.getSbn();
+            if (n != null) {
+                final Notifications.Notification proto = new Notifications.Notification();
+                proto.uid = n.getUid();
+                proto.packageName = n.getPackageName();
+                if (n.getInstanceId() != null) {
+                    proto.instanceId = n.getInstanceId().getId();
+                }
+                // TODO set np.groupInstanceId
+                if (n.getNotification() != null) {
+                    proto.isGroupSummary = n.getNotification().isGroupSummary();
+                }
+                proto.section = 1 + ne.getBucket();  // We want 0 to mean not set / unknown
+                proto_array[i] = proto;
+            }
+            ++i;
+        }
+        notificationList.notifications = proto_array;
+        return notificationList;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLoggerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLoggerImpl.java
new file mode 100644
index 0000000..75a6019
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLoggerImpl.java
@@ -0,0 +1,41 @@
+/*
+ * 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.logging;
+
+import com.android.systemui.shared.system.SysUiStatsLog;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.logging.nano.Notifications;
+
+import com.google.protobuf.nano.MessageNano;
+
+import java.util.List;
+
+/**
+ * Normal implementation of NotificationPanelLogger.
+ */
+public class NotificationPanelLoggerImpl implements NotificationPanelLogger {
+    @Override
+    public void logPanelShown(boolean isLockscreen,
+            List<NotificationEntry> visibleNotifications) {
+        final Notifications.NotificationList proto = NotificationPanelLogger.toNotificationProto(
+                visibleNotifications);
+        SysUiStatsLog.write(SysUiStatsLog.NOTIFICATION_PANEL_REPORTED,
+                /* int event_id */ NotificationPanelEvent.fromLockscreen(isLockscreen).getId(),
+                /* int num_notifications*/ proto.notifications.length,
+                /* byte[] notifications*/ MessageNano.toByteArray(proto));
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto
new file mode 100644
index 0000000..552a5fb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto
@@ -0,0 +1,49 @@
+/*
+ * 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.logging;
+
+/**
+ * NotificationList proto from atoms.proto, duplicated here so that it's accessible in the build.
+ * Must be kept in sync with the version in atoms.proto.
+ */
+
+message Notification {
+    // The notifying app's uid and package.
+    optional int32 uid = 1;
+    optional string package_name = 2;
+    // A small system-assigned identifier for the notification.
+    optional int32 instance_id = 3;
+
+    // Grouping information.
+    optional int32 group_instance_id = 4;
+    optional bool is_group_summary = 5;
+
+    // The section of the shade that the notification is in.
+    // See NotificationSectionsManager.PriorityBucket.
+    enum NotificationSection {
+        SECTION_UNKNOWN = 0;
+        SECTION_HEADS_UP = 1;
+        SECTION_PEOPLE = 2;
+        SECTION_ALERTING = 3;
+        SECTION_SILENT = 4;
+    }
+    optional NotificationSection section = 6;
+}
+
+message NotificationList {
+    repeated Notification notifications = 1;  // An ordered sequence of notifications.
+}
\ No newline at end of file
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 287ede4..b3a62d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -185,14 +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.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;
@@ -403,9 +404,10 @@
 
     private final NotificationGutsManager mGutsManager;
     private final NotificationLogger mNotificationLogger;
+    private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
     private final NotificationViewHierarchyManager mViewHierarchyManager;
     private final KeyguardViewMediator mKeyguardViewMediator;
-    protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
+    private final NotificationAlertingManager mNotificationAlertingManager;
 
     // for disabling the status bar
     private int mDisabled1 = 0;
@@ -619,9 +621,10 @@
             RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
             NotificationGutsManager notificationGutsManager,
             NotificationLogger notificationLogger,
-            NotificationInterruptStateProvider notificationInterruptStateProvider,
+            NotificationInterruptionStateProvider notificationInterruptionStateProvider,
             NotificationViewHierarchyManager notificationViewHierarchyManager,
             KeyguardViewMediator keyguardViewMediator,
+            NotificationAlertingManager notificationAlertingManager,
             DisplayMetrics displayMetrics,
             MetricsLogger metricsLogger,
             @UiBackground Executor uiBgExecutor,
@@ -698,9 +701,10 @@
         mRemoteInputQuickSettingsDisabler = remoteInputQuickSettingsDisabler;
         mGutsManager = notificationGutsManager;
         mNotificationLogger = notificationLogger;
-        mNotificationInterruptStateProvider = notificationInterruptStateProvider;
+        mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
         mViewHierarchyManager = notificationViewHierarchyManager;
         mKeyguardViewMediator = keyguardViewMediator;
+        mNotificationAlertingManager = notificationAlertingManager;
         mDisplayMetrics = displayMetrics;
         mMetricsLogger = metricsLogger;
         mUiBgExecutor = uiBgExecutor;
@@ -1234,9 +1238,9 @@
         mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanelViewController,
                 mHeadsUpManager, mNotificationShadeWindowView, mStackScroller, mDozeScrimController,
                 mScrimController, mActivityLaunchAnimator, mDynamicPrivacyController,
-                mKeyguardStateController, mKeyguardIndicationController,
-                this /* statusBar */, mShadeController, mCommandQueue, mInitController,
-                mNotificationInterruptStateProvider);
+                mNotificationAlertingManager, mKeyguardStateController,
+                mKeyguardIndicationController,
+                this /* statusBar */, mShadeController, mCommandQueue, mInitController);
 
         mNotificationShelf.setOnActivatedListener(mPresenter);
         mRemoteInputManager.getController().addCallback(mNotificationShadeWindowController);
@@ -1585,9 +1589,8 @@
         }
 
         if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
-            if (areNotificationAlertsDisabled()) {
-                mHeadsUpManager.releaseAllImmediately();
-            }
+            mNotificationInterruptionStateProvider.setDisableNotificationAlerts(
+                    (state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0);
         }
 
         if ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) {
@@ -1602,10 +1605,6 @@
         }
     }
 
-    boolean areNotificationAlertsDisabled() {
-        return (mDisabled1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
-    }
-
     protected H createHandler() {
         return new StatusBar.H();
     }
@@ -2333,7 +2332,7 @@
 
     void checkBarModes() {
         if (mDemoMode) return;
-        if (mNotificationShadeWindowViewController != null) {
+        if (mNotificationShadeWindowViewController != null && getStatusBarTransitions() != null) {
             checkBarMode(mStatusBarMode, mStatusBarWindowState, getStatusBarTransitions());
         }
         mNavigationBarController.checkNavBarModes(mDisplayId);
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 53fa263..e1a20b6 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 NotificationInterruptStateProvider mNotificationInterruptStateProvider;
+    private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
     private final MetricsLogger mMetricsLogger;
     private final Context mContext;
     private final NotificationPanelViewController mNotificationPanel;
@@ -142,7 +142,7 @@
             NotificationLockscreenUserManager lockscreenUserManager,
             ShadeController shadeController, StatusBar statusBar,
             KeyguardStateController keyguardStateController,
-            NotificationInterruptStateProvider notificationInterruptStateProvider,
+            NotificationInterruptionStateProvider notificationInterruptionStateProvider,
             MetricsLogger metricsLogger, LockPatternUtils lockPatternUtils,
             Handler mainThreadHandler, Handler backgroundHandler, Executor uiBgExecutor,
             ActivityIntentHelper activityIntentHelper, BubbleController bubbleController,
@@ -167,7 +167,7 @@
         mActivityStarter = activityStarter;
         mEntryManager = entryManager;
         mStatusBarStateController = statusBarStateController;
-        mNotificationInterruptStateProvider = notificationInterruptStateProvider;
+        mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
         mMetricsLogger = metricsLogger;
         mAssistManagerLazy = assistManagerLazy;
         mGroupManager = groupManager;
@@ -436,7 +436,7 @@
     }
 
     private void handleFullScreenIntent(NotificationEntry entry) {
-        if (mNotificationInterruptStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) {
+        if (mNotificationInterruptionStateProvider.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 NotificationInterruptStateProvider mNotificationInterruptStateProvider;
+        private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
         private final ShadeController mShadeController;
         private NotificationPresenter mNotificationPresenter;
         private ActivityLaunchAnimator mActivityLaunchAnimator;
@@ -626,7 +626,7 @@
                 NotificationGroupManager groupManager,
                 NotificationLockscreenUserManager lockscreenUserManager,
                 KeyguardStateController keyguardStateController,
-                NotificationInterruptStateProvider notificationInterruptStateProvider,
+                NotificationInterruptionStateProvider notificationInterruptionStateProvider,
                 MetricsLogger metricsLogger,
                 LockPatternUtils lockPatternUtils,
                 @Main Handler mainThreadHandler,
@@ -654,7 +654,7 @@
             mGroupManager = groupManager;
             mLockscreenUserManager = lockscreenUserManager;
             mKeyguardStateController = keyguardStateController;
-            mNotificationInterruptStateProvider = notificationInterruptStateProvider;
+            mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
             mMetricsLogger = metricsLogger;
             mLockPatternUtils = lockPatternUtils;
             mMainThreadHandler = mainThreadHandler;
@@ -712,7 +712,7 @@
                     mShadeController,
                     mStatusBar,
                     mKeyguardStateController,
-                    mNotificationInterruptStateProvider,
+                    mNotificationInterruptionStateProvider,
                     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 79cea91..30d6b507 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,6 +98,8 @@
             (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 =
@@ -138,13 +140,13 @@
             ScrimController scrimController,
             ActivityLaunchAnimator activityLaunchAnimator,
             DynamicPrivacyController dynamicPrivacyController,
+            NotificationAlertingManager notificationAlertingManager,
             KeyguardStateController keyguardStateController,
             KeyguardIndicationController keyguardIndicationController,
             StatusBar statusBar,
             ShadeController shadeController,
             CommandQueue commandQueue,
-            InitController initController,
-            NotificationInterruptStateProvider notificationInterruptStateProvider) {
+            InitController initController) {
         mContext = context;
         mKeyguardStateController = keyguardStateController;
         mNotificationPanel = panel;
@@ -214,7 +216,8 @@
             mEntryManager.addNotificationLifetimeExtender(mGutsManager);
             mEntryManager.addNotificationLifetimeExtenders(
                     remoteInputManager.getLifetimeExtenders());
-            notificationInterruptStateProvider.addSuppressor(mInterruptSuppressor);
+            mNotificationInterruptionStateProvider.setUpWithPresenter(
+                    this, mHeadsUpManager, this::canHeadsUp);
             mLockscreenUserManager.setUpWithPresenter(this);
             mMediaManager.setUpWithPresenter(this);
             mVisualStabilityManager.setUpWithPresenter(this);
@@ -333,6 +336,39 @@
         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
@@ -471,66 +507,4 @@
             }
         }
     };
-
-    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 824e0f0..eec8d50 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,12 +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.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.phone.AutoHideController;
@@ -138,9 +139,10 @@
             RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
             NotificationGutsManager notificationGutsManager,
             NotificationLogger notificationLogger,
-            NotificationInterruptStateProvider notificationInterruptStateProvider,
+            NotificationInterruptionStateProvider notificationInterruptionStateProvider,
             NotificationViewHierarchyManager notificationViewHierarchyManager,
             KeyguardViewMediator keyguardViewMediator,
+            NotificationAlertingManager notificationAlertingManager,
             DisplayMetrics displayMetrics,
             MetricsLogger metricsLogger,
             @UiBackground Executor uiBgExecutor,
@@ -216,9 +218,10 @@
                 remoteInputQuickSettingsDisabler,
                 notificationGutsManager,
                 notificationLogger,
-                notificationInterruptStateProvider,
+                notificationInterruptionStateProvider,
                 notificationViewHierarchyManager,
                 keyguardViewMediator,
+                notificationAlertingManager,
                 displayMetrics,
                 metricsLogger,
                 uiBgExecutor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index cf9d8e1..24b9685 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -53,6 +53,12 @@
     boolean isAodPowerSave();
 
     /**
+     * Set reverse state.
+     * @param isReverse true if turn on reverse, false otherwise
+     */
+    default void setReverseState(boolean isReverse) {}
+
+    /**
      * A listener that will be notified whenever a change in battery level or power save mode has
      * occurred.
      */
@@ -63,6 +69,9 @@
 
         default void onPowerSaveChanged(boolean isPowerSave) {
         }
+
+        default void onReverseChanged(boolean isReverse, int level, String name) {
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index d3e6f53..35954d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -59,13 +59,13 @@
 
     private final EnhancedEstimates mEstimates;
     private final BroadcastDispatcher mBroadcastDispatcher;
-    private final ArrayList<BatteryController.BatteryStateChangeCallback>
+    protected final ArrayList<BatteryController.BatteryStateChangeCallback>
             mChangeCallbacks = new ArrayList<>();
     private final ArrayList<EstimateFetchCompletion> mFetchCallbacks = new ArrayList<>();
     private final PowerManager mPowerManager;
     private final Handler mMainHandler;
     private final Handler mBgHandler;
-    private final Context mContext;
+    protected final Context mContext;
 
     private int mLevel;
     private boolean mPluggedIn;
@@ -80,7 +80,7 @@
 
     @VisibleForTesting
     @Inject
-    BatteryControllerImpl(Context context, EnhancedEstimates enhancedEstimates,
+    protected BatteryControllerImpl(Context context, EnhancedEstimates enhancedEstimates,
             PowerManager powerManager, BroadcastDispatcher broadcastDispatcher,
             @Main Handler mainHandler, @Background Handler bgHandler) {
         mContext = context;
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..73ffe42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java
@@ -43,7 +43,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 +97,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;
@@ -116,10 +134,14 @@
     }
 
     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 +159,13 @@
             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;
         }
     }
 
     private void onDoneRecording(String packageName) {
-        if (!mAudioRecordingApps.remove(packageName)) {
+        if (!mActiveAudioRecordingPackages.remove(packageName)) {
             // Was not marked as an active recorder, do nothing
             return;
         }
@@ -151,7 +173,8 @@
         // 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();
         }
     }
@@ -303,11 +326,12 @@
     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();
         }
     }
@@ -326,6 +350,12 @@
         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());
+        }
     }
 
     private void startPulsatingAnimation() {
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 30db37c..f31f8eb 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -43,6 +43,7 @@
 import org.json.JSONException;
 import org.json.JSONObject;
 
+import java.util.Collection;
 import java.util.Map;
 import java.util.Set;
 
@@ -101,7 +102,7 @@
                 new ContentObserver(mBgHandler) {
 
                     @Override
-                    public void onChange(boolean selfChange, Iterable<Uri> uris, int flags,
+                    public void onChange(boolean selfChange, Collection<Uri> uris, int flags,
                             int userId) {
                         if (DEBUG) Log.d(TAG, "Overlay changed for user: " + userId);
                         if (ActivityManager.getCurrentUser() == userId) {
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index b2a5f5b..2452218 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -262,7 +262,8 @@
         }
 
         @Override
-        public void onChange(boolean selfChange, Iterable<Uri> uris, int flags, int userId) {
+        public void onChange(boolean selfChange, java.util.Collection<Uri> uris,
+                int flags, int userId) {
             if (userId == ActivityManager.getCurrentUser()) {
                 for (Uri u : uris) {
                     reloadSetting(u);
diff --git a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt
index 2276ba1..812a1e4 100644
--- a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt
@@ -358,7 +358,7 @@
             targetObjectIsStuckTo = targetObjectIsInMagneticFieldOf
             cancelAnimations()
             magnetListener.onStuckToTarget(targetObjectIsInMagneticFieldOf!!)
-            animateStuckToTarget(targetObjectIsInMagneticFieldOf!!, velX, velY, false)
+            animateStuckToTarget(targetObjectIsInMagneticFieldOf, velX, velY, false)
 
             vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK)
         } else if (targetObjectIsInMagneticFieldOf == null && objectStuckToTarget) {
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 977d0bb..78160c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -45,11 +45,7 @@
 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;
@@ -65,12 +61,14 @@
 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;
@@ -229,17 +227,15 @@
         mZenModeConfig.suppressedVisualEffects = 0;
         when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
 
-        TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
-                new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
-                        mock(PowerManager.class),
-                        mock(IDreamManager.class),
-                        mock(AmbientDisplayConfiguration.class),
+        TestableNotificationInterruptionStateProvider interruptionStateProvider =
+                new TestableNotificationInterruptionStateProvider(mContext,
                         mock(NotificationFilter.class),
                         mock(StatusBarStateController.class),
-                        mock(BatteryController.class),
-                        mock(HeadsUpManager.class),
-                        mock(Handler.class)
-                );
+                        mock(BatteryController.class));
+        interruptionStateProvider.setUpWithPresenter(
+                mock(NotificationPresenter.class),
+                mock(HeadsUpManager.class),
+                mock(NotificationInterruptionStateProvider.HeadsUpSuppressor.class));
         mBubbleData = new BubbleData(mContext);
         when(mFeatureFlagsOldPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
         mBubbleController = new TestableBubbleController(
@@ -403,7 +399,8 @@
         // Switch which bubble is expanded
         mBubbleController.selectBubble(mRow.getEntry().getKey());
         mBubbleData.setExpanded(true);
-        assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry());
+        assertEquals(mRow.getEntry(),
+                mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry());
         assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
                 mRow.getEntry()));
 
@@ -496,21 +493,25 @@
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
 
         // Last added is the one that is expanded
-        assertEquals(mRow2.getEntry(), stackView.getExpandedBubble().getEntry());
+        assertEquals(mRow2.getEntry(),
+                mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry());
         assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
                 mRow2.getEntry()));
 
         // Dismiss currently expanded
-        mBubbleController.removeBubble(stackView.getExpandedBubble().getEntry(),
+        mBubbleController.removeBubble(
+                mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry(),
                 BubbleController.DISMISS_USER_GESTURE);
         verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
 
         // Make sure first bubble is selected
-        assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry());
+        assertEquals(mRow.getEntry(),
+                mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry());
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
 
         // Dismiss that one
-        mBubbleController.removeBubble(stackView.getExpandedBubble().getEntry(),
+        mBubbleController.removeBubble(
+                mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry(),
                 BubbleController.DISMISS_USER_GESTURE);
 
         // Make sure state changes and collapse happens
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 7fc83da..5ef4cd2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -41,11 +41,7 @@
 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,10 +57,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.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;
@@ -132,19 +130,15 @@
     private KeyguardBypassController mKeyguardBypassController;
     @Mock
     private FloatingContentCoordinator mFloatingContentCoordinator;
-
     @Captor
     private ArgumentCaptor<NotifCollectionListener> mNotifListenerCaptor;
-
     private TestableBubbleController mBubbleController;
     private NotificationShadeWindowController mNotificationShadeWindowController;
     private NotifCollectionListener mEntryListener;
-
     private NotificationTestHelper mNotificationTestHelper;
     private ExpandableNotificationRow mRow;
     private ExpandableNotificationRow mRow2;
     private ExpandableNotificationRow mNonBubbleNotifRow;
-
     @Mock
     private BubbleController.BubbleStateChangeListener mBubbleStateChangeListener;
     @Mock
@@ -218,17 +212,15 @@
         mZenModeConfig.suppressedVisualEffects = 0;
         when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
 
-        TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
-                new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
-                        mock(PowerManager.class),
-                        mock(IDreamManager.class),
-                        mock(AmbientDisplayConfiguration.class),
+        TestableNotificationInterruptionStateProvider interruptionStateProvider =
+                new TestableNotificationInterruptionStateProvider(mContext,
                         mock(NotificationFilter.class),
                         mock(StatusBarStateController.class),
-                        mock(BatteryController.class),
-                        mock(HeadsUpManager.class),
-                        mock(Handler.class)
-                );
+                        mock(BatteryController.class));
+        interruptionStateProvider.setUpWithPresenter(
+                mock(NotificationPresenter.class),
+                mock(HeadsUpManager.class),
+                mock(NotificationInterruptionStateProvider.HeadsUpSuppressor.class));
         mBubbleData = new BubbleData(mContext);
         when(mFeatureFlagsNewPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(true);
         mBubbleController = new TestableBubbleController(
@@ -313,7 +305,7 @@
         verify(mNotifCallback, times(1)).invalidateNotifications(anyString());
         assertNotNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
         mBubbleController.updateBubble(mRow2.getEntry());
-        verify(mNotifCallback,  times(2)).invalidateNotifications(anyString());
+        verify(mNotifCallback, times(2)).invalidateNotifications(anyString());
         assertNotNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey()));
         assertTrue(mBubbleController.hasBubbles());
 
@@ -383,7 +375,8 @@
         // Switch which bubble is expanded
         mBubbleController.selectBubble(mRow.getEntry().getKey());
         mBubbleData.setExpanded(true);
-        assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry());
+        assertEquals(mRow.getEntry(),
+                mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry());
         assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
                 mRow.getEntry()));
 
@@ -475,21 +468,25 @@
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
 
         // Last added is the one that is expanded
-        assertEquals(mRow2.getEntry(), stackView.getExpandedBubble().getEntry());
+        assertEquals(mRow2.getEntry(),
+                mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry());
         assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
                 mRow2.getEntry()));
 
         // Dismiss currently expanded
-        mBubbleController.removeBubble(stackView.getExpandedBubble().getEntry(),
+        mBubbleController.removeBubble(
+                mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry(),
                 BubbleController.DISMISS_USER_GESTURE);
         verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
 
         // Make sure first bubble is selected
-        assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry());
+        assertEquals(mRow.getEntry(),
+                mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry());
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
 
         // Dismiss that one
-        mBubbleController.removeBubble(stackView.getExpandedBubble().getEntry(),
+        mBubbleController.removeBubble(
+                mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry(),
                 BubbleController.DISMISS_USER_GESTURE);
 
         // Make sure state changes and collapse happens
@@ -649,7 +646,7 @@
     }
 
     @Test
-    public void removeBubble_intercepted()  {
+    public void removeBubble_intercepted() {
         mEntryListener.onEntryAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
index d3d90c4..de1fb41 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,
-            NotificationInterruptStateProvider interruptionStateProvider,
+            NotificationInterruptionStateProvider 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
deleted file mode 100644
index 17dc76b..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptStateProviderImpl.java
+++ /dev/null
@@ -1,55 +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.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
new file mode 100644
index 0000000..5d192b2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptionStateProvider.java
@@ -0,0 +1,35 @@
+/*
+ * 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/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
index a8c4851..c70c56a 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
@@ -234,7 +234,7 @@
             loaded = true
             assertEquals(1, controls.size)
             val controlStatus = controls[0]
-            assertEquals(ControlStatus(control, false), controlStatus)
+            assertEquals(ControlStatus(control, TEST_COMPONENT, false), controlStatus)
 
             assertTrue(favorites.isEmpty())
             assertFalse(data.errorOnLoad)
@@ -265,10 +265,10 @@
             loaded = true
             assertEquals(2, controls.size)
             val controlStatus = controls.first { it.control.controlId == TEST_CONTROL_ID }
-            assertEquals(ControlStatus(control, true), controlStatus)
+            assertEquals(ControlStatus(control, TEST_COMPONENT, true), controlStatus)
 
             val controlStatus2 = controls.first { it.control.controlId == TEST_CONTROL_ID_2 }
-            assertEquals(ControlStatus(control2, false), controlStatus2)
+            assertEquals(ControlStatus(control2, TEST_COMPONENT, false), controlStatus2)
 
             assertEquals(1, favorites.size)
             assertEquals(TEST_CONTROL_ID, favorites[0])
@@ -521,7 +521,7 @@
     }
 
     @Test
-    fun testReplaceFavoritesForStructure_noFavorites() {
+    fun testReplaceFavoritesForStructure_noExistingFavorites() {
         controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO)
         delayableExecutor.runAllReady()
 
@@ -531,6 +531,16 @@
     }
 
     @Test
+    fun testReplaceFavoritesForStructure_doNotStoreEmptyStructure() {
+        controller.replaceFavoritesForStructure(
+            StructureInfo(TEST_COMPONENT, "Home", emptyList<ControlInfo>()))
+        delayableExecutor.runAllReady()
+
+        assertEquals(0, controller.countFavoritesForComponent(TEST_COMPONENT))
+        assertEquals(emptyList<ControlInfo>(), controller.getFavoritesForComponent(TEST_COMPONENT))
+    }
+
+    @Test
     fun testReplaceFavoritesForStructure_differentComponentsAreFilteredOut() {
         controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO)
         controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO_2)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
index 68e1ec1..133df2a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.controls.management
 
 import android.app.PendingIntent
+import android.content.ComponentName
 import android.service.controls.Control
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
@@ -78,6 +79,7 @@
                     Control.StatelessBuilder("$idPrefix$it", pendingIntent)
                             .setZone(zoneMap(it))
                             .build(),
+                    ComponentName("", ""),
                     it in favoritesIndices
             )
         }
@@ -189,4 +191,4 @@
             assertTrue(sameControl(it.first, it.second))
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
index 85e937e..13a7708 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
@@ -30,7 +30,6 @@
 import com.android.systemui.util.time.FakeSystemClock
 import org.junit.After
 import org.junit.Assert.assertEquals
-import org.junit.Assert.assertTrue
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -38,6 +37,7 @@
 import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.Mockito.`when`
+import org.mockito.Mockito.inOrder
 import org.mockito.Mockito.never
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
@@ -98,74 +98,18 @@
     }
 
     @Test
+    fun testInitialStateListening() {
+        verify(mockSL).setListening(true)
+        verify(mockSL).reload()
+    }
+
+    @Test
     fun testStartsOnUser() {
         assertEquals(user, controller.currentUserId)
     }
 
     @Test
-    fun testNoServices_notListening() {
-        assertTrue(controller.getCurrentServices().isEmpty())
-    }
-
-    @Test
-    fun testStartListening_onFirstCallback() {
-        controller.addCallback(mockCallback)
-        executor.runAllReady()
-
-        verify(mockSL).setListening(true)
-    }
-
-    @Test
-    fun testStartListening_onlyOnce() {
-        controller.addCallback(mockCallback)
-        controller.addCallback(mockCallbackOther)
-
-        executor.runAllReady()
-
-        verify(mockSL).setListening(true)
-    }
-
-    @Test
-    fun testStopListening_callbackRemoved() {
-        controller.addCallback(mockCallback)
-
-        executor.runAllReady()
-
-        controller.removeCallback(mockCallback)
-
-        executor.runAllReady()
-
-        verify(mockSL).setListening(false)
-    }
-
-    @Test
-    fun testStopListening_notWhileRemainingCallbacks() {
-        controller.addCallback(mockCallback)
-        controller.addCallback(mockCallbackOther)
-
-        executor.runAllReady()
-
-        controller.removeCallback(mockCallback)
-
-        executor.runAllReady()
-
-        verify(mockSL, never()).setListening(false)
-    }
-
-    @Test
-    fun testReloadOnFirstCallbackAdded() {
-        controller.addCallback(mockCallback)
-        executor.runAllReady()
-
-        verify(mockSL).reload()
-    }
-
-    @Test
     fun testCallbackCalledWhenAdded() {
-        `when`(mockSL.reload()).then {
-            serviceListingCallbackCaptor.value.onServicesReloaded(emptyList())
-        }
-
         controller.addCallback(mockCallback)
         executor.runAllReady()
         verify(mockCallback).onServicesUpdated(any())
@@ -209,5 +153,11 @@
         controller.changeUser(UserHandle.of(otherUser))
         executor.runAllReady()
         assertEquals(otherUser, controller.currentUserId)
+
+        val inOrder = inOrder(mockSL)
+        inOrder.verify(mockSL).setListening(false)
+        inOrder.verify(mockSL).addCallback(any()) // We add a callback because we replaced the SL
+        inOrder.verify(mockSL).setListening(true)
+        inOrder.verify(mockSL).reload()
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoriteModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoriteModelTest.kt
index 9ffc29e..c330b38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoriteModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoriteModelTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.controls.management
 
 import android.app.PendingIntent
+import android.content.ComponentName
 import android.service.controls.Control
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
@@ -70,6 +71,7 @@
                     Control.StatelessBuilder("$idPrefix$it", pendingIntent)
                             .setZone((it % 3).toString())
                             .build(),
+                    ComponentName("", ""),
                     it in favoritesIndices
             )
         }
@@ -195,4 +197,4 @@
 
         verifyNoMoreInteractions(allAdapter, favoritesAdapter)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java
similarity index 63%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java
index f9c62e1..1693e7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui.statusbar.notification.interruption;
+package com.android.systemui.statusbar;
 
 
 import static android.app.Notification.FLAG_BUBBLE;
@@ -30,14 +30,15 @@
 
 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;
@@ -49,6 +50,7 @@
 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;
@@ -66,7 +68,7 @@
  */
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
-public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
+public class NotificationInterruptionStateProviderTest extends SysuiTestCase {
 
     @Mock
     PowerManager mPowerManager;
@@ -79,36 +81,38 @@
     @Mock
     StatusBarStateController mStatusBarStateController;
     @Mock
+    NotificationPresenter mPresenter;
+    @Mock
     HeadsUpManager mHeadsUpManager;
     @Mock
-    BatteryController mBatteryController;
+    NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor;
     @Mock
-    Handler mMockHandler;
+    BatteryController mBatteryController;
 
-    private NotificationInterruptStateProviderImpl mNotifInterruptionStateProvider;
+    private NotificationInterruptionStateProvider mNotifInterruptionStateProvider;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
 
         mNotifInterruptionStateProvider =
-                new NotificationInterruptStateProviderImpl(
-                        mContext.getContentResolver(),
+                new TestableNotificationInterruptionStateProvider(mContext,
                         mPowerManager,
                         mDreamManager,
                         mAmbientDisplayConfiguration,
                         mNotificationFilter,
-                        mBatteryController,
                         mStatusBarStateController,
-                        mHeadsUpManager,
-                        mMockHandler);
+                        mBatteryController);
 
-        mNotifInterruptionStateProvider.mUseHeadsUp = true;
+        mNotifInterruptionStateProvider.setUpWithPresenter(
+                mPresenter,
+                mHeadsUpManager,
+                mHeadsUpSuppressor);
     }
 
     /**
      * Sets up the state such that any requests to
-     * {@link NotificationInterruptStateProviderImpl#canAlertCommon(NotificationEntry)} will
+     * {@link NotificationInterruptionStateProvider#canAlertCommon(NotificationEntry)} will
      * pass as long its provided NotificationEntry fulfills group suppression check.
      */
     private void ensureStateForAlertCommon() {
@@ -117,16 +121,17 @@
 
     /**
      * Sets up the state such that any requests to
-     * {@link NotificationInterruptStateProviderImpl#canAlertAwakeCommon(NotificationEntry)} will
+     * {@link NotificationInterruptionStateProvider#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 NotificationInterruptStateProviderImpl#shouldHeadsUp(NotificationEntry)} will
+     * {@link NotificationInterruptionStateProvider#shouldHeadsUp(NotificationEntry)} will
      * pass as long its provided NotificationEntry fulfills importance & DND checks.
      */
     private void ensureStateForHeadsUpWhenAwake() throws RemoteException {
@@ -136,11 +141,12 @@
         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 NotificationInterruptStateProviderImpl#shouldHeadsUp(NotificationEntry)} will
+     * {@link NotificationInterruptionStateProvider#shouldHeadsUp(NotificationEntry)} will
      * pass as long its provided NotificationEntry fulfills importance & DND checks.
      */
     private void ensureStateForHeadsUpWhenDozing() {
@@ -152,7 +158,7 @@
 
     /**
      * Sets up the state such that any requests to
-     * {@link NotificationInterruptStateProviderImpl#shouldBubbleUp(NotificationEntry)} will
+     * {@link NotificationInterruptionStateProvider#shouldBubbleUp(NotificationEntry)} will
      * pass as long its provided NotificationEntry fulfills importance & bubble checks.
      */
     private void ensureStateForBubbleUp() {
@@ -160,53 +166,75 @@
         ensureStateForAlertAwakeCommon();
     }
 
+    /**
+     * Ensure that the disabled state is set correctly.
+     */
     @Test
-    public void testDefaultSuppressorDoesNotSuppress() {
-        // GIVEN a suppressor without any overrides
-        final NotificationInterruptSuppressor defaultSuppressor =
-                new NotificationInterruptSuppressor() {
-                    @Override
-                    public String getName() {
-                        return "defaultSuppressor";
-                    }
-                };
+    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();
 
         NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
-
-        // THEN this suppressor doesn't suppress anything by default
-        assertThat(defaultSuppressor.suppressAwakeHeadsUp(entry)).isFalse();
-        assertThat(defaultSuppressor.suppressAwakeInterruptions(entry)).isFalse();
-        assertThat(defaultSuppressor.suppressInterruptions(entry)).isFalse();
+        assertThat(mNotifInterruptionStateProvider.canAlertCommon(entry)).isTrue();
     }
 
+    /**
+     * Filtered out notifications don't alert.
+     */
     @Test
-    public void testShouldHeadsUpAwake() throws RemoteException {
-        ensureStateForHeadsUpWhenAwake();
+    public void testCanAlertCommon_false_filteredOut() {
+        ensureStateForAlertCommon();
+        when(mNotificationFilter.shouldFilterOut(any())).thenReturn(true);
 
-        NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
-        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+        NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
+        assertThat(mNotifInterruptionStateProvider.canAlertCommon(entry)).isFalse();
     }
 
+    /**
+     * 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 testShouldNotHeadsUpAwake_flteredOut() throws RemoteException {
-        // GIVEN state for "heads up when awake" is true
-        ensureStateForHeadsUpWhenAwake();
+    public void testCanAlertCommon_false_suppressedForGroups() {
+        ensureStateForAlertCommon();
 
-        // 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")
@@ -219,40 +247,40 @@
                 .setImportance(IMPORTANCE_DEFAULT)
                 .build();
 
-        // THEN this entry shouldn't HUN
-        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
+        assertThat(mNotifInterruptionStateProvider.canAlertCommon(entry)).isFalse();
     }
 
+    /**
+     * HUNs while dozing can happen.
+     */
     @Test
-    public void testShouldHeadsUpWhenDozing() {
+    public void testShouldHeadsUpWhenDozing_true() {
         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 testShouldNotHeadsUpWhenDozing_pulseDisabled() {
-        // GIVEN state for "heads up when dozing" is true
+    public void testShouldHeadsUpWhenDozing_false_pulseDisabled() {
         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 testShouldNotHeadsUpWhenDozing_notDozing() {
-        // GIVEN state for "heads up when dozing" is true
+    public void testShouldHeadsUpWhenDozing_false_notDozing() {
         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();
     }
@@ -262,7 +290,7 @@
      * {@link android.app.NotificationManager.Policy#SUPPRESSED_EFFECT_AMBIENT}.
      */
     @Test
-    public void testShouldNotHeadsUpWhenDozing_suppressingAmbient() {
+    public void testShouldHeadsUpWhenDozing_false_suppressingAmbient() {
         ensureStateForHeadsUpWhenDozing();
 
         NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
@@ -273,18 +301,23 @@
         assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
     }
 
+    /**
+     * Notifications that are < {@link android.app.NotificationManager#IMPORTANCE_DEFAULT} don't
+     * get to pulse.
+     */
     @Test
-    public void testShouldNotHeadsUpWhenDozing_lessImportant() {
+    public void testShouldHeadsUpWhenDozing_false_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() throws RemoteException {
+    public void testShouldHeadsUp_true() throws RemoteException {
         ensureStateForHeadsUpWhenAwake();
 
         NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
@@ -292,11 +325,38 @@
     }
 
     /**
+     * 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 testShouldNotHeadsUp_bubble() throws RemoteException {
+    public void testShouldHeadsUp_false_bubble() throws RemoteException {
         ensureStateForHeadsUpWhenAwake();
 
         // Bubble bit only applies to interruption when we're in the shade
@@ -309,7 +369,7 @@
      * If we're not allowed to alert in general, we shouldn't be shown as heads up.
      */
     @Test
-    public void testShouldNotHeadsUp_filtered() throws RemoteException {
+    public void testShouldHeadsUp_false_alertCommonFalse() throws RemoteException {
         ensureStateForHeadsUpWhenAwake();
         // Make canAlertCommon false by saying it's filtered out
         when(mNotificationFilter.shouldFilterOut(any())).thenReturn(true);
@@ -323,7 +383,7 @@
      * {@link android.app.NotificationManager.Policy#SUPPRESSED_EFFECT_PEEK}.
      */
     @Test
-    public void testShouldNotHeadsUp_suppressPeek() throws RemoteException {
+    public void testShouldHeadsUp_false_suppressPeek() throws RemoteException {
         ensureStateForHeadsUpWhenAwake();
 
         NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
@@ -339,7 +399,7 @@
      * to show as a heads up.
      */
     @Test
-    public void testShouldNotHeadsUp_lessImportant() throws RemoteException {
+    public void testShouldHeadsUp_false_lessImportant() throws RemoteException {
         ensureStateForHeadsUpWhenAwake();
 
         NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
@@ -350,7 +410,7 @@
      * If the device is not in use then we shouldn't be shown as heads up.
      */
     @Test
-    public void testShouldNotHeadsUp_deviceNotInUse() throws RemoteException {
+    public void testShouldHeadsUp_false_deviceNotInUse() throws RemoteException {
         ensureStateForHeadsUpWhenAwake();
         NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
 
@@ -364,58 +424,61 @@
         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 testShouldNotHeadsUp_headsUpSuppressed() throws RemoteException {
+    public void testShouldHeadsUp_false_suppressed() throws RemoteException {
         ensureStateForHeadsUpWhenAwake();
-
-        // If a suppressor is suppressing heads up, then it shouldn't be shown as a heads up.
-        mNotifInterruptionStateProvider.addSuppressor(mSuppressAwakeHeadsUp);
+        when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(false);
 
         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 testShouldNotHeadsUpAwake_awakeInterruptsSuppressed() throws RemoteException {
-        ensureStateForHeadsUpWhenAwake();
+    public void testCanAlertAwakeCommon__false_vrMode() {
+        ensureStateForAlertAwakeCommon();
+        when(mPresenter.isDeviceInVrMode()).thenReturn(true);
 
-        // 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();
+        NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
+        assertThat(mNotifInterruptionStateProvider.canAlertAwakeCommon(entry)).isFalse();
     }
 
     /**
      * On screen alerts don't happen when the notification is snoozed.
      */
     @Test
-    public void testShouldNotHeadsUp_snoozedPackage() {
-        NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
+    public void testCanAlertAwakeCommon_false_snoozedPackage() {
         ensureStateForAlertAwakeCommon();
+        when(mHeadsUpManager.isSnoozed(any())).thenReturn(true);
 
-        when(mHeadsUpManager.isSnoozed(entry.getSbn().getPackageName())).thenReturn(true);
-
-        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
+        NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
+        assertThat(mNotifInterruptionStateProvider.canAlertAwakeCommon(entry)).isFalse();
     }
 
-
+    /**
+     * On screen alerts don't happen when that package has just launched fullscreen.
+     */
     @Test
-    public void testShouldNotHeadsUp_justLaunchedFullscreen() {
+    public void testCanAlertAwakeCommon_false_justLaunchedFullscreen() {
         ensureStateForAlertAwakeCommon();
 
-        // On screen alerts don't happen when that package has just launched fullscreen.
         NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
         entry.notifyFullScreenIntentLaunched();
 
-        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
+        assertThat(mNotifInterruptionStateProvider.canAlertAwakeCommon(entry)).isFalse();
     }
 
     /**
      * Bubbles can happen.
      */
     @Test
-    public void testShouldBubbleUp() {
+    public void testShouldBubbleUp_true() {
         ensureStateForBubbleUp();
         assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(createBubble())).isTrue();
     }
@@ -424,7 +487,7 @@
      * If the notification doesn't have permission to bubble, it shouldn't bubble.
      */
     @Test
-    public void shouldNotBubbleUp_notAllowedToBubble() {
+    public void shouldBubbleUp_false_notAllowedToBubble() {
         ensureStateForBubbleUp();
 
         NotificationEntry entry = createBubble();
@@ -439,7 +502,7 @@
      * If the notification isn't a bubble, it should definitely not show as a bubble.
      */
     @Test
-    public void shouldNotBubbleUp_notABubble() {
+    public void shouldBubbleUp_false_notABubble() {
         ensureStateForBubbleUp();
 
         NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
@@ -454,7 +517,7 @@
      * If the notification doesn't have bubble metadata, it shouldn't bubble.
      */
     @Test
-    public void shouldNotBubbleUp_invalidMetadata() {
+    public void shouldBubbleUp_false_invalidMetadata() {
         ensureStateForBubbleUp();
 
         NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
@@ -466,18 +529,24 @@
         assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(entry)).isFalse();
     }
 
+    /**
+     * If the notification can't heads up in general, it shouldn't bubble.
+     */
     @Test
-    public void shouldNotBubbleUp_suppressedInterruptions() {
+    public void shouldBubbleUp_false_alertAwakeCommonFalse() {
         ensureStateForBubbleUp();
 
-        // If the notification can't heads up in general, it shouldn't bubble.
-        mNotifInterruptionStateProvider.addSuppressor(mSuppressInterruptions);
+        // Make alert common return false by pretending we're in VR mode
+        when(mPresenter.isDeviceInVrMode()).thenReturn(true);
 
         assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(createBubble())).isFalse();
     }
 
+    /**
+     * If the notification can't heads up in general, it shouldn't bubble.
+     */
     @Test
-    public void shouldNotBubbleUp_filteredOut() {
+    public void shouldBubbleUp_false_alertCommonFalse() {
         ensureStateForBubbleUp();
 
         // Make canAlertCommon false by saying it's filtered out
@@ -523,45 +592,20 @@
                 .build();
     }
 
-    private final NotificationInterruptSuppressor
-            mSuppressAwakeHeadsUp =
-            new NotificationInterruptSuppressor() {
-        @Override
-        public String getName() {
-            return "suppressAwakeHeadsUp";
-        }
+    /**
+     * Testable class overriding constructor.
+     */
+    public static class TestableNotificationInterruptionStateProvider extends
+            NotificationInterruptionStateProvider {
 
-        @Override
-        public boolean suppressAwakeHeadsUp(NotificationEntry entry) {
-            return true;
+        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
-            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/SbnBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java
index 62f406f..1b0ed11 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java
@@ -22,6 +22,8 @@
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 
+import com.android.internal.logging.InstanceId;
+
 /**
  * Convenience builder for {@link StatusBarNotification} since its constructor is terrifying.
  *
@@ -40,6 +42,7 @@
     private UserHandle mUser = UserHandle.of(0);
     private String mOverrideGroupKey;
     private long mPostTime;
+    private InstanceId mInstanceId;
 
     public SbnBuilder() {
     }
@@ -55,6 +58,7 @@
         mUser = source.getUser();
         mOverrideGroupKey = source.getOverrideGroupKey();
         mPostTime = source.getPostTime();
+        mInstanceId = source.getInstanceId();
     }
 
     public StatusBarNotification build() {
@@ -71,7 +75,7 @@
             notification.setBubbleMetadata(mBubbleMetadata);
         }
 
-        return new StatusBarNotification(
+        StatusBarNotification result = new StatusBarNotification(
                 mPkg,
                 mOpPkg,
                 mId,
@@ -82,6 +86,10 @@
                 mUser,
                 mOverrideGroupKey,
                 mPostTime);
+        if (mInstanceId != null) {
+            result.setInstanceId(mInstanceId);
+        }
+        return result;
     }
 
     public SbnBuilder setPkg(String pkg) {
@@ -175,4 +183,9 @@
         mBubbleMetadata = data;
         return this;
     }
+
+    public SbnBuilder setInstanceId(InstanceId instanceId) {
+        mInstanceId = instanceId;
+        return this;
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
index 92a9080..261dc82 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
@@ -26,6 +26,7 @@
 import android.service.notification.SnoozeCriterion;
 import android.service.notification.StatusBarNotification;
 
+import com.android.internal.logging.InstanceId;
 import com.android.systemui.statusbar.RankingBuilder;
 import com.android.systemui.statusbar.SbnBuilder;
 
@@ -141,6 +142,11 @@
         return this;
     }
 
+    public NotificationEntryBuilder setInstanceId(InstanceId instanceId) {
+        mSbnBuilder.setInstanceId(instanceId);
+        return this;
+    }
+
     /* Delegated to Notification.Builder (via SbnBuilder) */
 
     public NotificationEntryBuilder setContentTitle(Context context, String contentTitle) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index d826ce1..d39b2c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -16,7 +16,10 @@
 
 package com.android.systemui.statusbar.notification.logging;
 
+import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING;
+
 import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
@@ -34,6 +37,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.logging.InstanceId;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.SysuiTestCase;
@@ -43,6 +47,7 @@
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.logging.nano.Notifications;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.util.concurrency.FakeExecutor;
@@ -81,9 +86,10 @@
 
     private NotificationEntry mEntry;
     private TestableNotificationLogger mLogger;
-    private NotificationEntryListener mNotificationEntryListener;
     private ConcurrentLinkedQueue<AssertionError> mErrorQueue = new ConcurrentLinkedQueue<>();
     private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
+    private NotificationPanelLoggerFake mNotificationPanelLoggerFake =
+            new NotificationPanelLoggerFake();
 
     @Before
     public void setUp() {
@@ -97,6 +103,7 @@
                 .setUid(TEST_UID)
                 .setNotification(new Notification())
                 .setUser(UserHandle.CURRENT)
+                .setInstanceId(InstanceId.fakeInstanceId(1))
                 .build();
         mEntry.setRow(mRow);
 
@@ -105,7 +112,6 @@
                 mExpansionStateLogger);
         mLogger.setUpWithContainer(mListContainer);
         verify(mEntryManager).addNotificationEntryListener(mEntryListenerCaptor.capture());
-        mNotificationEntryListener = mEntryListenerCaptor.getValue();
     }
 
     @Test
@@ -164,6 +170,41 @@
         verify(mBarService, times(1)).onNotificationVisibilityChanged(any(), any());
     }
 
+    @Test
+    public void testLogPanelShownOnLoggingStart() {
+        when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
+        mLogger.startNotificationLogging();
+        assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
+        assertEquals(false, mNotificationPanelLoggerFake.get(0).isLockscreen);
+        assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length);
+        Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0];
+        assertEquals(TEST_PACKAGE_NAME, n.packageName);
+        assertEquals(TEST_UID, n.uid);
+        assertEquals(1, n.instanceId);
+        assertEquals(false, n.isGroupSummary);
+        assertEquals(1 + BUCKET_ALERTING, n.section);
+    }
+
+    @Test
+    public void testLogPanelShownHandlesNullInstanceIds() {
+        // Construct a NotificationEntry like mEntry, but with a null instance id.
+        NotificationEntry entry = new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE_NAME)
+                .setOpPkg(TEST_PACKAGE_NAME)
+                .setUid(TEST_UID)
+                .setNotification(new Notification())
+                .setUser(UserHandle.CURRENT)
+                .build();
+        entry.setRow(mRow);
+
+        when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(entry));
+        mLogger.startNotificationLogging();
+        assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
+        assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length);
+        Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0];
+        assertEquals(0, n.instanceId);
+    }
+
     private class TestableNotificationLogger extends NotificationLogger {
 
         TestableNotificationLogger(NotificationListener notificationListener,
@@ -173,7 +214,7 @@
                 IStatusBarService barService,
                 ExpansionStateLogger expansionStateLogger) {
             super(notificationListener, uiBgExecutor, entryManager, statusBarStateController,
-                    expansionStateLogger);
+                    expansionStateLogger, mNotificationPanelLoggerFake);
             mBarService = barService;
             // Make this on the current thread so we can wait for it during tests.
             mHandler = Handler.createAsync(Looper.myLooper());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLoggerFake.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLoggerFake.java
new file mode 100644
index 0000000..7e97629
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLoggerFake.java
@@ -0,0 +1,51 @@
+/*
+ * 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.logging;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.logging.nano.Notifications;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class NotificationPanelLoggerFake implements NotificationPanelLogger {
+    private List<CallRecord> mCalls = new ArrayList<>();
+
+    List<CallRecord> getCalls() {
+        return mCalls;
+    }
+
+    CallRecord get(int index) {
+        return mCalls.get(index);
+    }
+
+    @Override
+    public void logPanelShown(boolean isLockscreen,
+            List<NotificationEntry> visibleNotifications) {
+        mCalls.add(new CallRecord(isLockscreen,
+                NotificationPanelLogger.toNotificationProto(visibleNotifications)));
+    }
+
+    public static class CallRecord {
+        public boolean isLockscreen;
+        public Notifications.NotificationList list;
+        CallRecord(boolean isLockscreen, Notifications.NotificationList list) {
+            this.isLockscreen = isLockscreen;
+            this.list = list;
+        }
+    }
+}
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 a21a047..5d0349d 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 NotificationInterruptStateProvider mNotificationInterruptionStateProvider;
+    @Mock private NotificationInterruptionStateProvider 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/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index b9c5b7c..1e4df27 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(NotificationInterruptStateProvider.class), mock(MetricsLogger.class),
+                mock(NotificationInterruptionStateProvider.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 318e9b8..b9d2d22 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,9 +16,8 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.Notification;
@@ -36,7 +35,6 @@
 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;
@@ -50,12 +48,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;
@@ -64,7 +62,6 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
 
 import java.util.ArrayList;
 
@@ -75,9 +72,6 @@
 
 
     private StatusBarNotificationPresenter mStatusBarNotificationPresenter;
-    private NotificationInterruptStateProvider mNotificationInterruptStateProvider =
-            mock(NotificationInterruptStateProvider.class);
-    private NotificationInterruptSuppressor mInterruptSuppressor;
     private CommandQueue mCommandQueue;
     private FakeMetricsLogger mMetricsLogger;
     private ShadeController mShadeController = mock(ShadeController.class);
@@ -101,11 +95,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<>());
@@ -113,25 +107,18 @@
         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(KeyguardStateController.class),
+                mock(NotificationAlertingManager.class), mock(KeyguardStateController.class),
                 mock(KeyguardIndicationController.class), mStatusBar,
-                mock(ShadeControllerImpl.class), mCommandQueue, mInitController,
-                mNotificationInterruptStateProvider);
-        mInitController.executePostInitTasks();
-        ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor =
-                ArgumentCaptor.forClass(NotificationInterruptSuppressor.class);
-        verify(mNotificationInterruptStateProvider).addSuppressor(suppressorCaptor.capture());
-        mInterruptSuppressor = suppressorCaptor.getValue();
+                mock(ShadeControllerImpl.class), mCommandQueue, mInitController);
     }
 
     @Test
-    public void testSuppressHeadsUp_disabledStatusBar() {
+    public void testHeadsUp_disabledStatusBar() {
         Notification n = new Notification.Builder(getContext(), "a").build();
         NotificationEntry entry = new NotificationEntryBuilder()
                 .setPkg("a")
@@ -143,12 +130,12 @@
                 false /* animate */);
         TestableLooper.get(this).processAllMessages();
 
-        assertTrue("The panel should suppress heads up while disabled",
-                mInterruptSuppressor.suppressAwakeHeadsUp(entry));
+        assertFalse("The panel shouldn't allow heads up while disabled",
+                mStatusBarNotificationPresenter.canHeadsUp(entry, entry.getSbn()));
     }
 
     @Test
-    public void testSuppressHeadsUp_disabledNotificationShade() {
+    public void testHeadsUp_disabledNotificationShade() {
         Notification n = new Notification.Builder(getContext(), "a").build();
         NotificationEntry entry = new NotificationEntryBuilder()
                 .setPkg("a")
@@ -160,39 +147,8 @@
                 false /* animate */);
         TestableLooper.get(this).processAllMessages();
 
-        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));
+        assertFalse("The panel shouldn't allow heads up while notitifcation shade disabled",
+                mStatusBarNotificationPresenter.canHeadsUp(entry, entry.getSbn()));
     }
 
     @Test
@@ -216,3 +172,4 @@
         }
     }
 }
+
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 d9f4d4b..e407927 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.ContentResolver;
+import android.content.Context;
 import android.content.IntentFilter;
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.hardware.fingerprint.FingerprintManager;
@@ -109,18 +109,20 @@
 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.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;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
@@ -128,7 +130,6 @@
 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;
@@ -160,7 +161,7 @@
     private StatusBar mStatusBar;
     private FakeMetricsLogger mMetricsLogger;
     private PowerManager mPowerManager;
-    private TestableNotificationInterruptStateProviderImpl mNotificationInterruptStateProvider;
+    private TestableNotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
 
     @Mock private NotificationsController mNotificationsController;
     @Mock private LightBarController mLightBarController;
@@ -178,6 +179,7 @@
     @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;
@@ -190,9 +192,10 @@
     @Mock private StatusBarNotificationPresenter mNotificationPresenter;
     @Mock private NotificationEntryListener mEntryListener;
     @Mock private NotificationFilter mNotificationFilter;
-    @Mock private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
+    @Mock private NotificationAlertingManager mNotificationAlertingManager;
     @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;
@@ -260,12 +263,10 @@
         mPowerManager = new PowerManager(mContext, powerManagerService, thermalService,
                 Handler.createAsync(Looper.myLooper()));
 
-        mNotificationInterruptStateProvider =
-                new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
-                        mPowerManager,
+        mNotificationInterruptionStateProvider =
+                new TestableNotificationInterruptionStateProvider(mContext, mPowerManager,
                         mDreamManager, mAmbientDisplayConfiguration, mNotificationFilter,
-                        mStatusBarStateController, mBatteryController, mHeadsUpManager,
-                        new Handler(TestableLooper.get(this).getLooper()));
+                        mStatusBarStateController, mBatteryController);
 
         mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
         mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
@@ -273,7 +274,7 @@
         mMetricsLogger = new FakeMetricsLogger();
         NotificationLogger notificationLogger = new NotificationLogger(mNotificationListener,
                 mUiBgExecutor, mock(NotificationEntryManager.class), mStatusBarStateController,
-                mExpansionStateLogger);
+                mExpansionStateLogger, new NotificationPanelLoggerFake());
         notificationLogger.setVisibilityReporter(mock(Runnable.class));
 
         when(mCommandQueue.asBinder()).thenReturn(new Binder());
@@ -298,6 +299,9 @@
             return null;
         }).when(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any());
 
+        mNotificationInterruptionStateProvider.setUpWithPresenter(mNotificationPresenter,
+                mHeadsUpManager, mHeadsUpSuppressor);
+
         when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
 
         WakefulnessLifecycle wakefulnessLifecycle = new WakefulnessLifecycle();
@@ -344,9 +348,10 @@
                 ),
                 mNotificationGutsManager,
                 notificationLogger,
-                mNotificationInterruptStateProvider,
+                mNotificationInterruptionStateProvider,
                 mNotificationViewHierarchyManager,
                 mKeyguardViewMediator,
+                mNotificationAlertingManager,
                 new DisplayMetrics(),
                 mMetricsLogger,
                 mUiBgExecutor,
@@ -557,6 +562,7 @@
         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")
@@ -572,7 +578,7 @@
                 .setImportance(IMPORTANCE_HIGH)
                 .build();
 
-        assertTrue(mNotificationInterruptStateProvider.shouldHeadsUp(entry));
+        assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
     }
 
     @Test
@@ -581,6 +587,7 @@
         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")
@@ -596,7 +603,7 @@
                 .setImportance(IMPORTANCE_HIGH)
                 .build();
 
-        assertFalse(mNotificationInterruptStateProvider.shouldHeadsUp(entry));
+        assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
     }
 
     @Test
@@ -605,6 +612,7 @@
         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();
 
@@ -617,7 +625,7 @@
                 .setSuppressedVisualEffects(SUPPRESSED_EFFECT_PEEK)
                 .build();
 
-        assertFalse(mNotificationInterruptStateProvider.shouldHeadsUp(entry));
+        assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
     }
 
     @Test
@@ -626,6 +634,7 @@
         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();
 
@@ -637,7 +646,7 @@
                 .setImportance(IMPORTANCE_HIGH)
                 .build();
 
-        assertTrue(mNotificationInterruptStateProvider.shouldHeadsUp(entry));
+        assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
     }
 
     @Test
@@ -863,21 +872,19 @@
         verify(mDozeServiceHost).setDozeSuppressed(false);
     }
 
-    public static class TestableNotificationInterruptStateProviderImpl extends
-            NotificationInterruptStateProviderImpl {
+    public static class TestableNotificationInterruptionStateProvider extends
+            NotificationInterruptionStateProvider {
 
-        TestableNotificationInterruptStateProviderImpl(
-                ContentResolver contentResolver,
+        TestableNotificationInterruptionStateProvider(
+                Context context,
                 PowerManager powerManager,
                 IDreamManager dreamManager,
                 AmbientDisplayConfiguration ambientDisplayConfiguration,
                 NotificationFilter filter,
                 StatusBarStateController controller,
-                BatteryController batteryController,
-                HeadsUpManager headsUpManager,
-                Handler mainHandler) {
-            super(contentResolver, powerManager, dreamManager, ambientDisplayConfiguration, filter,
-                    batteryController, controller, headsUpManager, mainHandler);
+                BatteryController batteryController) {
+            super(context, powerManager, dreamManager, ambientDisplayConfiguration, filter,
+                    batteryController, controller);
             mUseHeadsUp = true;
         }
     }
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/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
index cc36f4a..a402ffa 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
@@ -288,10 +288,18 @@
 
         @Override
         public void setLimit(String iface, long quotaBytes) {
-            mLog.i("setLimit: " + iface + "," + quotaBytes);
             // Listen for all iface is necessary since upstream might be changed after limit
             // is set.
             mHandler.post(() -> {
+                final Long curIfaceQuota = mInterfaceQuotas.get(iface);
+
+                // If the quota is set to unlimited, the value set to HAL is Long.MAX_VALUE,
+                // which is ~8.4 x 10^6 TiB, no one can actually reach it. Thus, it is not
+                // useful to set it multiple times.
+                // Otherwise, the quota needs to be updated to tell HAL to re-count from now even
+                // if the quota is the same as the existing one.
+                if (null == curIfaceQuota && QUOTA_UNLIMITED == quotaBytes) return;
+
                 if (quotaBytes == QUOTA_UNLIMITED) {
                     mInterfaceQuotas.remove(iface);
                 } else {
@@ -323,7 +331,6 @@
 
         @Override
         public void requestStatsUpdate(int token) {
-            mLog.i("requestStatsUpdate: " + token);
             // Do not attempt to update stats by querying the offload HAL
             // synchronously from a different thread than the Handler thread. http://b/64771555.
             mHandler.post(() -> {
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/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 2420e69..e73f9ce 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -37,7 +37,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.service.autofill.Dataset;
-import android.service.autofill.InlinePresentation;
+import android.service.autofill.InlineAction;
 import android.service.autofill.augmented.AugmentedAutofillService;
 import android.service.autofill.augmented.IAugmentedAutofillService;
 import android.service.autofill.augmented.IFillCallback;
@@ -167,7 +167,7 @@
                             new IFillCallback.Stub() {
                                 @Override
                                 public void onSuccess(@Nullable List<Dataset> inlineSuggestionsData,
-                                        @Nullable List<InlinePresentation> inlineActions) {
+                                        @Nullable List<InlineAction> inlineActions) {
                                     mCallbacks.resetLastResponse();
                                     maybeRequestShowInlineSuggestions(sessionId,
                                             inlineSuggestionsRequest, inlineSuggestionsData,
@@ -237,7 +237,7 @@
     private void maybeRequestShowInlineSuggestions(int sessionId,
             @Nullable InlineSuggestionsRequest request,
             @Nullable List<Dataset> inlineSuggestionsData,
-            @Nullable List<InlinePresentation> inlineActions, @NonNull AutofillId focusedId,
+            @Nullable List<InlineAction> inlineActions, @NonNull AutofillId focusedId,
             @Nullable Function<InlineSuggestionsResponse, Boolean> inlineSuggestionsCallback,
             @NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback,
             @Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
@@ -250,7 +250,7 @@
 
         final InlineSuggestionsResponse inlineSuggestionsResponse =
                 InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse(
-                        request, inlineSuggestionsData, inlineActions, focusedId, mContext,
+                        request, inlineSuggestionsData, inlineActions, focusedId,
                         dataset -> {
                             mCallbacks.logAugmentedAutofillSelected(sessionId,
                                     dataset.getId());
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index f14a7e9..538082d 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -137,7 +137,6 @@
     private final Handler mHandler;
     private final Object mLock;
     private final AutoFillUI mUi;
-    private final Context mContext;
 
     private final MetricsLogger mMetricsLogger = new MetricsLogger();
 
@@ -695,7 +694,6 @@
         mLock = lock;
         mUi = ui;
         mHandler = handler;
-        mContext = context;
         mRemoteFillService = serviceComponentName == null ? null
                 : new RemoteFillService(context, serviceComponentName, userId, this,
                         bindInstantServiceAllowed);
@@ -2680,10 +2678,10 @@
         InlineSuggestionsResponse inlineSuggestionsResponse =
                 InlineSuggestionFactory.createInlineSuggestionsResponse(
                         inlineSuggestionsRequest.get(),
-                        response, filterText, response.getInlineActions(), mCurrentViewId, mContext,
+                        response, filterText, response.getInlineActions(), mCurrentViewId,
                         this, () -> {
                             synchronized (mLock) {
-                                requestHideFillUi(mCurrentViewId);
+                                mInlineSuggestionSession.hideInlineSuggestionsUi(mCurrentViewId);
                             }
                         }, remoteRenderService);
         if (inlineSuggestionsResponse == null) {
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 4cf4463..ee59d89 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -21,12 +21,13 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.Context;
+import android.content.IntentSender;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.service.autofill.Dataset;
 import android.service.autofill.FillResponse;
 import android.service.autofill.IInlineSuggestionUiCallback;
+import android.service.autofill.InlineAction;
 import android.service.autofill.InlinePresentation;
 import android.text.TextUtils;
 import android.util.Slog;
@@ -39,7 +40,6 @@
 import android.view.inputmethod.InlineSuggestionInfo;
 import android.view.inputmethod.InlineSuggestionsRequest;
 import android.view.inputmethod.InlineSuggestionsResponse;
-import android.widget.Toast;
 
 import com.android.internal.view.inline.IInlineContentCallback;
 import com.android.internal.view.inline.IInlineContentProvider;
@@ -73,8 +73,8 @@
     @Nullable
     public static InlineSuggestionsResponse createInlineSuggestionsResponse(
             @NonNull InlineSuggestionsRequest request, @NonNull FillResponse response,
-            @Nullable String filterText, @Nullable List<InlinePresentation> inlineActions,
-            @NonNull AutofillId autofillId, @NonNull Context context,
+            @Nullable String filterText, @Nullable List<InlineAction> inlineActions,
+            @NonNull AutofillId autofillId,
             @NonNull AutoFillUI.AutoFillUiCallback client, @NonNull Runnable onErrorCallback,
             @Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
         if (sDebug) Slog.d(TAG, "createInlineSuggestionsResponse called");
@@ -97,7 +97,7 @@
                 response.getAuthentication() == null ? null : response.getInlinePresentation();
         return createInlineSuggestionsResponseInternal(/* isAugmented= */ false, request,
                 response.getDatasets(), filterText, inlineAuthentication, inlineActions, autofillId,
-                context, onErrorCallback, onClickFactory, remoteRenderService);
+                onErrorCallback, onClickFactory, remoteRenderService);
     }
 
     /**
@@ -107,15 +107,14 @@
     @Nullable
     public static InlineSuggestionsResponse createAugmentedInlineSuggestionsResponse(
             @NonNull InlineSuggestionsRequest request, @NonNull List<Dataset> datasets,
-            @Nullable List<InlinePresentation> inlineActions,
-            @NonNull AutofillId autofillId, @NonNull Context context,
+            @Nullable List<InlineAction> inlineActions, @NonNull AutofillId autofillId,
             @NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback,
             @NonNull Runnable onErrorCallback,
             @Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
         if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
         return createInlineSuggestionsResponseInternal(/* isAugmented= */ true, request,
                 datasets, /* filterText= */ null, /* inlineAuthentication= */ null,
-                inlineActions, autofillId, context, onErrorCallback,
+                inlineActions, autofillId, onErrorCallback,
                 (dataset, datasetIndex) ->
                         inlineSuggestionUiCallback.autofill(dataset), remoteRenderService);
     }
@@ -125,9 +124,8 @@
             boolean isAugmented, @NonNull InlineSuggestionsRequest request,
             @Nullable List<Dataset> datasets, @Nullable String filterText,
             @Nullable InlinePresentation inlineAuthentication,
-            @Nullable List<InlinePresentation> inlineActions, @NonNull AutofillId autofillId,
-            @NonNull Context context, @NonNull Runnable onErrorCallback,
-            @NonNull BiConsumer<Dataset, Integer> onClickFactory,
+            @Nullable List<InlineAction> inlineActions, @NonNull AutofillId autofillId,
+            @NonNull Runnable onErrorCallback, @NonNull BiConsumer<Dataset, Integer> onClickFactory,
             @Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
 
         final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>();
@@ -169,14 +167,14 @@
 
             inlineSuggestions.add(inlineSuggestion);
         }
-        // We should only add inline actions if there is at least one suggestion.
-        if (!inlineSuggestions.isEmpty() && inlineActions != null) {
-            for (InlinePresentation inlinePresentation : inlineActions) {
-                final InlineSuggestion inlineAction = createInlineAction(isAugmented, context,
-                        mergedInlinePresentation(request, 0, inlinePresentation),
+        if (inlineActions != null) {
+            for (InlineAction inlineAction : inlineActions) {
+                final InlineSuggestion inlineActionSuggestion = createInlineAction(isAugmented,
+                        mergedInlinePresentation(request, 0, inlineAction.getInlinePresentation()),
+                        inlineAction.getAction(),
                         remoteRenderService, onErrorCallback, request.getHostInputToken(),
                         request.getHostDisplayId());
-                inlineSuggestions.add(inlineAction);
+                inlineSuggestions.add(inlineActionSuggestion);
             }
         }
         return new InlineSuggestionsResponse(inlineSuggestions);
@@ -215,22 +213,30 @@
 
 
     private static InlineSuggestion createInlineAction(boolean isAugmented,
-            @NonNull Context context,
-            @NonNull InlinePresentation inlinePresentation,
+            @NonNull InlinePresentation presentation,
+            @NonNull IntentSender action,
             @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
             @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken,
             int displayId) {
         final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
-                inlinePresentation.getInlinePresentationSpec(),
+                presentation.getInlinePresentationSpec(),
                 isAugmented ? InlineSuggestionInfo.SOURCE_PLATFORM
                         : InlineSuggestionInfo.SOURCE_AUTOFILL,
-                inlinePresentation.getAutofillHints(),
-                InlineSuggestionInfo.TYPE_ACTION, inlinePresentation.isPinned());
+                presentation.getAutofillHints(), InlineSuggestionInfo.TYPE_ACTION,
+                presentation.isPinned());
         final Runnable onClickAction = () -> {
-            Toast.makeText(context, "icon clicked", Toast.LENGTH_SHORT).show();
+            try {
+                // TODO(b/150499490): route the intent to the client app to have it fired there,
+                //  so that it will appear as a part of the same task as the client app (similar
+                //  to the authentication flow).
+                action.sendIntent(null, 0, null, null, null);
+            } catch (IntentSender.SendIntentException e) {
+                onErrorCallback.run();
+                Slog.w(TAG, "Error sending inline action intent");
+            }
         };
         return new InlineSuggestion(inlineSuggestionInfo,
-                createInlineContentProvider(inlinePresentation, onClickAction, onErrorCallback,
+                createInlineContentProvider(presentation, onClickAction, onErrorCallback,
                         remoteRenderService, hostInputToken, displayId));
     }
 
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 4cc6590..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/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index a4a42bc..03ca1c6 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -85,6 +85,7 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
@@ -272,6 +273,46 @@
                 }
             };
 
+    public boolean onFactoryReset() {
+        // Wait for stable state if bluetooth is temporary state.
+        int state = getState();
+        if (state == BluetoothAdapter.STATE_BLE_TURNING_ON
+                || state == BluetoothAdapter.STATE_TURNING_ON
+                || state == BluetoothAdapter.STATE_TURNING_OFF) {
+            if (!waitForState(Set.of(BluetoothAdapter.STATE_BLE_ON, BluetoothAdapter.STATE_ON))) {
+                return false;
+            }
+        }
+
+        // Clear registered LE apps to force shut-off Bluetooth
+        clearBleApps();
+        state = getState();
+        try {
+            mBluetoothLock.readLock().lock();
+            if (mBluetooth == null) {
+                return false;
+            }
+            if (state == BluetoothAdapter.STATE_BLE_ON) {
+                addActiveLog(
+                        BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET,
+                        mContext.getPackageName(), false);
+                mBluetooth.onBrEdrDown();
+                return true;
+            } else if (state == BluetoothAdapter.STATE_ON) {
+                addActiveLog(
+                        BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET,
+                        mContext.getPackageName(), false);
+                mBluetooth.disable();
+                return true;
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to shutdown Bluetooth", e);
+        } finally {
+            mBluetoothLock.readLock().unlock();
+        }
+        return false;
+    }
+
     public void onAirplaneModeChanged() {
         synchronized (this) {
             if (isBluetoothPersistedStateOn()) {
@@ -1670,7 +1711,8 @@
                         // the previous Bluetooth process has exited. The
                         // waiting period has three components:
                         // (a) Wait until the local state is STATE_OFF. This
-                        //     is accomplished by "waitForOnOff(false, true)".
+                        //     is accomplished by
+                        //     "waitForState(Set.of(BluetoothAdapter.STATE_OFF))".
                         // (b) Wait until the STATE_OFF state is updated to
                         //     all components.
                         // (c) Wait until the Bluetooth process exits, and
@@ -1680,7 +1722,7 @@
                         // message. The delay time is backed off if Bluetooth
                         // continuously failed to turn on itself.
                         //
-                        waitForOnOff(false, true);
+                        waitForState(Set.of(BluetoothAdapter.STATE_OFF));
                         Message restartMsg =
                                 mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
                         mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs());
@@ -1693,10 +1735,15 @@
                     }
                     mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
                     if (mEnable && mBluetooth != null) {
-                        waitForOnOff(true, false);
+                        waitForState(Set.of(BluetoothAdapter.STATE_ON));
                         mEnable = false;
                         handleDisable();
-                        waitForOnOff(false, false);
+                        waitForState(Set.of(BluetoothAdapter.STATE_OFF,
+                                BluetoothAdapter.STATE_TURNING_ON,
+                                BluetoothAdapter.STATE_TURNING_OFF,
+                                BluetoothAdapter.STATE_BLE_TURNING_ON,
+                                BluetoothAdapter.STATE_BLE_ON,
+                                BluetoothAdapter.STATE_BLE_TURNING_OFF));
                     } else {
                         mEnable = false;
                         handleDisable();
@@ -1819,9 +1866,14 @@
                     }
 
                     if (!mEnable) {
-                        waitForOnOff(true, false);
+                        waitForState(Set.of(BluetoothAdapter.STATE_ON));
                         handleDisable();
-                        waitForOnOff(false, false);
+                        waitForState(Set.of(BluetoothAdapter.STATE_OFF,
+                                BluetoothAdapter.STATE_TURNING_ON,
+                                BluetoothAdapter.STATE_TURNING_OFF,
+                                BluetoothAdapter.STATE_BLE_TURNING_ON,
+                                BluetoothAdapter.STATE_BLE_ON,
+                                BluetoothAdapter.STATE_BLE_TURNING_OFF));
                     }
                     break;
                 }
@@ -1853,7 +1905,7 @@
                             == BluetoothAdapter.STATE_OFF)) {
                         if (mEnable) {
                             Slog.d(TAG, "Entering STATE_OFF but mEnabled is true; restarting.");
-                            waitForOnOff(false, true);
+                            waitForState(Set.of(BluetoothAdapter.STATE_OFF));
                             Message restartMsg =
                                     mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
                             mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs());
@@ -1982,7 +2034,7 @@
                             mState = BluetoothAdapter.STATE_TURNING_ON;
                         }
 
-                        waitForOnOff(true, false);
+                        waitForState(Set.of(BluetoothAdapter.STATE_ON));
 
                         if (mState == BluetoothAdapter.STATE_TURNING_ON) {
                             bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON);
@@ -1997,7 +2049,8 @@
                         bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
                                 BluetoothAdapter.STATE_TURNING_OFF);
 
-                        boolean didDisableTimeout = !waitForOnOff(false, true);
+                        boolean didDisableTimeout =
+                                !waitForState(Set.of(BluetoothAdapter.STATE_OFF));
 
                         bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF,
                                 BluetoothAdapter.STATE_OFF);
@@ -2243,12 +2296,7 @@
         }
     }
 
-    /**
-     *  if on is true, wait for state become ON
-     *  if off is true, wait for state become OFF
-     *  if both on and off are false, wait for state not ON
-     */
-    private boolean waitForOnOff(boolean on, boolean off) {
+    private boolean waitForState(Set<Integer> states) {
         int i = 0;
         while (i < 10) {
             try {
@@ -2256,18 +2304,8 @@
                 if (mBluetooth == null) {
                     break;
                 }
-                if (on) {
-                    if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) {
-                        return true;
-                    }
-                } else if (off) {
-                    if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) {
-                        return true;
-                    }
-                } else {
-                    if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) {
-                        return true;
-                    }
+                if (states.contains(mBluetooth.getState())) {
+                    return true;
                 }
             } catch (RemoteException e) {
                 Slog.e(TAG, "getState()", e);
@@ -2275,14 +2313,10 @@
             } finally {
                 mBluetoothLock.readLock().unlock();
             }
-            if (on || off) {
-                SystemClock.sleep(300);
-            } else {
-                SystemClock.sleep(50);
-            }
+            SystemClock.sleep(300);
             i++;
         }
-        Slog.e(TAG, "waitForOnOff time out");
+        Slog.e(TAG, "waitForState " + states + " time out");
         return false;
     }
 
@@ -2343,7 +2377,7 @@
                 mContext.getPackageName(), false);
         handleDisable();
 
-        waitForOnOff(false, true);
+        waitForState(Set.of(BluetoothAdapter.STATE_OFF));
 
         sendBluetoothServiceDownCallback();
 
@@ -2533,6 +2567,8 @@
                 return "USER_SWITCH";
             case BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTORE_USER_SETTING:
                 return "RESTORE_USER_SETTING";
+            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET:
+                return "FACTORY_RESET";
             case BluetoothProtoEnums.ENABLE_DISABLE_REASON_UNSPECIFIED:
             default: return "UNKNOWN[" + reason + "]";
         }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index ff41d1cc..7287a44 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -273,9 +273,6 @@
     // connect anyway?" dialog after the user selects a network that doesn't validate.
     private static final int PROMPT_UNVALIDATED_DELAY_MS = 8 * 1000;
 
-    // How long to dismiss network notification.
-    private static final int TIMEOUT_NOTIFICATION_DELAY_MS = 20 * 1000;
-
     // Default to 30s linger time-out. Modifiable only for testing.
     private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
     private static final int DEFAULT_LINGER_DELAY_MS = 30_000;
@@ -523,18 +520,13 @@
     private static final int EVENT_PROVISIONING_NOTIFICATION = 43;
 
     /**
-     * This event can handle dismissing notification by given network id.
-     */
-    private static final int EVENT_TIMEOUT_NOTIFICATION = 44;
-
-    /**
      * Used to specify whether a network should be used even if connectivity is partial.
      * arg1 = whether to accept the network if its connectivity is partial (1 for true or 0 for
      * false)
      * arg2 = whether to remember this choice in the future (1 for true or 0 for false)
      * obj  = network
      */
-    private static final int EVENT_SET_ACCEPT_PARTIAL_CONNECTIVITY = 45;
+    private static final int EVENT_SET_ACCEPT_PARTIAL_CONNECTIVITY = 44;
 
     /**
      * Event for NetworkMonitor to inform ConnectivityService that the probe status has changed.
@@ -543,7 +535,7 @@
      * arg1 = A bitmask to describe which probes are completed.
      * arg2 = A bitmask to describe which probes are successful.
      */
-    public static final int EVENT_PROBE_STATUS_CHANGED = 46;
+    public static final int EVENT_PROBE_STATUS_CHANGED = 45;
 
     /**
      * Event for NetworkMonitor to inform ConnectivityService that captive portal data has changed.
@@ -551,7 +543,7 @@
      * arg2 = netId
      * obj = captive portal data
      */
-    private static final int EVENT_CAPPORT_DATA_CHANGED = 47;
+    private static final int EVENT_CAPPORT_DATA_CHANGED = 46;
 
     /**
      * Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
@@ -2877,13 +2869,6 @@
             final boolean valid = ((testResult & NETWORK_VALIDATION_RESULT_VALID) != 0);
             final boolean wasValidated = nai.lastValidated;
             final boolean wasDefault = isDefaultNetwork(nai);
-            // Only show a connected notification if the network is pending validation
-            // after the captive portal app was open, and it has now validated.
-            if (nai.captivePortalValidationPending && valid) {
-                // User is now logged in, network validated.
-                nai.captivePortalValidationPending = false;
-                showNetworkNotification(nai, NotificationType.LOGGED_IN);
-            }
 
             if (DBG) {
                 final String logMsg = !TextUtils.isEmpty(redirectUrl)
@@ -3764,12 +3749,6 @@
                 new CaptivePortal(new CaptivePortalImpl(network).asBinder()));
         appIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
 
-        // This runs on a random binder thread, but getNetworkAgentInfoForNetwork is thread-safe,
-        // and captivePortalValidationPending is volatile.
-        final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
-        if (nai != null) {
-            nai.captivePortalValidationPending = true;
-        }
         Binder.withCleanCallingIdentity(() ->
                 mContext.startActivityAsUser(appIntent, UserHandle.CURRENT));
     }
@@ -3888,14 +3867,6 @@
         final String action;
         final boolean highPriority;
         switch (type) {
-            case LOGGED_IN:
-                action = Settings.ACTION_WIFI_SETTINGS;
-                mHandler.removeMessages(EVENT_TIMEOUT_NOTIFICATION);
-                mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_TIMEOUT_NOTIFICATION,
-                        nai.network.netId, 0), TIMEOUT_NOTIFICATION_DELAY_MS);
-                // High priority because it is a direct result of the user logging in to a portal.
-                highPriority = true;
-                break;
             case NO_INTERNET:
                 action = ConnectivityManager.ACTION_PROMPT_UNVALIDATED;
                 // High priority because it is only displayed for explicitly selected networks.
@@ -3923,7 +3894,7 @@
         }
 
         Intent intent = new Intent(action);
-        if (type != NotificationType.LOGGED_IN && type != NotificationType.PRIVATE_DNS_BROKEN) {
+        if (type != NotificationType.PRIVATE_DNS_BROKEN) {
             intent.setData(Uri.fromParts("netId", Integer.toString(nai.network.netId), null));
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             intent.setClassName("com.android.settings",
@@ -4139,9 +4110,6 @@
                 case EVENT_DATA_SAVER_CHANGED:
                     handleRestrictBackgroundChanged(toBool(msg.arg1));
                     break;
-                case EVENT_TIMEOUT_NOTIFICATION:
-                    mNotifier.clearNotification(msg.arg1, NotificationType.LOGGED_IN);
-                    break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index b464422..2cfe404 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -161,6 +161,9 @@
     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
@@ -624,17 +627,22 @@
      * @see #syncRequestsAsync
      */
     private void syncRequests() {
-        Set<String> packages = null;
+        boolean syncRequired = false;
         synchronized (mLock) {
             if (mIsPackagesReady) {
-                packages = getPackagesPendingHealthChecksLocked();
+                Set<String> packages = getPackagesPendingHealthChecksLocked();
+                if (!packages.equals(mRequestedHealthCheckPackages)) {
+                    syncRequired = true;
+                    mRequestedHealthCheckPackages = packages;
+                }
             } // else, we will sync requests when packages become ready
         }
 
         // Call outside lock to avoid holding lock when calling into the controller.
-        if (packages != null) {
-            Slog.i(TAG, "Syncing health check requests for packages: " + packages);
-            mHealthCheckController.syncRequests(packages);
+        if (syncRequired) {
+            Slog.i(TAG, "Syncing health check requests for packages: "
+                    + mRequestedHealthCheckPackages);
+            mHealthCheckController.syncRequests(mRequestedHealthCheckPackages);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9058ac4..f64272b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4766,7 +4766,8 @@
                 packageName == null ? ApplicationExitInfo.REASON_USER_STOPPED
                         : ApplicationExitInfo.REASON_USER_REQUESTED,
                 ApplicationExitInfo.SUBREASON_UNKNOWN,
-                packageName == null ? ("stop user " + userId) : ("stop " + packageName));
+                (packageName == null ? ("stop user " + userId) : ("stop " + packageName))
+                + " due to " + reason);
 
         didSomething |=
                 mAtmInternal.onForceStopPackage(packageName, doit, evenPersistent, userId);
@@ -17066,6 +17067,7 @@
             proc.lastCachedPss = pss;
             proc.lastCachedSwapPss = swapPss;
         }
+        proc.mLastRss = rss;
 
         final SparseArray<Pair<Long, String>> watchUids
                 = mMemWatchProcesses.getMap().get(proc.processName);
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index a09aa64..028a059 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -129,7 +129,7 @@
     private final ProcessMap<AppExitInfoContainer> mData;
 
     /** A pool of raw {@link android.app.ApplicationExitInfo} records. */
-    @GuardedBy("mService")
+    @GuardedBy("mLock")
     private final SynchronizedPool<ApplicationExitInfo> mRawRecordsPool;
 
     /**
@@ -204,8 +204,7 @@
         });
     }
 
-    @GuardedBy("mService")
-    void scheduleNoteProcessDiedLocked(final ProcessRecord app) {
+    void scheduleNoteProcessDied(final ProcessRecord app) {
         if (app == null || app.info == null) {
             return;
         }
@@ -214,11 +213,9 @@
             if (!mAppExitInfoLoaded) {
                 return;
             }
+            mKillHandler.obtainMessage(KillHandler.MSG_PROC_DIED, obtainRawRecordLocked(app))
+                    .sendToTarget();
         }
-        // The current thread is holding the global lock, let's extract the info from it
-        // and schedule the info note task in the kill handler.
-        mKillHandler.obtainMessage(KillHandler.MSG_PROC_DIED, obtainRawRecordLocked(app))
-                .sendToTarget();
     }
 
     void scheduleNoteAppKill(final ProcessRecord app, final @Reason int reason,
@@ -227,8 +224,6 @@
             if (!mAppExitInfoLoaded) {
                 return;
             }
-        }
-        synchronized (mService) {
             if (app == null || app.info == null) {
                 return;
             }
@@ -247,8 +242,6 @@
             if (!mAppExitInfoLoaded) {
                 return;
             }
-        }
-        synchronized (mService) {
             ProcessRecord app;
             synchronized (mService.mPidsSelfLocked) {
                 app = mService.mPidsSelfLocked.get(pid);
@@ -512,9 +505,13 @@
     @VisibleForTesting
     void onPackageRemoved(String packageName, int uid, boolean allUsers) {
         if (packageName != null) {
-            mAppExitInfoSourceZygote.removeByUid(uid, allUsers);
-            mAppExitInfoSourceLmkd.removeByUid(uid, allUsers);
-            mIsolatedUidRecords.removeAppUid(uid, allUsers);
+            final boolean removeUid = TextUtils.isEmpty(
+                    mService.mPackageManagerInt.getNameForUid(uid));
+            if (removeUid) {
+                mAppExitInfoSourceZygote.removeByUid(uid, allUsers);
+                mAppExitInfoSourceLmkd.removeByUid(uid, allUsers);
+                mIsolatedUidRecords.removeAppUid(uid, allUsers);
+            }
             removePackage(packageName, allUsers ? UserHandle.USER_ALL : UserHandle.getUserId(uid));
             schedulePersistProcessExitInfo(true);
         }
@@ -540,6 +537,11 @@
         mService.mContext.registerReceiverForAllUsers(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
+                boolean replacing = intent.getBooleanExtra(
+                        Intent.EXTRA_REPLACING, false);
+                if (replacing) {
+                    return;
+                }
                 int uid = intent.getIntExtra(Intent.EXTRA_UID, UserHandle.USER_NULL);
                 boolean allUsers = intent.getBooleanExtra(
                         Intent.EXTRA_REMOVED_FOR_ALL_USERS, false);
@@ -823,7 +825,7 @@
     }
 
     @VisibleForTesting
-    @GuardedBy("mService")
+    @GuardedBy("mLock")
     ApplicationExitInfo obtainRawRecordLocked(ProcessRecord app) {
         ApplicationExitInfo info = mRawRecordsPool.acquire();
         if (info == null) {
@@ -842,15 +844,15 @@
         info.setReason(ApplicationExitInfo.REASON_UNKNOWN);
         info.setStatus(0);
         info.setImportance(procStateToImportance(app.setProcState));
-        info.setPss(app.lastMemInfo == null ? 0 : app.lastMemInfo.getTotalPss());
-        info.setRss(app.lastMemInfo == null ? 0 : app.lastMemInfo.getTotalRss());
+        info.setPss(app.lastPss);
+        info.setRss(app.mLastRss);
         info.setTimestamp(System.currentTimeMillis());
 
         return info;
     }
 
     @VisibleForTesting
-    @GuardedBy("mService")
+    @GuardedBy("mLock")
     void recycleRawRecordLocked(ApplicationExitInfo info) {
         info.setProcessName(null);
         info.setDescription(null);
@@ -1135,8 +1137,6 @@
                     ApplicationExitInfo raw = (ApplicationExitInfo) msg.obj;
                     synchronized (mLock) {
                         handleNoteProcessDiedLocked(raw);
-                    }
-                    synchronized (mService) {
                         recycleRawRecordLocked(raw);
                     }
                 }
@@ -1145,8 +1145,6 @@
                     ApplicationExitInfo raw = (ApplicationExitInfo) msg.obj;
                     synchronized (mLock) {
                         handleNoteAppKillLocked(raw);
-                    }
-                    synchronized (mService) {
                         recycleRawRecordLocked(raw);
                     }
                 }
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/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 7cc2e8e..1f826b5 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -16,12 +16,6 @@
 
 # Windows & Activities
 ogunwale@google.com
-jjaggi@google.com
-racarr@google.com
-chaviw@google.com
-vishnun@google.com
-akulian@google.com
-roosa@google.com
 
 # Permissions & Packages
 svetoslavganov@google.com
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 644f0f7..b584ea5 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1708,11 +1708,6 @@
                                     // TOP process passes all capabilities to the service.
                                     capability |= PROCESS_CAPABILITY_ALL;
                                 }
-                            } else if (clientProcState
-                                    <= PROCESS_STATE_FOREGROUND_SERVICE) {
-                                if (cr.notHasFlag(Context.BIND_INCLUDE_CAPABILITIES)) {
-                                    clientProcState = PROCESS_STATE_FOREGROUND_SERVICE;
-                                }
                             }
                         } else if ((cr.flags & Context.BIND_IMPORTANT_BACKGROUND) == 0) {
                             if (clientProcState <
@@ -2036,7 +2031,7 @@
             case PROCESS_STATE_TOP:
                 return PROCESS_CAPABILITY_ALL;
             case PROCESS_STATE_BOUND_TOP:
-                return PROCESS_CAPABILITY_ALL_IMPLICIT;
+                return PROCESS_CAPABILITY_NONE;
             case PROCESS_STATE_FOREGROUND_SERVICE:
                 if (app.hasForegroundServices()) {
                     // Capability from FGS are conditional depending on foreground service type in
@@ -2044,10 +2039,12 @@
                     return PROCESS_CAPABILITY_NONE;
                 } else {
                     // process has no FGS, the PROCESS_STATE_FOREGROUND_SERVICE is from client.
+                    // the implicit capability could be removed in the future, client should use
+                    // BIND_INCLUDE_CAPABILITY flag.
                     return PROCESS_CAPABILITY_ALL_IMPLICIT;
                 }
             case PROCESS_STATE_BOUND_FOREGROUND_SERVICE:
-                return PROCESS_CAPABILITY_ALL_IMPLICIT;
+                return PROCESS_CAPABILITY_NONE;
             default:
                 return PROCESS_CAPABILITY_NONE;
         }
@@ -2588,8 +2585,13 @@
             return;
         }
 
+        // if an app is already frozen and shouldNotFreeze becomes true, immediately unfreeze
+        if (app.frozen && app.shouldNotFreeze) {
+            mCachedAppOptimizer.unfreezeAppLocked(app);
+        }
+
         // Use current adjustment when freezing, set adjustment when unfreezing.
-        if (app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ && !app.frozen) {
+        if (app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ && !app.frozen && !app.shouldNotFreeze) {
             mCachedAppOptimizer.freezeAppAsync(app);
         } else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ && app.frozen) {
             mCachedAppOptimizer.unfreezeAppLocked(app);
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index a9d18e0..6b16513 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -3773,7 +3773,7 @@
         }
 
         Watchdog.getInstance().processDied(app.processName, app.pid);
-        mAppExitInfoTracker.scheduleNoteProcessDiedLocked(app);
+        mAppExitInfoTracker.scheduleNoteProcessDied(app);
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index f2ca1da..c029811 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -323,6 +323,8 @@
     // set of disabled compat changes for the process (all others are enabled)
     long[] mDisabledCompatChanges;
 
+    long mLastRss;               // Last computed memory rss.
+
     // The precede instance of the process, which would exist when the previous process is killed
     // but not fully dead yet; in this case, the new instance of the process should be held until
     // this precede instance is fully dead.
@@ -431,6 +433,7 @@
                 pw.print(" lastSwapPss="); DebugUtils.printSizeValue(pw, lastSwapPss*1024);
                 pw.print(" lastCachedPss="); DebugUtils.printSizeValue(pw, lastCachedPss*1024);
                 pw.print(" lastCachedSwapPss="); DebugUtils.printSizeValue(pw, lastCachedSwapPss*1024);
+        pw.print(" lastRss="); DebugUtils.printSizeValue(pw, mLastRss * 1024);
                 pw.println();
         pw.print(prefix); pw.print("procStateMemTracker: ");
         procStateMemTracker.dumpLine(pw);
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index bfc2f82..61bede9 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -219,6 +219,19 @@
     }
 
     /**
+     * Returns whether the change is marked as disabled.
+     */
+    boolean isDisabled(long changeId) {
+        synchronized (mChanges) {
+            CompatChange c = mChanges.get(changeId);
+            if (c == null) {
+                return false;
+            }
+            return c.getDisabled();
+        }
+    }
+
+    /**
      * Removes an override previously added via {@link #addOverride(long, String, boolean)}. This
      * restores the default behaviour for the given change and app, once any app processes have been
      * restarted.
diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
index 9e18c74..f5d6e5a 100644
--- a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
+++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
@@ -59,6 +59,7 @@
         boolean debuggableBuild = mAndroidBuildClassifier.isDebuggableBuild();
         boolean finalBuild = mAndroidBuildClassifier.isFinalBuild();
         int minTargetSdk = mCompatConfig.minTargetSdkForChangeId(changeId);
+        boolean disabled = mCompatConfig.isDisabled(changeId);
 
         // Allow any override for userdebug or eng builds.
         if (debuggableBuild) {
@@ -83,12 +84,12 @@
         if (!finalBuild) {
             return new OverrideAllowedState(ALLOWED, appTargetSdk, minTargetSdk);
         }
-        // Do not allow overriding non-target sdk gated changes on user builds
-        if (minTargetSdk == -1) {
+        // Do not allow overriding default enabled changes on user builds
+        if (minTargetSdk == -1 && !disabled) {
             return new OverrideAllowedState(DISABLED_NON_TARGET_SDK, appTargetSdk, minTargetSdk);
         }
         // Only allow to opt-in for a targetSdk gated change.
-        if (applicationInfo.targetSdkVersion < minTargetSdk) {
+        if (disabled || applicationInfo.targetSdkVersion < minTargetSdk) {
             return new OverrideAllowedState(ALLOWED, appTargetSdk, minTargetSdk);
         }
         return new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, appTargetSdk, minTargetSdk);
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 23b954c..2f04715 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -160,10 +160,6 @@
     // Whether a captive portal was found during the last network validation attempt.
     public boolean lastCaptivePortalDetected;
 
-    // Indicates the captive portal app was opened to show a login UI to the user, but the network
-    // has not validated yet.
-    public volatile boolean captivePortalValidationPending;
-
     // Set to true when partial connectivity was detected.
     public boolean partialConnectivity;
 
@@ -638,7 +634,6 @@
                 + "acceptUnvalidated{" + networkAgentConfig.acceptUnvalidated + "} "
                 + "everCaptivePortalDetected{" + everCaptivePortalDetected + "} "
                 + "lastCaptivePortalDetected{" + lastCaptivePortalDetected + "} "
-                + "captivePortalValidationPending{" + captivePortalValidationPending + "} "
                 + "partialConnectivity{" + partialConnectivity + "} "
                 + "acceptPartialConnectivity{" + networkAgentConfig.acceptPartialConnectivity + "} "
                 + "clat{" + clatd + "} "
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index 25c761a..0925de8 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -51,7 +51,6 @@
         LOST_INTERNET(SystemMessage.NOTE_NETWORK_LOST_INTERNET),
         NETWORK_SWITCH(SystemMessage.NOTE_NETWORK_SWITCH),
         NO_INTERNET(SystemMessage.NOTE_NETWORK_NO_INTERNET),
-        LOGGED_IN(SystemMessage.NOTE_NETWORK_LOGGED_IN),
         PARTIAL_CONNECTIVITY(SystemMessage.NOTE_NETWORK_PARTIAL_CONNECTIVITY),
         SIGN_IN(SystemMessage.NOTE_NETWORK_SIGN_IN),
         PRIVATE_DNS_BROKEN(SystemMessage.NOTE_NETWORK_PRIVATE_DNS_BROKEN);
@@ -114,14 +113,10 @@
         }
     }
 
-    private static int getIcon(int transportType, NotificationType notifyType) {
-        if (transportType != TRANSPORT_WIFI) {
-            return R.drawable.stat_notify_rssi_in_range;
-        }
-
-        return notifyType == NotificationType.LOGGED_IN
-            ? R.drawable.ic_wifi_signal_4
-            : R.drawable.stat_notify_wifi_in_range;  // TODO: Distinguish ! from ?.
+    private static int getIcon(int transportType) {
+        return (transportType == TRANSPORT_WIFI)
+                ? R.drawable.stat_notify_wifi_in_range :  // TODO: Distinguish ! from ?.
+                R.drawable.stat_notify_rssi_in_range;
     }
 
     /**
@@ -185,7 +180,7 @@
         Resources r = mContext.getResources();
         final CharSequence title;
         final CharSequence details;
-        int icon = getIcon(transportType, notifyType);
+        int icon = getIcon(transportType);
         if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) {
             title = r.getString(R.string.wifi_no_internet,
                     WifiInfo.sanitizeSsid(nai.networkCapabilities.getSSID()));
@@ -235,9 +230,6 @@
                     details = r.getString(R.string.network_available_sign_in_detailed, name);
                     break;
             }
-        } else if (notifyType == NotificationType.LOGGED_IN) {
-            title = WifiInfo.sanitizeSsid(nai.networkCapabilities.getSSID());
-            details = r.getString(R.string.captive_portal_logged_in_detailed);
         } else if (notifyType == NotificationType.NETWORK_SWITCH) {
             String fromTransport = getTransportName(transportType);
             String toTransport = getTransportName(approximateTransportType(switchToNai));
@@ -379,7 +371,6 @@
             case NETWORK_SWITCH:
                 return 2;
             case LOST_INTERNET:
-            case LOGGED_IN:
                 return 1;
             default:
                 return 0;
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/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index b90681d..79a4da2 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -388,7 +388,7 @@
         for (int i = ArrayUtils.size(intents) - 1; i >= 0; i--) {
             IntentFilter intentFilter = intents.get(i);
             if (intentFilter.match(intent.getAction(), intent.getType(), intent.getScheme(),
-                    intent.getData(), intent.getCategories(), "AppsFilter") > 0) {
+                    intent.getData(), intent.getCategories(), "AppsFilter", true) > 0) {
                 return true;
             }
         }
diff --git a/services/core/java/com/android/server/pm/DataLoaderManagerService.java b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
index ad20d38..8eb773a 100644
--- a/services/core/java/com/android/server/pm/DataLoaderManagerService.java
+++ b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
@@ -116,9 +116,6 @@
                 return null;
             }
 
-            // TODO(b/136132412): better way to enable privileged data loaders in tests
-            boolean checkLoader =
-                    android.os.SystemProperties.getBoolean("incremental.check_loader", false);
             int numServices = services.size();
             for (int i = 0; i < numServices; i++) {
                 ResolveInfo ri = services.get(i);
@@ -128,7 +125,7 @@
                 // If there's more than one, return the first one found.
                 try {
                     ApplicationInfo ai = pm.getApplicationInfo(resolved.getPackageName(), 0);
-                    if (checkLoader && !ai.isPrivilegedApp()) {
+                    if (!ai.isPrivilegedApp()) {
                         Slog.w(TAG,
                                 "Data loader: " + resolved + " is not a privileged app, skipping.");
                         continue;
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/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 32cff3b..98579af 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -24,6 +24,8 @@
 import static android.os.Process.getUidForPid;
 import static android.os.storage.VolumeInfo.TYPE_PRIVATE;
 import static android.os.storage.VolumeInfo.TYPE_PUBLIC;
+import static android.util.MathUtils.abs;
+import static android.util.MathUtils.constrain;
 
 import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
 import static com.android.server.stats.pull.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
@@ -146,12 +148,15 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.MissingResourceException;
+import java.util.Random;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
@@ -169,6 +174,9 @@
     private static final String TAG = "StatsPullAtomService";
     private static final boolean DEBUG = true;
 
+    // Random seed stable for StatsPullAtomService life cycle - can be used for stable sampling
+    private static final int RANDOM_SEED = new Random().nextInt();
+
     /**
      * Lowest available uid for apps.
      *
@@ -256,6 +264,8 @@
 
     private StatsPullAtomCallbackImpl mStatsCallbackImpl;
 
+    private int mAppOpsSamplingRate = 0;
+
     public StatsPullAtomService(Context context) {
         super(context);
         mContext = context;
@@ -2877,44 +2887,7 @@
 
             HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS,
                     TimeUnit.MILLISECONDS);
-
-            for (int uidIdx = 0; uidIdx < histOps.getUidCount(); uidIdx++) {
-                final HistoricalUidOps uidOps = histOps.getUidOpsAt(uidIdx);
-                final int uid = uidOps.getUid();
-                for (int pkgIdx = 0; pkgIdx < uidOps.getPackageCount(); pkgIdx++) {
-                    final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(pkgIdx);
-                    for (int opIdx = 0; opIdx < packageOps.getOpCount(); opIdx++) {
-                        final AppOpsManager.HistoricalOp op = packageOps.getOpAt(opIdx);
-
-                        StatsEvent.Builder e = StatsEvent.newBuilder();
-                        e.setAtomId(atomTag);
-                        e.writeInt(uid);
-                        e.writeString(packageOps.getPackageName());
-                        e.writeInt(op.getOpCode());
-                        e.writeLong(op.getForegroundAccessCount(OP_FLAGS_PULLED));
-                        e.writeLong(op.getBackgroundAccessCount(OP_FLAGS_PULLED));
-                        e.writeLong(op.getForegroundRejectCount(OP_FLAGS_PULLED));
-                        e.writeLong(op.getBackgroundRejectCount(OP_FLAGS_PULLED));
-                        e.writeLong(op.getForegroundAccessDuration(OP_FLAGS_PULLED));
-                        e.writeLong(op.getBackgroundAccessDuration(OP_FLAGS_PULLED));
-
-                        String perm = AppOpsManager.opToPermission(op.getOpCode());
-                        if (perm == null) {
-                            e.writeBoolean(false);
-                        } else {
-                            PermissionInfo permInfo;
-                            try {
-                                permInfo = mContext.getPackageManager().getPermissionInfo(perm, 0);
-                                e.writeBoolean(permInfo.getProtection() == PROTECTION_DANGEROUS);
-                            } catch (PackageManager.NameNotFoundException exception) {
-                                e.writeBoolean(false);
-                            }
-                        }
-
-                        pulledData.add(e.build());
-                    }
-                }
-            }
+            processHistoricalOps(histOps, atomTag, pulledData);
         } catch (Throwable t) {
             // TODO: catch exceptions at a more granular level
             Slog.e(TAG, "Could not read appops", t);
@@ -2945,54 +2918,12 @@
                     new HistoricalOpsRequest.Builder(0, Long.MAX_VALUE).setFlags(
                             OP_FLAGS_PULLED).build();
             appOps.getHistoricalOps(histOpsRequest, mContext.getMainExecutor(), ops::complete);
-
             HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS,
                     TimeUnit.MILLISECONDS);
-
-            for (int uidIdx = 0; uidIdx < histOps.getUidCount(); uidIdx++) {
-                final HistoricalUidOps uidOps = histOps.getUidOpsAt(uidIdx);
-                final int uid = uidOps.getUid();
-                for (int pkgIdx = 0; pkgIdx < uidOps.getPackageCount(); pkgIdx++) {
-                    final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(pkgIdx);
-                    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);
-                            StatsEvent.Builder e = StatsEvent.newBuilder();
-                            e.setAtomId(atomTag);
-                            e.writeInt(uid);
-                            e.writeString(packageOps.getPackageName());
-                            e.writeString(featureOps.getFeatureId());
-                            e.writeString(op.getOpName());
-                            e.writeLong(op.getForegroundAccessCount(OP_FLAGS_PULLED));
-                            e.writeLong(op.getBackgroundAccessCount(OP_FLAGS_PULLED));
-                            e.writeLong(op.getForegroundRejectCount(OP_FLAGS_PULLED));
-                            e.writeLong(op.getBackgroundRejectCount(OP_FLAGS_PULLED));
-                            e.writeLong(op.getForegroundAccessDuration(OP_FLAGS_PULLED));
-                            e.writeLong(op.getBackgroundAccessDuration(OP_FLAGS_PULLED));
-
-                            String perm = AppOpsManager.opToPermission(op.getOpCode());
-                            if (perm == null) {
-                                e.writeBoolean(false);
-                            } else {
-                                PermissionInfo permInfo;
-                                try {
-                                    permInfo = mContext.getPackageManager().getPermissionInfo(perm,
-                                            0);
-                                    e.writeBoolean(
-                                            permInfo.getProtection() == PROTECTION_DANGEROUS);
-                                } catch (PackageManager.NameNotFoundException exception) {
-                                    e.writeBoolean(false);
-                                }
-                            }
-                            pulledData.add(e.build());
-                        }
-
-                    }
-                }
+            if (mAppOpsSamplingRate == 0) {
+                mAppOpsSamplingRate = constrain((5000 * 100) / estimateAppOpsSize(), 1, 100);
             }
+            processHistoricalOps(histOps, atomTag, pulledData);
         } catch (Throwable t) {
             // TODO: catch exceptions at a more granular level
             Slog.e(TAG, "Could not read appops", t);
@@ -3003,6 +2934,111 @@
         return StatsManager.PULL_SUCCESS;
     }
 
+    private int estimateAppOpsSize() throws Exception {
+        AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
+
+        CompletableFuture<HistoricalOps> ops = new CompletableFuture<>();
+        HistoricalOpsRequest histOpsRequest =
+                new HistoricalOpsRequest.Builder(
+                        Instant.now().minus(1, ChronoUnit.DAYS).toEpochMilli(),
+                        Long.MAX_VALUE).setFlags(
+                        OP_FLAGS_PULLED).build();
+        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);
+    }
+
+    int processHistoricalOps(HistoricalOps histOps, int atomTag, List<StatsEvent> pulledData) {
+        int counter = 0;
+        for (int uidIdx = 0; uidIdx < histOps.getUidCount(); uidIdx++) {
+            final HistoricalUidOps uidOps = histOps.getUidOpsAt(uidIdx);
+            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);
+                            counter += processHistoricalOp(op, atomTag, pulledData, uid,
+                                    packageOps.getPackageName(), featureOps.getFeatureId());
+                        }
+                    }
+                } else if (atomTag == FrameworkStatsLog.APP_OPS) {
+                    for (int opIdx = 0; opIdx < packageOps.getOpCount(); opIdx++) {
+                        final AppOpsManager.HistoricalOp op = packageOps.getOpAt(opIdx);
+                        counter += processHistoricalOp(op, atomTag, pulledData, uid,
+                                packageOps.getPackageName(), null);
+                    }
+                }
+            }
+        }
+        return counter;
+    }
+
+    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) {
+            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());
+                }
+            } else {
+                if (abs((op.getOpCode() + feature + packageName).hashCode() + RANDOM_SEED) % 100
+                        >= mAppOpsSamplingRate) {
+                    return 0;
+                }
+            }
+        }
+
+        StatsEvent.Builder e = StatsEvent.newBuilder();
+        e.setAtomId(atomTag);
+        e.writeInt(uid);
+        e.writeString(packageName);
+        if (atomTag == FrameworkStatsLog.APP_FEATURES_OPS) {
+            e.writeString(feature);
+        }
+        if (atomTag == FrameworkStatsLog.APP_FEATURES_OPS) {
+            e.writeString(op.getOpName());
+        } else {
+            e.writeInt(op.getOpCode());
+        }
+        e.writeLong(op.getForegroundAccessCount(OP_FLAGS_PULLED));
+        e.writeLong(op.getBackgroundAccessCount(OP_FLAGS_PULLED));
+        e.writeLong(op.getForegroundRejectCount(OP_FLAGS_PULLED));
+        e.writeLong(op.getBackgroundRejectCount(OP_FLAGS_PULLED));
+        e.writeLong(op.getForegroundAccessDuration(OP_FLAGS_PULLED));
+        e.writeLong(op.getBackgroundAccessDuration(OP_FLAGS_PULLED));
+
+        String perm = AppOpsManager.opToPermission(op.getOpCode());
+        if (perm == null) {
+            e.writeBoolean(false);
+        } else {
+            PermissionInfo permInfo;
+            try {
+                permInfo = mContext.getPackageManager().getPermissionInfo(
+                        perm,
+                        0);
+                e.writeBoolean(
+                        permInfo.getProtection() == PROTECTION_DANGEROUS);
+            } catch (PackageManager.NameNotFoundException exception) {
+                e.writeBoolean(false);
+            }
+        }
+        if (atomTag == FrameworkStatsLog.APP_FEATURES_OPS) {
+            e.writeInt(mAppOpsSamplingRate);
+        }
+        pulledData.add(e.build());
+        return 0;
+    }
+
     int pullRuntimeAppOpAccessMessage(int atomTag, List<StatsEvent> pulledData) {
         final long token = Binder.clearCallingIdentity();
         try {
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index d7a80bf..68224b5 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -27,7 +27,6 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
-import android.app.Service;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -127,7 +126,7 @@
     public boolean setWindowsForAccessibilityCallbackLocked(int displayId,
             WindowsForAccessibilityCallback callback) {
         if (callback != null) {
-            final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+            final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
             if (dc == null) {
                 return false;
             }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 7a30211..b6ad241 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -200,6 +200,7 @@
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
+import static com.android.server.wm.WindowContainerChildProto.ACTIVITY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
@@ -3150,7 +3151,7 @@
 
         // Reset the last saved PiP snap fraction on removal.
         mDisplayContent.mPinnedStackControllerLocked.resetReentryBounds(mActivityComponent);
-
+        mWmService.mEmbeddedWindowController.onActivityRemoved(this);
         mRemovingFromDisplay = false;
     }
 
@@ -7450,6 +7451,11 @@
     }
 
     @Override
+    long getProtoFieldId() {
+        return ACTIVITY;
+    }
+
+    @Override
     public void dumpDebug(ProtoOutputStream proto, long fieldId,
             @WindowTraceLogLevel int logLevel) {
         // Critical log level logs only visible elements to mitigate performance overheard
@@ -7620,7 +7626,7 @@
         return new RemoteAnimationTarget(task.mTaskId, record.getMode(),
                 record.mAdapter.mCapturedLeash, !fillsParent(),
                 mainWindow.mWinAnimator.mLastClipRect, insets,
-                getPrefixOrderIndex(), record.mAdapter.mPosition,
+                getPrefixOrderIndex(), record.mAdapter.mPosition, record.mAdapter.mLocalBounds,
                 record.mAdapter.mStackBounds, task.getWindowConfiguration(),
                 false /*isNotInRecents*/,
                 record.mThumbnailAdapter != null ? record.mThumbnailAdapter.mCapturedLeash : null,
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 420675c..9bad799 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -90,7 +90,6 @@
 import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
-import static com.android.server.wm.TaskProto.ACTIVITIES;
 import static com.android.server.wm.TaskProto.ACTIVITY_TYPE;
 import static com.android.server.wm.TaskProto.ANIMATING_BOUNDS;
 import static com.android.server.wm.TaskProto.BOUNDS;
@@ -109,7 +108,6 @@
 import static com.android.server.wm.TaskProto.ROOT_TASK_ID;
 import static com.android.server.wm.TaskProto.SURFACE_HEIGHT;
 import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
-import static com.android.server.wm.TaskProto.TASKS;
 import static com.android.server.wm.TaskProto.WINDOW_CONTAINER;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -649,14 +647,6 @@
 
         if (prevWindowingMode != getWindowingMode()) {
             mDisplayContent.onStackWindowingModeChanged(this);
-
-            if (inSplitScreenSecondaryWindowingMode()) {
-                // When the stack is resized due to entering split screen secondary, offset the
-                // windows to compensate for the new stack position.
-                forAllWindows(w -> {
-                    w.mWinAnimator.setOffsetPositionForStackResize(true);
-                }, true);
-            }
         }
 
         final DisplayContent display = getDisplay();
@@ -2395,8 +2385,10 @@
         }
         Task task = null;
         if (!newTask && isOrhasTask) {
+            // Starting activity cannot be occluding activity, otherwise starting window could be
+            // remove immediately without transferring to starting activity.
             final ActivityRecord occludingActivity = getActivity(
-                    (ar) -> !ar.finishing && ar.occludesParent(), true, rTask);
+                    (ar) -> !ar.finishing && ar.occludesParent(), true, r);
             if (occludingActivity != null) {
                 // Here it is!  Now, if this is not yet visible (occluded by another task) to the
                 // user, then just add it without starting; it will get started when the user
@@ -3883,9 +3875,10 @@
             return;
         }
         if (mTile != null) {
-            reparentSurfaceControl(getPendingTransaction(), mTile.getSurfaceControl());
+            // don't use reparentSurfaceControl because we need to bypass taskorg check
+            mSurfaceAnimator.reparent(getPendingTransaction(), mTile.getSurfaceControl());
         } else if (mTile == null && origTile != null) {
-            reparentSurfaceControl(getPendingTransaction(), getParentSurfaceControl());
+            mSurfaceAnimator.reparent(getPendingTransaction(), getParentSurfaceControl());
         }
     }
 
@@ -3918,17 +3911,6 @@
         proto.write(DISPLAY_ID, getDisplayId());
         proto.write(ROOT_TASK_ID, getRootTaskId());
 
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowContainer child = mChildren.get(i);
-            if (child instanceof Task) {
-                child.dumpDebug(proto, TASKS, logLevel);
-            } else if (child instanceof ActivityRecord) {
-                child.dumpDebug(proto, ACTIVITIES, logLevel);
-            } else {
-                throw new IllegalStateException("Unknown child type: " + child);
-            }
-        }
-
         if (mResumedActivity != null) {
             mResumedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
         }
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 984ae21..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);
 
@@ -1570,10 +1568,16 @@
 
         mService.mUgmInternal.grantUriPermissionFromIntent(mCallingUid, mStartActivity.packageName,
                 mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.mUserId);
-        mService.getPackageManagerInternalLocked().grantImplicitAccess(
-                mStartActivity.mUserId, mIntent,
-                UserHandle.getAppId(mStartActivity.info.applicationInfo.uid), mCallingUid,
-                true /*direct*/);
+        if (mStartActivity.resultTo != null && mStartActivity.resultTo.info != null) {
+            // we need to resolve resultTo to a uid as grantImplicitAccess deals explicitly in UIDs
+            final PackageManagerInternal pmInternal =
+                    mService.getPackageManagerInternalLocked();
+            final int resultToUid = pmInternal.getPackageUidInternal(
+                            mStartActivity.resultTo.info.packageName, 0, mStartActivity.mUserId);
+            pmInternal.grantImplicitAccess(mStartActivity.mUserId, mIntent,
+                    UserHandle.getAppId(mStartActivity.info.applicationInfo.uid) /*recipient*/,
+                    resultToUid /*visible*/, true /*direct*/);
+        }
         if (newTask) {
             EventLogTags.writeWmCreateTask(mStartActivity.mUserId,
                     mStartActivity.getTask().mTaskId);
@@ -2012,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 d3ff912..21d300a 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -288,6 +288,7 @@
 import java.text.DateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -680,7 +681,7 @@
         }
 
         @Override
-        public void onChange(boolean selfChange, Iterable<Uri> uris, int flags,
+        public void onChange(boolean selfChange, Collection<Uri> uris, int flags,
                 @UserIdInt int userId) {
             for (Uri uri : uris) {
                 if (mFontScaleUri.equals(uri)) {
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index 94decc7..c18ed7d 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -114,7 +114,6 @@
         return st.addToSync(wc);
     }
 
-    // TODO(b/148476626): TIMEOUTS!
     void setReady(int id) {
         final SyncState st = mPendingSyncs.get(id);
         st.setReady();
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 0d365b1..d9e41af 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -23,13 +23,10 @@
 import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
 
 import static com.android.internal.util.Preconditions.checkState;
-import static com.android.server.wm.DisplayAreaChildProto.DISPLAY_AREA;
-import static com.android.server.wm.DisplayAreaChildProto.UNKNOWN;
-import static com.android.server.wm.DisplayAreaChildProto.WINDOW;
-import static com.android.server.wm.DisplayAreaProto.CHILDREN;
 import static com.android.server.wm.DisplayAreaProto.NAME;
 import static com.android.server.wm.DisplayAreaProto.WINDOW_CONTAINER;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
+import static com.android.server.wm.WindowContainerChildProto.DISPLAY_AREA;
 
 import android.graphics.Rect;
 import android.util.proto.ProtoOutputStream;
@@ -111,24 +108,14 @@
         final long token = proto.start(fieldId);
         super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
         proto.write(NAME, mName);
-        for (int i = 0; i < getChildCount(); i++) {
-            final long childToken = proto.start(CHILDREN);
-            final T child = getChildAt(i);
-            if (child instanceof ActivityStack) {
-                // TODO(display-area): Dump stacks & tasks here, instead of in DisplayContent's
-                //  dumpDebug. For now, skip them here to avoid dumping them as UNKNOWN.
-            } else if (child instanceof WindowToken) {
-                ((WindowToken) child).dumpDebug(proto, WINDOW, logLevel);
-            } else if (child instanceof DisplayArea) {
-                child.dumpDebug(proto, DISPLAY_AREA, logLevel);
-            } else {
-                proto.write(UNKNOWN, child.getClass().getSimpleName());
-            }
-            proto.end(childToken);
-        }
         proto.end(token);
     }
 
+    @Override
+    long getProtoFieldId() {
+        return DISPLAY_AREA;
+    }
+
     /**
      * DisplayArea that contains WindowTokens, and orders them according to their type.
      */
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 55ce84e..36fdb2d 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -94,6 +94,7 @@
 import static com.android.server.wm.DisplayContentProto.CLOSING_APPS;
 import static com.android.server.wm.DisplayContentProto.DISPLAY_FRAMES;
 import static com.android.server.wm.DisplayContentProto.DISPLAY_INFO;
+import static com.android.server.wm.DisplayContentProto.DISPLAY_READY;
 import static com.android.server.wm.DisplayContentProto.DPI;
 import static com.android.server.wm.DisplayContentProto.FOCUSED_APP;
 import static com.android.server.wm.DisplayContentProto.FOCUSED_ROOT_TASK_ID;
@@ -105,7 +106,6 @@
 import static com.android.server.wm.DisplayContentProto.ROTATION;
 import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
 import static com.android.server.wm.DisplayContentProto.SINGLE_TASK_INSTANCE;
-import static com.android.server.wm.DisplayContentProto.TASKS;
 import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
@@ -117,6 +117,7 @@
 import static com.android.server.wm.RootWindowContainer.TAG_STATES;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
+import static com.android.server.wm.WindowContainerChildProto.DISPLAY_CONTENT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
@@ -2837,10 +2838,6 @@
 
         proto.write(ID, mDisplayId);
         mRootDisplayArea.dumpDebug(proto, ROOT_DISPLAY_AREA, logLevel);
-        for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
-            final ActivityStack stack = mTaskContainers.getChildAt(i);
-            stack.dumpDebug(proto, TASKS, logLevel);
-        }
         for (int i = mOverlayContainers.getChildCount() - 1; i >= 0; --i) {
             final WindowToken windowToken = mOverlayContainers.getChildAt(i);
             windowToken.dumpDebug(proto, OVERLAY_WINDOWS, logLevel);
@@ -2875,11 +2872,17 @@
         } else {
             proto.write(FOCUSED_ROOT_TASK_ID, INVALID_TASK_ID);
         }
+        proto.write(DISPLAY_READY, isReady());
 
         proto.end(token);
     }
 
     @Override
+    long getProtoFieldId() {
+        return DISPLAY_CONTENT;
+    }
+
+    @Override
     public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         super.dump(pw, prefix, dumpAll);
         pw.print(prefix);
@@ -4931,6 +4934,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..a094c81 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;
@@ -1469,8 +1470,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();
 
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 884f769..484a5a8 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -17,11 +17,15 @@
 package com.android.server.wm;
 
 
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
 import android.annotation.Nullable;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.ArrayMap;
+import android.util.Slog;
 import android.view.IWindow;
 import android.view.InputApplicationHandle;
 
@@ -33,12 +37,15 @@
  * the host window to send pointerDownOutsideFocus.
  */
 class EmbeddedWindowController {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "EmbeddedWindowController" : TAG_WM;
     /* maps input token to an embedded window */
     private ArrayMap<IBinder /*input token */, EmbeddedWindow> mWindows = new ArrayMap<>();
-    private final Object mWmLock;
+    private final Object mGlobalLock;
+    private final ActivityTaskManagerService mAtmService;
 
-    EmbeddedWindowController(Object wmLock) {
-        mWmLock = wmLock;
+    EmbeddedWindowController(ActivityTaskManagerService atmService) {
+        mAtmService = atmService;
+        mGlobalLock = atmService.getGlobalLock();
     }
 
     /**
@@ -46,13 +53,14 @@
      *
      * @param inputToken input channel token passed in by the embedding process when it requests
      *                   the server to add an input channel to the embedded surface.
-     * @param embeddedWindow An {@link EmbeddedWindow} object to add to this controller.
+     * @param window An {@link EmbeddedWindow} object to add to this controller.
      */
-    void add(IBinder inputToken, EmbeddedWindow embeddedWindow) {
+    void add(IBinder inputToken, EmbeddedWindow window) {
         try {
-            mWindows.put(inputToken, embeddedWindow);
-            embeddedWindow.mClient.asBinder().linkToDeath(()-> {
-                synchronized (mWmLock) {
+            mWindows.put(inputToken, window);
+            updateProcessController(window);
+            window.mClient.asBinder().linkToDeath(()-> {
+                synchronized (mGlobalLock) {
                     mWindows.remove(inputToken);
                 }
             }, 0);
@@ -62,6 +70,23 @@
         }
     }
 
+    /**
+     * Track the host activity in the embedding process so we can determine if the
+     * process is currently showing any UI to the user.
+     */
+    private void updateProcessController(EmbeddedWindow window) {
+        if (window.mHostActivityRecord == null) {
+            return;
+        }
+        final WindowProcessController processController =
+                mAtmService.getProcessController(window.mOwnerPid, window.mOwnerUid);
+        if (processController == null) {
+            Slog.w(TAG, "Could not find the embedding process.");
+        } else {
+            processController.addHostActivity(window.mHostActivityRecord);
+        }
+    }
+
     WindowState getHostWindow(IBinder inputToken) {
         EmbeddedWindow embeddedWindow = mWindows.get(inputToken);
         return embeddedWindow != null ? embeddedWindow.mHostWindowState : null;
@@ -76,7 +101,7 @@
         }
     }
 
-    void removeWindowsWithHost(WindowState host) {
+    void onWindowRemoved(WindowState host) {
         for (int i = mWindows.size() - 1; i >= 0; i--) {
             if (mWindows.valueAt(i).mHostWindowState == host) {
                 mWindows.removeAt(i);
@@ -88,9 +113,23 @@
         return mWindows.get(inputToken);
     }
 
+    void onActivityRemoved(ActivityRecord activityRecord) {
+        for (int i = mWindows.size() - 1; i >= 0; i--) {
+            final EmbeddedWindow window = mWindows.valueAt(i);
+            if (window.mHostActivityRecord == activityRecord) {
+                final WindowProcessController processController =
+                        mAtmService.getProcessController(window.mOwnerPid, window.mOwnerUid);
+                if (processController != null) {
+                    processController.removeHostActivity(activityRecord);
+                }
+            }
+        }
+    }
+
     static class EmbeddedWindow {
         final IWindow mClient;
         @Nullable final WindowState mHostWindowState;
+        @Nullable final ActivityRecord mHostActivityRecord;
         final int mOwnerUid;
         final int mOwnerPid;
 
@@ -107,6 +146,8 @@
                 int ownerPid) {
             mClient = clientToken;
             mHostWindowState = hostWindowState;
+            mHostActivityRecord = (mHostWindowState != null) ? mHostWindowState.mActivityRecord
+                    : null;
             mOwnerUid = ownerUid;
             mOwnerPid = ownerPid;
         }
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 58aefdc..ada6d47 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -274,7 +274,7 @@
             // window crop of the surface controls (including the leash) until the client finishes
             // drawing the new frame of the new orientation. Although we cannot defer the reparent
             // operation, it is fine, because reparent won't cause any visual effect.
-            final SurfaceControl barrier = mWin.getDeferTransactionBarrier();
+            final SurfaceControl barrier = mWin.getClientViewRootSurface();
             t.deferTransactionUntil(mWin.getSurfaceControl(), barrier, frameNumber);
             t.deferTransactionUntil(leash, barrier, frameNumber);
         }
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index e1906da..6ff029b 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -18,10 +18,14 @@
 
 import static android.view.InsetsState.ITYPE_CAPTION_BAR;
 import static android.view.InsetsState.ITYPE_IME;
+import static android.view.InsetsState.ITYPE_INVALID;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
 import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
 import static android.view.ViewRootImpl.sNewInsetsMode;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -32,6 +36,7 @@
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.InsetsState.InternalInsetsType;
+import android.view.WindowManager;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -74,15 +79,40 @@
      * @param target The client we dispatch the state to.
      * @return The state stripped of the necessary information.
      */
-    InsetsState getInsetsForDispatch(WindowState target) {
+    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);
+    }
 
+    InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
+        final @InternalInsetsType int type = getInsetsTypeForWindowType(attrs.type);
+        if (type == ITYPE_INVALID) {
+            return mState;
+        }
+        return getInsetsForType(type);
+    }
+
+    @InternalInsetsType
+    private static int getInsetsTypeForWindowType(int type) {
+        switch(type) {
+            case TYPE_STATUS_BAR:
+                return ITYPE_STATUS_BAR;
+            case TYPE_NAVIGATION_BAR:
+                return ITYPE_NAVIGATION_BAR;
+            case TYPE_INPUT_METHOD:
+                return ITYPE_IME;
+            default:
+                return ITYPE_INVALID;
+        }
+    }
+
+    private InsetsState getInsetsForType(@InternalInsetsType int type) {
         final InsetsState state = new InsetsState();
         state.set(mState);
-        final int type = provider.getSource().getType();
         state.removeSource(type);
 
         // Navigation bar doesn't get influenced by anything else
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 5cd0169..3e5cb50 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -820,15 +820,19 @@
         private @AnimationType int mLastAnimationType;
         private final boolean mIsRecentTaskInvisible;
         private RemoteAnimationTarget mTarget;
-        private final Point mPosition = new Point();
         private final Rect mBounds = new Rect();
+        // The bounds of the target relative to its parent.
+        private Rect mLocalBounds = new Rect();
 
         TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) {
             mTask = task;
             mIsRecentTaskInvisible = isRecentTaskInvisible;
-            final WindowContainer container = mTask.getParent();
-            mBounds.set(container.getDisplayedBounds());
-            mPosition.set(mBounds.left, mBounds.top);
+            mBounds.set(mTask.getDisplayedBounds());
+
+            mLocalBounds.set(mBounds);
+            Point tmpPos = new Point();
+            mTask.getRelativeDisplayedPosition(tmpPos);
+            mLocalBounds.offsetTo(tmpPos.x, tmpPos.y);
         }
 
         RemoteAnimationTarget createRemoteAnimationTarget() {
@@ -847,8 +851,9 @@
                     : MODE_CLOSING;
             mTarget = new RemoteAnimationTarget(mTask.mTaskId, mode, mCapturedLeash,
                     !topApp.fillsParent(), mainWindow.mWinAnimator.mLastClipRect,
-                    insets, mTask.getPrefixOrderIndex(), mPosition, mBounds,
-                    mTask.getWindowConfiguration(), mIsRecentTaskInvisible, null, null);
+                    insets, mTask.getPrefixOrderIndex(), new Point(mBounds.left, mBounds.top),
+                    mLocalBounds, mBounds, mTask.getWindowConfiguration(),
+                    mIsRecentTaskInvisible, null, null);
             return mTarget;
         }
 
@@ -862,8 +867,8 @@
                 @AnimationType int type, OnAnimationFinishedCallback finishCallback) {
             // Restore z-layering, position and stack crop until client has a chance to modify it.
             t.setLayer(animationLeash, mTask.getPrefixOrderIndex());
-            t.setPosition(animationLeash, mPosition.x, mPosition.y);
-            mTmpRect.set(mBounds);
+            t.setPosition(animationLeash, mLocalBounds.left, mLocalBounds.top);
+            mTmpRect.set(mLocalBounds);
             mTmpRect.offsetTo(0, 0);
             t.setWindowCrop(animationLeash, mTmpRect);
             mCapturedLeash = animationLeash;
@@ -897,7 +902,7 @@
                 pw.print(prefix); pw.println("Target: null");
             }
             pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible);
-            pw.println("mPosition=" + mPosition);
+            pw.println("mLocalBounds=" + mLocalBounds);
             pw.println("mBounds=" + mBounds);
             pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible);
         }
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 0eb9daf..35f8d34 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -80,16 +80,17 @@
      *
      * @param windowContainer The windows to animate.
      * @param position The position app bounds, in screen coordinates.
+     * @param localBounds The bounds of the app relative to its parent.
      * @param stackBounds The stack bounds of the app relative to position.
      * @param startBounds The stack bounds before the transition, in screen coordinates
      * @return The record representing animation(s) to run on the app.
      */
     RemoteAnimationRecord createRemoteAnimationRecord(WindowContainer windowContainer,
-            Point position, Rect stackBounds, Rect startBounds) {
+            Point position, Rect localBounds, Rect stackBounds, Rect startBounds) {
         ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAnimationAdapter(): container=%s",
                 windowContainer);
-        final RemoteAnimationRecord adapters =
-                new RemoteAnimationRecord(windowContainer, position, stackBounds, startBounds);
+        final RemoteAnimationRecord adapters = new RemoteAnimationRecord(windowContainer, position,
+                localBounds, stackBounds, startBounds);
         mPendingAnimations.add(adapters);
         return adapters;
     }
@@ -355,17 +356,18 @@
         final WindowContainer mWindowContainer;
         final Rect mStartBounds;
 
-        RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect endBounds,
-                Rect startBounds) {
+        RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect localBounds,
+                Rect endBounds, Rect startBounds) {
             mWindowContainer = windowContainer;
-            mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, endBounds);
+            mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds);
             if (startBounds != null) {
                 mStartBounds = new Rect(startBounds);
                 mTmpRect.set(startBounds);
                 mTmpRect.offsetTo(0, 0);
                 if (mRemoteAnimationAdapter.getChangeNeedsSnapshot()) {
                     mThumbnailAdapter =
-                            new RemoteAnimationAdapterWrapper(this, new Point(0, 0), mTmpRect);
+                            new RemoteAnimationAdapterWrapper(this, new Point(0, 0), localBounds,
+                                    mTmpRect);
                 }
             } else {
                 mStartBounds = null;
@@ -401,12 +403,14 @@
         private OnAnimationFinishedCallback mCapturedFinishCallback;
         private @AnimationType int mAnimationType;
         final Point mPosition = new Point();
+        final Rect mLocalBounds;
         final Rect mStackBounds = new Rect();
 
         RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position,
-                Rect stackBounds) {
+                Rect localBounds, Rect stackBounds) {
             mRecord = record;
             mPosition.set(position.x, position.y);
+            mLocalBounds = localBounds;
             mStackBounds.set(stackBounds);
         }
 
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 0fa135b..15a49a7 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -66,11 +66,9 @@
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.server.wm.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
 import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
-import static com.android.server.wm.RootWindowContainerProto.DISPLAYS;
 import static com.android.server.wm.RootWindowContainerProto.IS_HOME_RECENTS_COMPONENT;
 import static com.android.server.wm.RootWindowContainerProto.KEYGUARD_CONTROLLER;
 import static com.android.server.wm.RootWindowContainerProto.PENDING_ACTIVITIES;
-import static com.android.server.wm.RootWindowContainerProto.WINDOWS;
 import static com.android.server.wm.RootWindowContainerProto.WINDOW_CONTAINER;
 import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE;
 import static com.android.server.wm.Task.REPARENT_MOVE_STACK_TO_FRONT;
@@ -1278,18 +1276,6 @@
 
         final long token = proto.start(fieldId);
         super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
-        if (mWmService.mDisplayReady) {
-            final int count = mChildren.size();
-            for (int i = 0; i < count; ++i) {
-                final DisplayContent displayContent = mChildren.get(i);
-                displayContent.dumpDebug(proto, DISPLAYS, logLevel);
-            }
-        }
-        if (logLevel == WindowTraceLogLevel.ALL) {
-            forAllWindows((w) -> {
-                w.dumpDebug(proto, WINDOWS, logLevel);
-            }, true);
-        }
 
         mStackSupervisor.getKeyguardController().dumpDebug(proto, KEYGUARD_CONTROLLER);
         proto.write(IS_HOME_RECENTS_COMPONENT,
diff --git a/services/core/java/com/android/server/wm/SeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java
index 024da88..8e1c632 100644
--- a/services/core/java/com/android/server/wm/SeamlessRotator.java
+++ b/services/core/java/com/android/server/wm/SeamlessRotator.java
@@ -118,9 +118,9 @@
         finish(t, win);
         if (win.mWinAnimator.mSurfaceController != null && !timeout) {
             t.deferTransactionUntil(win.mSurfaceControl,
-                    win.getDeferTransactionBarrier(), win.getFrameNumber());
+                    win.getClientViewRootSurface(), win.getFrameNumber());
             t.deferTransactionUntil(win.mWinAnimator.mSurfaceController.mSurfaceControl,
-                    win.getDeferTransactionBarrier(), win.getFrameNumber());
+                    win.getClientViewRootSurface(), win.getFrameNumber());
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 88d4fb3..b2db99b 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -85,6 +85,7 @@
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
+import static com.android.server.wm.WindowContainerChildProto.TASK;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -3970,12 +3971,12 @@
 
     boolean isControlledByTaskOrganizer() {
         final Task rootTask = getRootTask();
-        return rootTask == this && rootTask.mTaskOrganizer != null
-                // TODO(task-hierarchy): Figure out how to control nested tasks.
-                // For now, if this is in a tile let WM drive.
-                && !(rootTask instanceof TaskTile)
-                && !(rootTask instanceof ActivityStack
-                        && ((ActivityStack) rootTask).getTile() != null);
+        // if the rootTask is a "child" of a tile, then don't consider it a root task.
+        // TODO: remove this along with removing tile.
+        if (((ActivityStack) rootTask).getTile() != null) {
+            return false;
+        }
+        return rootTask == this && rootTask.mTaskOrganizer != null;
     }
 
     @Override
@@ -4121,4 +4122,9 @@
     SurfaceControl.Transaction getMainWindowSizeChangeTransaction() {
         return mMainWindowSizeChangeTransaction;
     }
+
+    @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 b38c18b..4093fe5 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -128,7 +128,7 @@
 
         void removeTask(Task t) {
             try {
-                mOrganizer.taskVanished(t.getRemoteToken());
+                mOrganizer.taskVanished(t.getTaskInfo());
             } catch (Exception e) {
                 Slog.e(TAG, "Exception sending taskVanished callback" + e);
             }
diff --git a/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java b/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
index bd70599..f467015 100644
--- a/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
@@ -92,7 +92,7 @@
      */
     RemoteAnimationTarget createRemoteAnimationTarget() {
         mTarget = new RemoteAnimationTarget(-1, -1, getLeash(), false, null, null,
-                mWallpaperToken.getPrefixOrderIndex(), new Point(), null,
+                mWallpaperToken.getPrefixOrderIndex(), new Point(), null, null,
                 mWallpaperToken.getWindowConfiguration(), true, null, null);
         return mTarget;
     }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 10d34b5..e8897e1 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -40,6 +40,7 @@
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
+import static com.android.server.wm.WindowContainerChildProto.WINDOW_CONTAINER;
 import static com.android.server.wm.WindowContainerProto.CONFIGURATION_CONTAINER;
 import static com.android.server.wm.WindowContainerProto.ORIENTATION;
 import static com.android.server.wm.WindowContainerProto.SURFACE_ANIMATOR;
@@ -1819,9 +1820,25 @@
         if (mSurfaceAnimator.isAnimating()) {
             mSurfaceAnimator.dumpDebug(proto, SURFACE_ANIMATOR);
         }
+
+        // add children to proto
+        for (int i = 0; i < getChildCount(); i++) {
+            final long childToken = proto.start(WindowContainerProto.CHILDREN);
+            final E child = getChildAt(i);
+            child.dumpDebug(proto, child.getProtoFieldId(), logLevel);
+            proto.end(childToken);
+        }
         proto.end(token);
     }
 
+    /**
+     * @return a proto field id to identify where to add the derived class to the generic window
+     * container proto.
+     */
+    long getProtoFieldId() {
+        return WINDOW_CONTAINER;
+    }
+
     private ForAllWindowsConsumerWrapper obtainConsumerWrapper(Consumer<WindowState> consumer) {
         ForAllWindowsConsumerWrapper wrapper = mConsumerWrapperPool.acquire();
         if (wrapper == null) {
@@ -2086,9 +2103,11 @@
 
         // Delaying animation start isn't compatible with remote animations at all.
         if (controller != null && !mSurfaceAnimator.isAnimationStartDelayed()) {
+            final Rect localBounds = new Rect(mTmpRect);
+            localBounds.offsetTo(mTmpPoint.x, mTmpPoint.y);
             final RemoteAnimationController.RemoteAnimationRecord adapters =
-                    controller.createRemoteAnimationRecord(this, mTmpPoint, mTmpRect,
-                            (isChanging ? mSurfaceFreezer.mFreezeBounds : null));
+                    controller.createRemoteAnimationRecord(this, mTmpPoint, localBounds,
+                            mTmpRect, (isChanging ? mSurfaceFreezer.mFreezeBounds : null));
             resultAdapters = new Pair<>(adapters.mAdapter, adapters.mThumbnailAdapter);
         } else if (isChanging) {
             final float durationScale = mWmService.getTransitionAnimationScaleLocked();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 3f4f629..ecbbb03 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1287,7 +1287,7 @@
         mConstants.start(new HandlerExecutor(mH));
 
         LocalServices.addService(WindowManagerInternal.class, new LocalService());
-        mEmbeddedWindowController = new EmbeddedWindowController(mGlobalLock);
+        mEmbeddedWindowController = new EmbeddedWindowController(mAtmService);
 
         mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources(
                 mContext.getResources());
@@ -1900,7 +1900,7 @@
         if (dc.mCurrentFocus == null) {
             dc.mWinRemovedSinceNullFocus.add(win);
         }
-        mEmbeddedWindowController.removeWindowsWithHost(win);
+        mEmbeddedWindowController.onWindowRemoved(win);
         mPendingRemove.remove(win);
         mResizingWindows.remove(win);
         updateNonSystemOverlayWindowsVisibilityIfNeeded(win, false /* surfaceShown */);
@@ -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
@@ -2610,7 +2606,8 @@
                 if (type == TYPE_WALLPAPER) {
                     new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens);
                 } else {
-                    new WindowToken(this, binder, type, true, dc, callerCanManageAppTokens);
+                    new WindowToken(this, binder, type, true, dc, callerCanManageAppTokens,
+                            false /* roundedCornerOverlay */, fromClientToken);
                 }
             }
         } finally {
@@ -4661,6 +4658,7 @@
         public static final int RECOMPUTE_FOCUS = 61;
         public static final int ON_POINTER_DOWN_OUTSIDE_FOCUS = 62;
         public static final int LAYOUT_AND_ASSIGN_WINDOW_LAYERS_IF_NEEDED = 63;
+        public static final int WINDOW_STATE_BLAST_SYNC_TIMEOUT = 64;
 
         /**
          * Used to denote that an integer field in a message will not be used.
@@ -5041,6 +5039,13 @@
                     }
                     break;
                 }
+                case WINDOW_STATE_BLAST_SYNC_TIMEOUT: {
+                    synchronized (mGlobalLock) {
+                      final WindowState ws = (WindowState) msg.obj;
+                      ws.finishDrawing(null);
+                    }
+                    break;
+                }
             }
             if (DEBUG_WINDOW_TRACE) {
                 Slog.v(TAG_WM, "handleMessage: exit");
@@ -6126,7 +6131,7 @@
                 }
 
                 mRoot.forAllWindows((w) -> {
-                    if ((!visibleOnly || w.mWinAnimator.getShown())
+                    if ((!visibleOnly || w.isVisible())
                             && (!appsOnly || w.mActivityRecord != null)) {
                         windows.add(w);
                     }
@@ -8010,9 +8015,9 @@
     }
 
     @Override
-    public void getWindowInsets(WindowManager.LayoutParams attrs,
+    public boolean getWindowInsets(WindowManager.LayoutParams attrs,
             int displayId, Rect outContentInsets, Rect outStableInsets,
-            DisplayCutout.ParcelableWrapper displayCutout) {
+            DisplayCutout.ParcelableWrapper outDisplayCutout, InsetsState outInsetsState) {
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
@@ -8022,8 +8027,13 @@
                             + "could not be found!");
                 }
                 final WindowToken windowToken = dc.getWindowToken(attrs.token);
-                dc.getDisplayPolicy().getLayoutHint(attrs, windowToken, mTmpRect /* outFrame */,
-                        outContentInsets, outStableInsets, displayCutout);
+                final InsetsStateController insetsStateController =
+                        dc.getInsetsStateController();
+                outInsetsState.set(insetsStateController.getInsetsForWindowMetrics(attrs));
+
+                return dc.getDisplayPolicy().getLayoutHint(attrs, windowToken,
+                        mTmpRect /* outFrame */, outContentInsets, outStableInsets,
+                        outDisplayCutout);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 03dc4c9..833bb83 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -41,6 +41,7 @@
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityThread;
 import android.app.IApplicationThread;
 import android.app.ProfilerInfo;
@@ -185,6 +186,13 @@
     // Registered display id as a listener to override config change
     private int mDisplayId;
     private ActivityRecord mConfigActivityRecord;
+    /**
+     * Activities that hosts some UI drawn by the current process. The activities live
+     * in another process. This is used to check if the process is currently showing anything
+     * visible to the user.
+     */
+    @Nullable
+    private final ArrayList<ActivityRecord> mHostActivities = new ArrayList<>();
 
     /** Whether our process is currently running a {@link RecentsAnimation} */
     private boolean mRunningRecentsAnimation;
@@ -672,6 +680,23 @@
                     return true;
                 }
             }
+            if (isEmbedded()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @return {@code true} if this process is rendering content on to a window shown by
+     * another process.
+     */
+    private boolean isEmbedded() {
+        for (int i = mHostActivities.size() - 1; i >= 0; --i) {
+            final ActivityRecord r = mHostActivities.get(i);
+            if (r.isInterestingToUserLocked()) {
+                return true;
+            }
         }
         return false;
     }
@@ -814,6 +839,19 @@
         }
     }
 
+    /** Adds an activity that hosts UI drawn by the current process. */
+    void addHostActivity(ActivityRecord r) {
+        if (mHostActivities.contains(r)) {
+            return;
+        }
+        mHostActivities.add(r);
+    }
+
+    /** Removes an activity that hosts UI drawn by the current process. */
+    void removeHostActivity(ActivityRecord r) {
+        mHostActivities.remove(r);
+    }
+
     public interface ComputeOomAdjCallback {
         void onVisibleActivity();
         void onPausedActivity();
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 27f1ca0..fc28cd5 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -118,6 +118,7 @@
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
+import static com.android.server.wm.WindowContainerChildProto.WINDOW;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
@@ -137,6 +138,7 @@
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
 import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
+import static com.android.server.wm.WindowManagerService.H.WINDOW_STATE_BLAST_SYNC_TIMEOUT;
 import static com.android.server.wm.WindowStateAnimator.COMMIT_DRAW_PENDING;
 import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
 import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
@@ -145,7 +147,6 @@
 import static com.android.server.wm.WindowStateProto.ANIMATING_EXIT;
 import static com.android.server.wm.WindowStateProto.ANIMATOR;
 import static com.android.server.wm.WindowStateProto.ATTRIBUTES;
-import static com.android.server.wm.WindowStateProto.CHILD_WINDOWS;
 import static com.android.server.wm.WindowStateProto.DESTROYING;
 import static com.android.server.wm.WindowStateProto.DISPLAY_ID;
 import static com.android.server.wm.WindowStateProto.FINISHED_SEAMLESS_ROTATION_FRAME;
@@ -669,6 +670,8 @@
      */
     int mFrameRateSelectionPriority = RefreshRatePolicy.LAYER_PRIORITY_UNSET;
 
+    static final int BLAST_TIMEOUT_DURATION = 5000; /* milliseconds */
+
     /**
      * @return The insets state as requested by the client, i.e. the dispatched insets state
      *         for which the visibilities are overridden with what the client requested.
@@ -3758,9 +3761,6 @@
         mSurfacePosition.dumpDebug(proto, SURFACE_POSITION);
         mWinAnimator.dumpDebug(proto, ANIMATOR);
         proto.write(ANIMATING_EXIT, mAnimatingExit);
-        for (int i = 0; i < mChildren.size(); i++) {
-            mChildren.get(i).dumpDebug(proto, CHILD_WINDOWS, logLevel);
-        }
         proto.write(REQUESTED_WIDTH, mRequestedWidth);
         proto.write(REQUESTED_HEIGHT, mRequestedHeight);
         proto.write(VIEW_VISIBILITY, mViewVisibility);
@@ -3779,6 +3779,11 @@
     }
 
     @Override
+    long getProtoFieldId() {
+        return WINDOW;
+    }
+
+    @Override
     public void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
         proto.write(HASH_CODE, System.identityHashCode(this));
@@ -5660,8 +5665,8 @@
         return mSession.mPid == pid && isNonToastOrStarting() && isVisibleNow();
     }
 
-    SurfaceControl getDeferTransactionBarrier() {
-        return mWinAnimator.getDeferTransactionBarrier();
+    SurfaceControl getClientViewRootSurface() {
+        return mWinAnimator.getClientViewRootSurface();
     }
 
     @Override
@@ -5671,6 +5676,11 @@
         mWaitingListener = waitingListener;
         mWaitingSyncId = waitingId;
         mUsingBLASTSyncTransaction = true;
+
+        mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
+        mWmService.mH.sendNewMessageDelayed(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this,
+            BLAST_TIMEOUT_DURATION);
+
         return true;
     }
 
@@ -5678,6 +5688,8 @@
         if (!mUsingBLASTSyncTransaction) {
             return mWinAnimator.finishDrawingLocked(postDrawTransaction);
         }
+
+        mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
         if (postDrawTransaction == null) {
             postDrawTransaction = new SurfaceControl.Transaction();
         }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 81d0e3e..79dc536 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -383,8 +383,9 @@
             // Make sure to reparent any children of the new surface back to the preserved
             // surface before destroying it.
             if (mSurfaceController != null && mPendingDestroySurface != null) {
-                mPostDrawTransaction.reparentChildren(mSurfaceController.mSurfaceControl,
-                        mPendingDestroySurface.mSurfaceControl).apply();
+                mPostDrawTransaction.reparentChildren(
+                    mSurfaceController.getClientViewRootSurface(),
+                    mPendingDestroySurface.mSurfaceControl).apply();
             }
             destroySurfaceLocked();
             mSurfaceDestroyDeferred = true;
@@ -413,9 +414,9 @@
                 // child layers need to be reparented to the new surface to make this
                 // transparent to the app.
                 if (mWin.mActivityRecord == null || mWin.mActivityRecord.isRelaunching() == false) {
-                    mPostDrawTransaction.reparentChildren(mPendingDestroySurface.mSurfaceControl,
-                            mSurfaceController.mSurfaceControl)
-                            .apply();
+                    mPostDrawTransaction.reparentChildren(
+                        mPendingDestroySurface.getClientViewRootSurface(),
+                        mSurfaceController.mSurfaceControl).apply();
                 }
             }
         }
@@ -875,7 +876,7 @@
 
         if (mSurfaceResized && (mAttrType == TYPE_BASE_APPLICATION) &&
             (task != null) && (task.getMainWindowSizeChangeTransaction() != null)) {
-            mSurfaceController.deferTransactionUntil(mWin.getDeferTransactionBarrier(),
+            mSurfaceController.deferTransactionUntil(mWin.getClientViewRootSurface(),
                     mWin.getFrameNumber());
             SurfaceControl.mergeToGlobalTransaction(task.getMainWindowSizeChangeTransaction());
             task.setMainWindowSizeChangeTransaction(null);
@@ -1012,7 +1013,7 @@
                         // the WS position is reset (so the stack position is shown) at the same
                         // time that the buffer size changes.
                         setOffsetPositionForStackResize(false);
-                        mSurfaceController.deferTransactionUntil(mWin.getDeferTransactionBarrier(),
+                        mSurfaceController.deferTransactionUntil(mWin.getClientViewRootSurface(),
                                 mWin.getFrameNumber());
                     } else {
                         final ActivityStack stack = mWin.getRootTask();
@@ -1043,7 +1044,7 @@
         // comes in at the new size (normally position and crop are unfrozen).
         // deferTransactionUntil accomplishes this for us.
         if (wasForceScaled && !mForceScaleUntilResize) {
-            mSurfaceController.deferTransactionUntil(mWin.getDeferTransactionBarrier(),
+            mSurfaceController.deferTransactionUntil(mWin.getClientViewRootSurface(),
                     mWin.getFrameNumber());
             mSurfaceController.forceScaleableInTransaction(false);
         }
@@ -1288,8 +1289,9 @@
         if (mPendingDestroySurface != null && mDestroyPreservedSurfaceUponRedraw) {
             final SurfaceControl pendingSurfaceControl = mPendingDestroySurface.mSurfaceControl;
             mPostDrawTransaction.reparent(pendingSurfaceControl, null);
-            mPostDrawTransaction.reparentChildren(pendingSurfaceControl,
-                    mSurfaceController.mSurfaceControl);
+            mPostDrawTransaction.reparentChildren(
+                mPendingDestroySurface.getClientViewRootSurface(),
+                mSurfaceController.mSurfaceControl);
         }
 
         SurfaceControl.mergeToGlobalTransaction(mPostDrawTransaction);
@@ -1521,10 +1523,10 @@
         mOffsetPositionForStackResize = offsetPositionForStackResize;
     }
 
-    SurfaceControl getDeferTransactionBarrier() {
+    SurfaceControl getClientViewRootSurface() {
         if (!hasSurface()) {
             return null;
         }
-        return mSurfaceController.getDeferTransactionBarrier();
+        return mSurfaceController.getClientViewRootSurface();
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 383c0d9..d7c97b9 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -533,7 +533,15 @@
         return mSurfaceH;
     }
 
-    SurfaceControl getDeferTransactionBarrier() {
+    /**
+     * Returns the Surface which the client-framework ViewRootImpl will be using.
+     * This is either the WSA SurfaceControl or it's BLAST child surface.
+     * This has too main uses:
+     * 1. This is the Surface the client will add children to, we use this to make
+     *    sure we don't reparent the BLAST surface itself when calling reparentChildren
+     * 2. We use this as the barrier Surface for some deferTransaction operations.
+     */
+    SurfaceControl getClientViewRootSurface() {
         if (mBLASTSurfaceControl != null) {
             return mBLASTSurfaceControl;
         }
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 98d9c8fc..b7c6af2 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -28,13 +28,13 @@
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
+import static com.android.server.wm.WindowContainerChildProto.WINDOW_TOKEN;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 import static com.android.server.wm.WindowTokenProto.HASH_CODE;
 import static com.android.server.wm.WindowTokenProto.PAUSED;
 import static com.android.server.wm.WindowTokenProto.WAITING_TO_SHOW;
-import static com.android.server.wm.WindowTokenProto.WINDOWS;
 import static com.android.server.wm.WindowTokenProto.WINDOW_CONTAINER;
 
 import android.annotation.CallSuper;
@@ -50,6 +50,7 @@
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.protolog.common.ProtoLog;
 
@@ -99,7 +100,8 @@
     private Configuration mLastReportedConfig;
     private int mLastReportedDisplay = INVALID_DISPLAY;
 
-    private final boolean mFromClientToken;
+    @VisibleForTesting
+    final boolean mFromClientToken;
 
     /**
      * Used to fix the transform of the token to be rotated to a rotation different than it's
@@ -180,13 +182,13 @@
     WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
             DisplayContent dc, boolean ownerCanManageAppTokens) {
         this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens,
-                false /* roundedCornersOverlay */);
+                false /* roundedCornerOverlay */);
     }
 
     WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
-            DisplayContent dc, boolean ownerCanManageAppTokens, boolean fromClientToken) {
+            DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) {
         this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens,
-                false /* roundedCornersOverlay */, fromClientToken);
+                roundedCornerOverlay, false /* fromClientToken */);
     }
 
     WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
@@ -539,15 +541,16 @@
         final long token = proto.start(fieldId);
         super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
         proto.write(HASH_CODE, System.identityHashCode(this));
-        for (int i = 0; i < mChildren.size(); i++) {
-            final WindowState w = mChildren.get(i);
-            w.dumpDebug(proto, WINDOWS, logLevel);
-        }
         proto.write(WAITING_TO_SHOW, waitingToShow);
         proto.write(PAUSED, paused);
         proto.end(token);
     }
 
+    @Override
+    long getProtoFieldId() {
+        return WINDOW_TOKEN;
+    }
+
     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         super.dump(pw, prefix, dumpAll);
         pw.print(prefix); pw.print("windows="); pw.println(mChildren);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c58eae1..312d2d2 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -148,10 +148,6 @@
 import android.app.admin.SystemUpdateInfo;
 import android.app.admin.SystemUpdatePolicy;
 import android.app.backup.IBackupManager;
-import android.app.timedetector.ManualTimeSuggestion;
-import android.app.timedetector.TimeDetector;
-import android.app.timezonedetector.ManualTimeZoneSuggestion;
-import android.app.timezonedetector.TimeZoneDetector;
 import android.app.trust.TrustManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.compat.annotation.ChangeId;
@@ -2118,14 +2114,6 @@
             return mContext.getSystemService(AlarmManager.class);
         }
 
-        TimeDetector getTimeDetector() {
-            return mContext.getSystemService(TimeDetector.class);
-        }
-
-        TimeZoneDetector getTimeZoneDetector() {
-            return mContext.getSystemService(TimeZoneDetector.class);
-        }
-
         ConnectivityManager getConnectivityManager() {
             return mContext.getSystemService(ConnectivityManager.class);
         }
@@ -11730,15 +11718,11 @@
         if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) == 1) {
             return false;
         }
-        ManualTimeSuggestion manualTimeSuggestion = TimeDetector.createManualTimeSuggestion(
-                millis, "DevicePolicyManagerService: setTime");
-        mInjector.binderWithCleanCallingIdentity(
-                () -> mInjector.getTimeDetector().suggestManualTime(manualTimeSuggestion));
-
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_TIME)
                 .setAdmin(who)
                 .write();
+        mInjector.binderWithCleanCallingIdentity(() -> mInjector.getAlarmManager().setTime(millis));
         return true;
     }
 
@@ -11750,11 +11734,8 @@
         if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) == 1) {
             return false;
         }
-        ManualTimeZoneSuggestion manualTimeZoneSuggestion =
-                TimeZoneDetector.createManualTimeZoneSuggestion(
-                        timeZone, "DevicePolicyManagerService: setTimeZone");
         mInjector.binderWithCleanCallingIdentity(() ->
-                mInjector.getTimeZoneDetector().suggestManualTimeZone(manualTimeZoneSuggestion));
+                mInjector.getAlarmManager().setTimeZone(timeZone));
 
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_TIME_ZONE)
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/MmsQueryHelper.java b/services/people/java/com/android/server/people/data/MmsQueryHelper.java
index 1e485c0..39dba9c 100644
--- a/services/people/java/com/android/server/people/data/MmsQueryHelper.java
+++ b/services/people/java/com/android/server/people/data/MmsQueryHelper.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.Binder;
 import android.provider.Telephony.BaseMmsColumns;
 import android.provider.Telephony.Mms;
 import android.telephony.PhoneNumberUtils;
@@ -71,31 +72,36 @@
         // NOTE: The field Mms.DATE is stored in seconds, not milliseconds.
         String[] selectionArgs = new String[] { Long.toString(sinceTime / MILLIS_PER_SECONDS) };
         boolean hasResults = false;
-        try (Cursor cursor = mContext.getContentResolver().query(
-                Mms.CONTENT_URI, projection, selection, selectionArgs, null)) {
-            if (cursor == null) {
-                Slog.w(TAG, "Cursor is null when querying MMS table.");
-                return false;
-            }
-            while (cursor.moveToNext()) {
-                // ID
-                int msgIdIndex = cursor.getColumnIndex(Mms._ID);
-                String msgId = cursor.getString(msgIdIndex);
+        Binder.allowBlockingForCurrentThread();
+        try {
+            try (Cursor cursor = mContext.getContentResolver().query(
+                    Mms.CONTENT_URI, projection, selection, selectionArgs, null)) {
+                if (cursor == null) {
+                    Slog.w(TAG, "Cursor is null when querying MMS table.");
+                    return false;
+                }
+                while (cursor.moveToNext()) {
+                    // ID
+                    int msgIdIndex = cursor.getColumnIndex(Mms._ID);
+                    String msgId = cursor.getString(msgIdIndex);
 
-                // Date
-                int dateIndex = cursor.getColumnIndex(Mms.DATE);
-                long date = cursor.getLong(dateIndex) * MILLIS_PER_SECONDS;
+                    // Date
+                    int dateIndex = cursor.getColumnIndex(Mms.DATE);
+                    long date = cursor.getLong(dateIndex) * MILLIS_PER_SECONDS;
 
-                // Message box
-                int msgBoxIndex = cursor.getColumnIndex(Mms.MESSAGE_BOX);
-                int msgBox = cursor.getInt(msgBoxIndex);
+                    // Message box
+                    int msgBoxIndex = cursor.getColumnIndex(Mms.MESSAGE_BOX);
+                    int msgBox = cursor.getInt(msgBoxIndex);
 
-                mLastMessageTimestamp = Math.max(mLastMessageTimestamp, date);
-                String address = getMmsAddress(msgId, msgBox);
-                if (address != null && addEvent(address, date, msgBox)) {
-                    hasResults = true;
+                    mLastMessageTimestamp = Math.max(mLastMessageTimestamp, date);
+                    String address = getMmsAddress(msgId, msgBox);
+                    if (address != null && addEvent(address, date, msgBox)) {
+                        hasResults = true;
+                    }
                 }
             }
+        } finally {
+            Binder.defaultBlockingForCurrentThread();
         }
         return hasResults;
     }
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/SmsQueryHelper.java b/services/people/java/com/android/server/people/data/SmsQueryHelper.java
index c38c846..a5eb3a5 100644
--- a/services/people/java/com/android/server/people/data/SmsQueryHelper.java
+++ b/services/people/java/com/android/server/people/data/SmsQueryHelper.java
@@ -19,6 +19,7 @@
 import android.annotation.WorkerThread;
 import android.content.Context;
 import android.database.Cursor;
+import android.os.Binder;
 import android.provider.Telephony.Sms;
 import android.provider.Telephony.TextBasedSmsColumns;
 import android.telephony.PhoneNumberUtils;
@@ -65,35 +66,40 @@
         String selection = Sms.DATE + " > ?";
         String[] selectionArgs = new String[] { Long.toString(sinceTime) };
         boolean hasResults = false;
-        try (Cursor cursor = mContext.getContentResolver().query(
-                Sms.CONTENT_URI, projection, selection, selectionArgs, null)) {
-            if (cursor == null) {
-                Slog.w(TAG, "Cursor is null when querying SMS table.");
-                return false;
-            }
-            while (cursor.moveToNext()) {
-                // ID
-                int msgIdIndex = cursor.getColumnIndex(Sms._ID);
-                String msgId = cursor.getString(msgIdIndex);
+        Binder.allowBlockingForCurrentThread();
+        try {
+            try (Cursor cursor = mContext.getContentResolver().query(
+                    Sms.CONTENT_URI, projection, selection, selectionArgs, null)) {
+                if (cursor == null) {
+                    Slog.w(TAG, "Cursor is null when querying SMS table.");
+                    return false;
+                }
+                while (cursor.moveToNext()) {
+                    // ID
+                    int msgIdIndex = cursor.getColumnIndex(Sms._ID);
+                    String msgId = cursor.getString(msgIdIndex);
 
-                // Date
-                int dateIndex = cursor.getColumnIndex(Sms.DATE);
-                long date = cursor.getLong(dateIndex);
+                    // Date
+                    int dateIndex = cursor.getColumnIndex(Sms.DATE);
+                    long date = cursor.getLong(dateIndex);
 
-                // Type
-                int typeIndex = cursor.getColumnIndex(Sms.TYPE);
-                int type = cursor.getInt(typeIndex);
+                    // Type
+                    int typeIndex = cursor.getColumnIndex(Sms.TYPE);
+                    int type = cursor.getInt(typeIndex);
 
-                // Address
-                int addressIndex = cursor.getColumnIndex(Sms.ADDRESS);
-                String address = PhoneNumberUtils.formatNumberToE164(
-                        cursor.getString(addressIndex), mCurrentCountryIso);
+                    // Address
+                    int addressIndex = cursor.getColumnIndex(Sms.ADDRESS);
+                    String address = PhoneNumberUtils.formatNumberToE164(
+                            cursor.getString(addressIndex), mCurrentCountryIso);
 
-                mLastMessageTimestamp = Math.max(mLastMessageTimestamp, date);
-                if (address != null && addEvent(address, date, type)) {
-                    hasResults = true;
+                    mLastMessageTimestamp = Math.max(mLastMessageTimestamp, date);
+                    if (address != null && addEvent(address, date, type)) {
+                        hasResults = true;
+                    }
                 }
             }
+        } finally {
+            Binder.defaultBlockingForCurrentThread();
         }
         return hasResults;
     }
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/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
index 6083ce34..95f4e83 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
@@ -847,8 +847,8 @@
         app.connectionGroup = connectionGroup;
         app.setProcState = procState;
         app.lastMemInfo = spy(new Debug.MemoryInfo());
-        doReturn((int) pss).when(app.lastMemInfo).getTotalPss();
-        doReturn((int) rss).when(app.lastMemInfo).getTotalRss();
+        app.lastPss = pss;
+        app.mLastRss = rss;
         return app;
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index 4722b39..e5ec1f7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -36,6 +36,7 @@
 import com.android.server.testables.TestableDeviceConfig;
 
 import org.junit.After;
+import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -267,9 +268,7 @@
 
     @Test
     public void useFreeze_doesNotListenToDeviceConfigChanges() throws InterruptedException {
-        if (!mCachedAppOptimizerUnderTest.isFreezerSupported()) {
-            return;
-        }
+        Assume.assumeTrue(mCachedAppOptimizerUnderTest.isFreezerSupported());
 
         assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo(
                 CachedAppOptimizer.DEFAULT_USE_FREEZER);
diff --git a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
index 16291b2..425c724 100644
--- a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
@@ -176,7 +176,8 @@
         CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext)
                         .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1)
                         .addTargetSdkChangeWithId(TARGET_SDK, 2)
-                        .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3).build();
+                        .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3)
+                        .addDisabledChangeWithId(4).build();
         IOverrideValidator overrideValidator = config.getOverrideValidator();
         when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
                 .thenReturn(ApplicationInfoBuilder.create()
@@ -190,6 +191,8 @@
                 overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME);
         OverrideAllowedState stateTargetSdkAfterChange =
                 overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME);
+        OverrideAllowedState stateDisabledChange =
+                overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME);
 
         assertThat(stateTargetSdkLessChange)
                 .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK_BEFORE));
@@ -197,6 +200,8 @@
                 .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK));
         assertThat(stateTargetSdkAfterChange)
                 .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK_AFTER));
+        assertThat(stateDisabledChange)
+                .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, -1));
     }
 
     @Test
@@ -343,21 +348,22 @@
     }
 
     @Test
-    public void getOverrideAllowedState_finalBuildDisabledChangeDebugApp_rejectOverride()
+    public void getOverrideAllowedState_finalBuildDisabledChangeDebugApp_allowOverride()
             throws Exception {
         CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
-                        .addDisabledChangeWithId(1).build();
+                .addDisabledChangeWithId(1).build();
         IOverrideValidator overrideValidator = config.getOverrideValidator();
         when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
                 .thenReturn(ApplicationInfoBuilder.create()
                         .withPackageName(PACKAGE_NAME)
+                        .withTargetSdk(TARGET_SDK)
                         .debuggable().build());
 
         OverrideAllowedState allowedState =
                 overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
 
         assertThat(allowedState)
-                .isEqualTo(new OverrideAllowedState(DISABLED_NON_TARGET_SDK, -1, -1));
+                .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, -1));
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 5ad81b2..c1bcf1f 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -22,8 +22,6 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.backup.IBackupManager;
-import android.app.timedetector.TimeDetector;
-import android.app.timezonedetector.TimeZoneDetector;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.Context;
 import android.content.Intent;
@@ -236,16 +234,6 @@
         AlarmManager getAlarmManager() {return services.alarmManager;}
 
         @Override
-        TimeDetector getTimeDetector() {
-            return services.timeDetector;
-        }
-
-        @Override
-        TimeZoneDetector getTimeZoneDetector() {
-            return services.timeZoneDetector;
-        }
-
-        @Override
         LockPatternUtils newLockPatternUtils() {
             return services.lockPatternUtils;
         }
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 ac818ea..2a6a24e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -67,9 +67,6 @@
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.admin.FactoryResetProtectionPolicy;
 import android.app.admin.PasswordMetrics;
-import android.app.timedetector.ManualTimeSuggestion;
-import android.app.timezonedetector.ManualTimeZoneSuggestion;
-import android.app.timezonedetector.TimeZoneDetector;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -3969,19 +3966,7 @@
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
         dpm.setTime(admin1, 0);
-
-        BaseMatcher<ManualTimeSuggestion> hasZeroTime = new BaseMatcher<ManualTimeSuggestion>() {
-            @Override
-            public boolean matches(Object item) {
-                final ManualTimeSuggestion suggestion = (ManualTimeSuggestion) item;
-                return suggestion.getUtcTime().getValue() == 0;
-            }
-            @Override
-            public void describeTo(Description description) {
-                description.appendText("ManualTimeSuggestion{utcTime.value=0}");
-            }
-        };
-        verify(getServices().timeDetector).suggestManualTime(argThat(hasZeroTime));
+        verify(getServices().alarmManager).setTime(0);
     }
 
     public void testSetTimeFailWithPO() throws Exception {
@@ -3993,19 +3978,7 @@
         setupProfileOwner();
         configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
         dpm.setTime(admin1, 0);
-
-        BaseMatcher<ManualTimeSuggestion> hasZeroTime = new BaseMatcher<ManualTimeSuggestion>() {
-            @Override
-            public boolean matches(Object item) {
-                final ManualTimeSuggestion suggestion = (ManualTimeSuggestion) item;
-                return suggestion.getUtcTime().getValue() == 0;
-            }
-            @Override
-            public void describeTo(Description description) {
-                description.appendText("ManualTimeSuggestion{utcTime.value=0}");
-            }
-        };
-        verify(getServices().timeDetector).suggestManualTime(argThat(hasZeroTime));
+        verify(getServices().alarmManager).setTime(0);
     }
 
     public void testSetTimeWithAutoTimeOn() throws Exception {
@@ -4020,9 +3993,7 @@
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
         dpm.setTimeZone(admin1, "Asia/Shanghai");
-        ManualTimeZoneSuggestion suggestion =
-                TimeZoneDetector.createManualTimeZoneSuggestion("Asia/Shanghai", "Test debug info");
-        verify(getServices().timeZoneDetector).suggestManualTimeZone(suggestion);
+        verify(getServices().alarmManager).setTimeZone("Asia/Shanghai");
     }
 
     public void testSetTimeZoneFailWithPO() throws Exception {
@@ -4035,9 +4006,7 @@
         setupProfileOwner();
         configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
         dpm.setTimeZone(admin1, "Asia/Shanghai");
-        ManualTimeZoneSuggestion suggestion =
-                TimeZoneDetector.createManualTimeZoneSuggestion("Asia/Shanghai", "Test debug info");
-        verify(getServices().timeZoneDetector).suggestManualTimeZone(suggestion);
+        verify(getServices().alarmManager).setTimeZone("Asia/Shanghai");
     }
 
     public void testSetTimeZoneWithAutoTimeZoneOn() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 12228c1..8625a1e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -207,8 +207,6 @@
         switch (name) {
             case Context.ALARM_SERVICE:
                 return mMockSystemServices.alarmManager;
-            case Context.TIME_DETECTOR_SERVICE:
-                return mMockSystemServices.timeDetector;
             case Context.USER_SERVICE:
                 return mMockSystemServices.userManager;
             case Context.POWER_SERVICE:
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 01f1a3e..bbd4472 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -33,8 +33,6 @@
 import android.app.IActivityTaskManager;
 import android.app.NotificationManager;
 import android.app.backup.IBackupManager;
-import android.app.timedetector.TimeDetector;
-import android.app.timezonedetector.TimeZoneDetector;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -118,8 +116,6 @@
     public final TelephonyManager telephonyManager;
     public final AccountManager accountManager;
     public final AlarmManager alarmManager;
-    public final TimeDetector timeDetector;
-    public final TimeZoneDetector timeZoneDetector;
     public final KeyChain.KeyChainConnection keyChainConnection;
     public final CrossProfileApps crossProfileApps;
     public final PersistentDataBlockManagerInternal persistentDataBlockManagerInternal;
@@ -164,8 +160,6 @@
         telephonyManager = mock(TelephonyManager.class);
         accountManager = mock(AccountManager.class);
         alarmManager = mock(AlarmManager.class);
-        timeDetector = mock(TimeDetector.class);
-        timeZoneDetector = mock(TimeZoneDetector.class);
         keyChainConnection = mock(KeyChain.KeyChainConnection.class, RETURNS_DEEP_STUBS);
         crossProfileApps = mock(CrossProfileApps.class);
         persistentDataBlockManagerInternal = mock(PersistentDataBlockManagerInternal.class);
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 049c8e1..1c267a0 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;
@@ -892,7 +893,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();
@@ -1019,7 +1020,7 @@
         public void taskAppeared(ActivityManager.RunningTaskInfo info) {
         }
         @Override
-        public void taskVanished(IWindowContainer wc) {
+        public void taskVanished(ActivityManager.RunningTaskInfo info) {
         }
         @Override
         public void transactionReady(int id, SurfaceControl.Transaction t) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index c7f94ef..34ac835 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -98,7 +98,7 @@
         mDisplayContent.mOpeningApps.add(win.mActivityRecord);
         try {
             final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
-                    new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
+                    new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
             mController.goodToGo();
@@ -136,7 +136,7 @@
     public void testCancel() throws Exception {
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
         final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
-                new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
+                new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                 mFinishedCallback);
         mController.goodToGo();
@@ -150,7 +150,7 @@
     public void testTimeout() throws Exception {
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
         final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
-                new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
+                new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                 mFinishedCallback);
         mController.goodToGo();
@@ -170,7 +170,8 @@
             final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
                     "testWin");
             final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
-                    win.mActivityRecord, new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
+                    win.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150),
+                    null).mAdapter;
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
             mController.goodToGo();
@@ -201,7 +202,7 @@
     public void testNotReallyStarted() {
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
         mController.createRemoteAnimationRecord(win.mActivityRecord,
-                new Point(50, 100), new Rect(50, 100, 150, 150), null);
+                new Point(50, 100), null, new Rect(50, 100, 150, 150), null);
         mController.goodToGo();
         verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
     }
@@ -211,9 +212,9 @@
         final WindowState win1 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin1");
         final WindowState win2 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin2");
         mController.createRemoteAnimationRecord(win1.mActivityRecord,
-                new Point(50, 100), new Rect(50, 100, 150, 150), null);
+                new Point(50, 100), null, new Rect(50, 100, 150, 150), null);
         final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win2.mActivityRecord,
-                new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
+                new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                 mFinishedCallback);
         mController.goodToGo();
@@ -234,7 +235,7 @@
     public void testRemovedBeforeStarted() {
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
         final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
-                new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
+                new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                 mFinishedCallback);
         win.mActivityRecord.removeImmediately();
@@ -250,7 +251,7 @@
         mDisplayContent.mChangingContainers.add(win.mActivityRecord);
         try {
             final RemoteAnimationRecord record = mController.createRemoteAnimationRecord(
-                    win.mActivityRecord, new Point(50, 100), new Rect(50, 100, 150, 150),
+                    win.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150),
                     new Rect(0, 0, 200, 200));
             assertNotNull(record.mThumbnailAdapter);
             ((AnimationAdapter) record.mAdapter)
@@ -304,7 +305,7 @@
         mDisplayContent.mOpeningApps.add(win.mActivityRecord);
         try {
             final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
-                    new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
+                    new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
             mController.goodToGo();
@@ -333,7 +334,7 @@
         mDisplayContent.mOpeningApps.add(win.mActivityRecord);
         try {
             final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
-                    new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
+                    new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
             mController.goodToGo();
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 48a583c..53a3682 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -129,7 +129,7 @@
         final Task task = createTaskInStack(stack, 0 /* userId */);
         final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
         final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED);
- 
+
         stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         verify(organizer).taskAppeared(any());
         stack.setWindowingMode(WINDOWING_MODE_PINNED);
@@ -345,7 +345,7 @@
             public void taskAppeared(RunningTaskInfo taskInfo) { }
 
             @Override
-            public void taskVanished(IWindowContainer container) { }
+            public void taskVanished(RunningTaskInfo container) { }
 
             @Override
             public void transactionReady(int id, SurfaceControl.Transaction t) { }
@@ -399,7 +399,7 @@
             public void taskAppeared(RunningTaskInfo taskInfo) { }
 
             @Override
-            public void taskVanished(IWindowContainer container) { }
+            public void taskVanished(RunningTaskInfo container) { }
 
             @Override
             public void transactionReady(int id, SurfaceControl.Transaction t) { }
@@ -539,7 +539,7 @@
             mInfo = info;
         }
         @Override
-        public void taskVanished(IWindowContainer wc) {
+        public void taskVanished(RunningTaskInfo info) {
         }
         @Override
         public void transactionReady(int id, SurfaceControl.Transaction t) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 35723ab..da4bde5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -16,9 +16,16 @@
 
 package com.android.server.wm;
 
+import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
 import android.content.pm.PackageManager;
+import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
@@ -57,4 +64,25 @@
         return getInstrumentation().getTargetContext().getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_AUTOMOTIVE);
     }
+
+    @Test
+    public void testAddWindowToken() {
+        IBinder token = mock(IBinder.class);
+        mWm.addWindowToken(token, TYPE_TOAST, mDisplayContent.getDisplayId());
+
+        WindowToken windowToken = mWm.mRoot.getWindowToken(token);
+        assertFalse(windowToken.mRoundedCornerOverlay);
+        assertFalse(windowToken.mFromClientToken);
+    }
+
+    @Test
+    public void testAddWindowTokenWithOptions() {
+        IBinder token = mock(IBinder.class);
+        mWm.addWindowTokenWithOptions(token, TYPE_TOAST, mDisplayContent.getDisplayId(),
+                null /* options */, null /* options */);
+
+        WindowToken windowToken = mWm.mRoot.getWindowToken(token);
+        assertFalse(windowToken.mRoundedCornerOverlay);
+        assertTrue(windowToken.mFromClientToken);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index e6aed49..38f643d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -25,7 +25,9 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
 
+import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
@@ -129,4 +131,28 @@
         // Verify that the token windows are no longer attached to it.
         assertEquals(0, token.getWindowsCount());
     }
+
+    /**
+     * Test that {@link WindowToken} constructor parameters is set with expectation.
+     */
+    @Test
+    public void testWindowTokenConstructorSanity() {
+        WindowToken token = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class),
+                TYPE_TOAST, true /* persistOnEmpty */, mDisplayContent,
+                true /* ownerCanManageAppTokens */);
+        assertFalse(token.mRoundedCornerOverlay);
+        assertFalse(token.mFromClientToken);
+
+        token = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), TYPE_TOAST,
+                true /* persistOnEmpty */, mDisplayContent, true /* ownerCanManageAppTokens */,
+                true /* roundedCornerOverlay */);
+        assertTrue(token.mRoundedCornerOverlay);
+        assertFalse(token.mFromClientToken);
+
+        token = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), TYPE_TOAST,
+                true /* persistOnEmpty */, mDisplayContent, true /* ownerCanManageAppTokens */,
+                true /* roundedCornerOverlay */, true /* fromClientToken */);
+        assertTrue(token.mRoundedCornerOverlay);
+        assertTrue(token.mFromClientToken);
+    }
 }
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 c5c08c2..c20748b 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1568,6 +1568,7 @@
     /**
      * The string is used to compare with operator name.
      * If it matches the pattern then show specific data icon.
+     * @hide
      */
     public static final String KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING =
             "show_carrier_data_icon_pattern_string";
@@ -2978,9 +2979,9 @@
      * UE wants to display 5G_Plus icon for scenario#1, and 5G icon for scenario#2; otherwise not
      * define.
      * The configuration is: "connected_mmwave:5G_Plus,connected:5G"
+     * @hide
      */
-    public static final String KEY_5G_ICON_CONFIGURATION_STRING =
-            "5g_icon_configuration_string";
+    public static final String KEY_5G_ICON_CONFIGURATION_STRING = "5g_icon_configuration_string";
 
     /**
      * Timeout in seconds for displaying 5G icon, default value is 0 which means the timer is
@@ -2992,12 +2993,14 @@
      *
      * If 5G is reacquired during this timer, the timer is canceled and restarted when 5G is next
      * lost. Allows us to momentarily lose 5G without blinking the icon.
+     * @hide
      */
     public static final String KEY_5G_ICON_DISPLAY_GRACE_PERIOD_SEC_INT =
             "5g_icon_display_grace_period_sec_int";
 
     /**
      * Controls time in milliseconds until DcTracker reevaluates 5G connection state.
+     * @hide
      */
     public static final String KEY_5G_WATCHDOG_TIME_MS_LONG = "5g_watchdog_time_long";
 
@@ -3527,6 +3530,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.
      */
@@ -4021,7 +4033,8 @@
         sDefaults.putBoolean(KEY_USE_CALLER_ID_USSD_BOOL, false);
         sDefaults.putInt(KEY_CALL_WAITING_SERVICE_CLASS_INT, 1 /* SERVICE_CLASS_VOICE */);
         sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING,
-                "connected_mmwave:5G,connected:5G");
+                "connected_mmwave:5G,connected:5G,not_restricted_rrc_idle:5G,"
+                        + "not_restricted_rrc_con:5G");
         sDefaults.putInt(KEY_5G_ICON_DISPLAY_GRACE_PERIOD_SEC_INT, 0);
         /* Default value is 1 hour. */
         sDefaults.putLong(KEY_5G_WATCHDOG_TIME_MS_LONG, 3600000);
@@ -4082,6 +4095,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.
diff --git a/telephony/java/android/telephony/CdmaEriInformation.java b/telephony/java/android/telephony/CdmaEriInformation.java
index 1cd9d30..fd0b905 100644
--- a/telephony/java/android/telephony/CdmaEriInformation.java
+++ b/telephony/java/android/telephony/CdmaEriInformation.java
@@ -40,7 +40,6 @@
  *
  * @hide
  */
-@SystemApi
 public final class CdmaEriInformation implements Parcelable {
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
diff --git a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
index 270eafe..c667165 100644
--- a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
+++ b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
@@ -208,7 +208,6 @@
      * @return {@code true} if using carrier aggregation.
      * @hide
      */
-    @SystemApi
     public boolean isUsingCarrierAggregation() {
         return mIsUsingCarrierAggregation;
     }
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java
index fe75266..34bac5d 100644
--- a/telephony/java/android/telephony/ImsManager.java
+++ b/telephony/java/android/telephony/ImsManager.java
@@ -34,8 +34,9 @@
     private Context mContext;
 
     /**
-     * <p>Broadcast Action: Indicates that an IMS operation was rejected by the network due to it
-     * not being authorized on the network.
+     * <p>Broadcast Action: Indicates that a previously allowed IMS operation was rejected by the
+     * network due to the network returning a "forbidden" response. This may be due to a
+     * provisioning change from the network.
      * May include the {@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX} extra to also specify
      * which subscription the operation was rejected for.
      * <p class="note">
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index c74e17f..1a79bf7 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -367,6 +367,7 @@
      * Get the 5G NR connection state.
      *
      * @return the 5G NR connection state.
+     * @hide
      */
     public @NRState int getNrState() {
         return mNrState;
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 9b1baef..45cba51 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -689,8 +689,9 @@
      * @return true if registration indicates roaming, false otherwise
      * @hide
      */
-    @SystemApi
     public boolean getDataRoamingFromRegistration() {
+        // TODO: all callers should refactor to get roaming state directly from modem
+        // this should not be exposed as a public API
         return mIsDataRoamingFromRegistration;
     }
 
@@ -1422,7 +1423,6 @@
      * @return the frequency range of 5G NR.
      * @hide
      */
-    @SystemApi
     public @FrequencyRange int getNrFrequencyRange() {
         return mNrFrequencyRange;
     }
@@ -2026,6 +2026,7 @@
      * The long format can be up to 16 characters long.
      *
      * @return long raw name of operator, null if unregistered or unknown
+     * @hide
      */
     @Nullable
     public String getOperatorAlphaLongRaw() {
@@ -2045,6 +2046,7 @@
      * The short format can be up to 8 characters long.
      *
      * @return short raw name of operator, null if unregistered or unknown
+     * @hide
      */
     @Nullable
     public String getOperatorAlphaShortRaw() {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 40def40..631eaac 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -54,6 +54,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
@@ -5605,7 +5606,6 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    @SystemApi
     @NonNull
     public CdmaEriInformation getCdmaEriInformation() {
         return new CdmaEriInformation(
@@ -8725,7 +8725,6 @@
      *
      * @hide
      */
-    @SystemApi
     @Nullable
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public PinResult supplyPinReportPinResult(@NonNull String pin) {
@@ -8750,7 +8749,6 @@
      *
      * @hide
      */
-    @SystemApi
     @Nullable
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public PinResult supplyPukReportPinResult(@NonNull String puk, @NonNull String pin) {
@@ -11648,11 +11646,9 @@
     }
 
     /**
-     * Override the file path for testing OTA emergency number database in a file partition.
+     * Override the file path for OTA emergency number database in a file partition.
      *
-     * @param otaFilePath The test OTA emergency number database file path;
-     *                    if "RESET", recover the original database file partition.
-     *                    Format: <root file folder>@<file path>
+     * @param otaParcelFileDescriptor parcelable file descriptor for OTA emergency number database.
      *
      * <p> Requires permission:
      * {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION}
@@ -11662,16 +11658,42 @@
     @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
     @SystemApi
     @TestApi
-    public void updateTestOtaEmergencyNumberDbFilePath(@NonNull String otaFilePath) {
+    public void updateOtaEmergencyNumberDbFilePath(
+            @NonNull ParcelFileDescriptor otaParcelFileDescriptor) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                telephony.updateTestOtaEmergencyNumberDbFilePath(otaFilePath);
+                telephony.updateOtaEmergencyNumberDbFilePath(otaParcelFileDescriptor);
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
         } catch (RemoteException ex) {
-            Log.e(TAG, "notifyOtaEmergencyNumberDatabaseInstalled RemoteException", ex);
+            Log.e(TAG, "updateOtaEmergencyNumberDbFilePath RemoteException", ex);
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Reset the file path to default for OTA emergency number database in a file partition.
+     *
+     * <p> Requires permission:
+     * {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION}
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+    @SystemApi
+    @TestApi
+    public void resetOtaEmergencyNumberDbFilePath() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.resetOtaEmergencyNumberDbFilePath();
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "resetOtaEmergencyNumberDbFilePath RemoteException", ex);
             ex.rethrowAsRuntimeException();
         }
     }
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/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 9c1be48..1597cd5 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -18,7 +18,6 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -718,11 +717,16 @@
      * @return A {@link Bundle} containing proprietary call extras that were not set by the
      * platform.
      */
-    public @Nullable Bundle getProprietaryCallExtras() {
+    public @NonNull Bundle getProprietaryCallExtras() {
         if (mCallExtras == null) {
-            return null;
+            return new Bundle();
         }
-        return mCallExtras.getBundle(EXTRA_OEM_EXTRAS);
+        Bundle proprietaryExtras = mCallExtras.getBundle(EXTRA_OEM_EXTRAS);
+        if (proprietaryExtras == null) {
+            return new Bundle();
+        }
+        // Make a copy so users do not accidentally change this copy of the extras.
+        return new Bundle(proprietaryExtras);
     }
 
     public ImsStreamMediaProfile getMediaProfile() {
diff --git a/telephony/java/android/telephony/ims/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
index 025721c..81af99f 100644
--- a/telephony/java/android/telephony/ims/ImsCallSessionListener.java
+++ b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
@@ -58,7 +58,7 @@
         try {
             mListener.callSessionProgressing(profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -71,7 +71,7 @@
         try {
             mListener.callSessionInitiated(profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -85,7 +85,7 @@
         try {
             mListener.callSessionInitiatedFailed(reasonInfo);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -98,7 +98,7 @@
         try {
             mListener.callSessionTerminated(reasonInfo);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -115,7 +115,7 @@
         try {
             mListener.callSessionHeld(profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -128,7 +128,7 @@
         try {
             mListener.callSessionHoldFailed(reasonInfo);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -141,7 +141,7 @@
         try {
             mListener.callSessionHoldReceived(profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -155,7 +155,7 @@
         try {
             mListener.callSessionResumed(profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -169,7 +169,7 @@
         try {
             mListener.callSessionResumeFailed(reasonInfo);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -182,7 +182,7 @@
         try {
             mListener.callSessionResumeReceived(profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -201,7 +201,7 @@
             mListener.callSessionMergeStarted(newSession != null ?
                             newSession.getServiceImpl() : null, profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -216,7 +216,7 @@
         try {
             mListener.callSessionMergeStarted(newSession, profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -232,7 +232,7 @@
             mListener.callSessionMergeComplete(newSession != null ?
                     newSession.getServiceImpl() : null);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -247,7 +247,7 @@
         try {
             mListener.callSessionMergeComplete(newSession);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -260,7 +260,7 @@
         try {
             mListener.callSessionMergeFailed(reasonInfo);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -273,7 +273,7 @@
         try {
             mListener.callSessionUpdated(profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -286,7 +286,7 @@
         try {
             mListener.callSessionUpdateFailed(reasonInfo);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -299,7 +299,7 @@
         try {
             mListener.callSessionUpdateReceived(profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -319,7 +319,7 @@
             mListener.callSessionConferenceExtended(
                     newSession != null ? newSession.getServiceImpl() : null, profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -333,7 +333,7 @@
         try {
             mListener.callSessionConferenceExtended(newSession, profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -347,7 +347,7 @@
         try {
             mListener.callSessionConferenceExtendFailed(reasonInfo);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -364,7 +364,7 @@
             mListener.callSessionConferenceExtendReceived(newSession != null
                     ? newSession.getServiceImpl() : null, profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -379,7 +379,7 @@
         try {
             mListener.callSessionConferenceExtendReceived(newSession, profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -391,7 +391,7 @@
         try {
             mListener.callSessionInviteParticipantsRequestDelivered();
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -407,7 +407,7 @@
         try {
             mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -419,7 +419,7 @@
         try {
             mListener.callSessionRemoveParticipantsRequestDelivered();
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -435,7 +435,7 @@
         try {
             mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -448,7 +448,7 @@
         try {
             mListener.callSessionConferenceStateUpdated(state);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -465,7 +465,7 @@
         try {
             mListener.callSessionUssdMessageReceived(mode, ussdMessage);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -501,7 +501,7 @@
         try {
             mListener.callSessionMayHandover(srcNetworkType, targetNetworkType);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -537,7 +537,7 @@
         try {
             mListener.callSessionHandover(srcNetworkType, targetNetworkType, reasonInfo);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -570,7 +570,7 @@
         try {
             mListener.callSessionHandoverFailed(srcNetworkType, targetNetworkType, reasonInfo);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -587,7 +587,7 @@
         try {
             mListener.callSessionTtyModeReceived(mode);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -600,7 +600,7 @@
         try {
             mListener.callSessionMultipartyStateChanged(isMultiParty);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -614,7 +614,7 @@
         try {
             mListener.callSessionSuppServiceReceived(suppSrvNotification);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -628,7 +628,7 @@
         try {
             mListener.callSessionRttModifyRequestReceived(callProfile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -641,7 +641,7 @@
         try {
             mListener.callSessionRttModifyResponseReceived(status);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -654,7 +654,7 @@
         try {
             mListener.callSessionRttMessageReceived(rttMessage);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -667,7 +667,7 @@
         try {
             mListener.callSessionRttAudioIndicatorChanged(profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -680,7 +680,7 @@
         try {
             mListener.callQualityChanged(callQuality);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 }
diff --git a/telephony/java/android/telephony/ims/ImsUtListener.java b/telephony/java/android/telephony/ims/ImsUtListener.java
index bc124044..460a032 100644
--- a/telephony/java/android/telephony/ims/ImsUtListener.java
+++ b/telephony/java/android/telephony/ims/ImsUtListener.java
@@ -49,7 +49,8 @@
      * {@link ImsSsInfo#CLIR_STATUS_TEMPORARILY_RESTRICTED}, and
      * {@link ImsSsInfo#CLIR_STATUS_TEMPORARILY_ALLOWED}.
      * @deprecated Use {@link #onLineIdentificationSupplementaryServiceResponse(int, ImsSsInfo)}
-     * instead.
+     * instead, this key has been added for backwards compatibility with older proprietary
+     * implementations only and is being phased out.
      */
     @Deprecated
     public static final String BUNDLE_KEY_CLIR = "queryClir";
@@ -60,7 +61,8 @@
      * response. The value will be an instance of {@link ImsSsInfo}, which contains the response to
      * the query.
      * @deprecated Use {@link #onLineIdentificationSupplementaryServiceResponse(int, ImsSsInfo)}
-     * instead.
+     * instead, this key has been added for backwards compatibility with older proprietary
+     * implementations only and is being phased out.
      */
     @Deprecated
     public static final String BUNDLE_KEY_SSINFO = "imsSsInfo";
@@ -123,7 +125,7 @@
         try {
             mServiceInterface.lineIdentificationSupplementaryServiceResponse(id, configuration);
         } catch (RemoteException e) {
-            Log.w(LOG_TAG, "onLineIdentificationSupplementaryServicesResponse: remote exception");
+            e.rethrowFromSystemServer();
         }
     }
 
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 0370846..00fa942 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -305,13 +305,13 @@
 
     /**
      * An integer key associated with the carrier configured expiration time in seconds for
-     * RCS presence published offline availability in RCS presence.
+     * published offline availability in RCS presence provided, which is provided to the network.
      * <p>
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
      */
-    public static final int KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC = 16;
+    public static final int KEY_RCS_PUBLISH_OFFLINE_AVAILABILITY_TIMER_SEC = 16;
 
     /**
      * An integer key associated with whether or not capability discovery is provisioned for this
@@ -326,8 +326,10 @@
     public static final int KEY_RCS_CAPABILITY_DISCOVERY_ENABLED = 17;
 
     /**
-     * An integer key associated with the period of time the capability information of each contact
-     * is cached on the device.
+     * An integer key associated with the period of time in seconds the capability information of
+     * each contact is cached on the device.
+     * <p>
+     * Seconds are used because this is usually measured in the span of days.
      * <p>
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
@@ -337,7 +339,8 @@
 
     /**
      * An integer key associated with the period of time in seconds that the availability
-     * information of a contact is cached on the device.
+     * information of a contact is cached on the device, which is based on the carrier provisioning
+     * configuration from the network.
      * <p>
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
@@ -347,7 +350,8 @@
 
     /**
      * An integer key associated with the carrier configured interval in seconds expected between
-     * successive capability polling attempts.
+     * successive capability polling attempts, which is based on the carrier provisioning
+     * configuration from the network.
      * <p>
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
@@ -357,7 +361,7 @@
 
     /**
      * An integer key representing the minimum time allowed between two consecutive presence publish
-     * messages from the device.
+     * messages from the device in milliseconds.
      * <p>
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
@@ -378,7 +382,7 @@
     /**
      * An integer associated with the expiration timer used during the SIP subscription of a
      * Request Contained List (RCL), which is used to retrieve the RCS capabilities of the contact
-     * book.
+     * book. This timer value is sent in seconds to the network.
      * <p>
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
@@ -470,7 +474,8 @@
     public static final int KEY_SIP_KEEP_ALIVE_ENABLED = 32;
 
     /**
-     * Registration retry Base Time value in seconds.
+     * Registration retry Base Time value in seconds, which is based off of the carrier
+     * configuration.
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
@@ -478,7 +483,8 @@
     public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33;
 
     /**
-     * Registration retry Max Time value in seconds.
+     * Registration retry Max Time value in seconds, which is based off of the carrier
+     * configuration.
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 58e9b70..30306c7 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -24,12 +24,10 @@
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.content.Context;
-import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.provider.Telephony;
 import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.ims.aidl.IImsRcsController;
 import android.telephony.ims.aidl.IRcsUceControllerCallback;
@@ -138,7 +136,7 @@
      * UCE.
      * @hide
      */
-    public static final int PUBLISH_STATE_200_OK = 1;
+    public static final int PUBLISH_STATE_OK = 1;
 
     /**
      * The hasn't published its capabilities since boot or hasn't gotten any publish response yet.
@@ -178,7 +176,7 @@
     /**@hide*/
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = "PUBLISH_STATE_", value = {
-            PUBLISH_STATE_200_OK,
+            PUBLISH_STATE_OK,
             PUBLISH_STATE_NOT_PUBLISHED,
             PUBLISH_STATE_VOLTE_PROVISION_ERROR,
             PUBLISH_STATE_RCS_PROVISION_ERROR,
@@ -305,7 +303,7 @@
      * Gets the last publish result from the UCE service if the device is using an RCS presence
      * server.
      * @return The last publish result from the UCE service. If the device is using SIP OPTIONS,
-     * this method will return {@link #PUBLISH_STATE_200_OK} as well.
+     * this method will return {@link #PUBLISH_STATE_OK} as well.
      * @throws ImsException if the subscription associated with this instance of
      * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
      * available. This can happen if the ImsService has crashed, for example, or if the subscription
diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java
index 96f77d8..d0cec52d 100644
--- a/telephony/java/com/android/ims/ImsConfig.java
+++ b/telephony/java/com/android/ims/ImsConfig.java
@@ -270,11 +270,12 @@
         /**
          * Requested expiration for Published Offline availability.
          * Value is in Integer format.
-         * @deprecated use {@link ProvisioningManager#KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC}.
+         * @deprecated use
+         *     {@link ProvisioningManager#KEY_RCS_PUBLISH_OFFLINE_AVAILABILITY_TIMER_SEC}.
          */
         @Deprecated
         public static final int PUBLISH_TIMER_EXTENDED =
-                ProvisioningManager.KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC;
+                ProvisioningManager.KEY_RCS_PUBLISH_OFFLINE_AVAILABILITY_TIMER_SEC;
 
         /**
          *
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index af5089f..dcf339c 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -22,6 +22,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Messenger;
+import android.os.ParcelFileDescriptor;
 import android.os.ResultReceiver;
 import android.os.WorkSource;
 import android.net.NetworkStats;
@@ -2121,9 +2122,14 @@
     void notifyOtaEmergencyNumberDbInstalled();
 
     /**
-     * Override the file partition name for testing OTA emergency number database.
+     * Override a customized file partition name for OTA emergency number database.
      */
-    void updateTestOtaEmergencyNumberDbFilePath(String otaFilePath);
+    void updateOtaEmergencyNumberDbFilePath(in ParcelFileDescriptor otaParcelFileDescriptor);
+
+    /**
+     * Reset file partition to default for OTA emergency number database.
+     */
+    void resetOtaEmergencyNumberDbFilePath();
 
     /**
      * Enable or disable a logical modem stack associated with the slotIndex.
diff --git a/test-mock/src/android/test/mock/MockContentResolver.java b/test-mock/src/android/test/mock/MockContentResolver.java
index 8283019..8f4bccc 100644
--- a/test-mock/src/android/test/mock/MockContentResolver.java
+++ b/test-mock/src/android/test/mock/MockContentResolver.java
@@ -25,6 +25,7 @@
 import android.database.ContentObserver;
 import android.net.Uri;
 
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -172,7 +173,7 @@
      * from observers elsewhere in the system.
      */
     @Override
-    public void notifyChange(@NonNull Iterable<Uri> uris, @Nullable ContentObserver observer,
+    public void notifyChange(@NonNull Collection<Uri> uris, @Nullable ContentObserver observer,
             @NotifyFlags int flags) {
     }
 }
diff --git a/tests/PackageWatchdog/src/com/android/server/ExplicitHealthCheckServiceTest.java b/tests/PackageWatchdog/src/com/android/server/ExplicitHealthCheckServiceTest.java
new file mode 100644
index 0000000..2fbfeba
--- /dev/null
+++ b/tests/PackageWatchdog/src/com/android/server/ExplicitHealthCheckServiceTest.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 com.android.server;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteCallback;
+import android.service.watchdog.ExplicitHealthCheckService;
+import android.service.watchdog.IExplicitHealthCheckService;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.concurrent.CountDownLatch;
+
+public class ExplicitHealthCheckServiceTest {
+
+    private ExplicitHealthCheckService mExplicitHealthCheckService;
+    private static final String PACKAGE_NAME = "com.test.package";
+
+    @Before
+    public void setup() throws Exception {
+        mExplicitHealthCheckService = spy(ExplicitHealthCheckService.class);
+    }
+
+    /**
+     * Test to verify that the correct information is sent in the callback when a package has
+     * passed an explicit health check.
+     */
+    @Test
+    public void testNotifyHealthCheckPassed() throws Exception {
+        IBinder binder = mExplicitHealthCheckService.onBind(new Intent());
+        CountDownLatch countDownLatch = new CountDownLatch(1);
+        RemoteCallback callback = new RemoteCallback(result -> {
+            assertThat(result.get(ExplicitHealthCheckService.EXTRA_HEALTH_CHECK_PASSED_PACKAGE))
+                    .isEqualTo(PACKAGE_NAME);
+            countDownLatch.countDown();
+        });
+        IExplicitHealthCheckService.Stub.asInterface(binder).setCallback(callback);
+        mExplicitHealthCheckService.notifyHealthCheckPassed(PACKAGE_NAME);
+        countDownLatch.await();
+    }
+}
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 8cc8cf4..819fc02 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -1063,6 +1063,52 @@
         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()
@@ -1219,6 +1265,7 @@
         private Consumer<String> mPassedConsumer;
         private Consumer<List<PackageConfig>> mSupportedConsumer;
         private Runnable mNotifySyncRunnable;
+        private List<Set> mSyncRequests = new ArrayList<>();
 
         @Override
         public void setEnabled(boolean enabled) {
@@ -1238,6 +1285,7 @@
 
         @Override
         public void syncRequests(Set<String> packages) {
+            mSyncRequests.add(packages);
             mRequestedPackages.clear();
             if (mIsEnabled) {
                 packages.retainAll(mSupportedPackages);
@@ -1268,6 +1316,10 @@
                 return Collections.emptyList();
             }
         }
+
+        public List<Set> getSyncRequests() {
+            return mSyncRequests;
+        }
     }
 
     private static class TestClock implements PackageWatchdog.SystemClock {
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index 70be83f..a616c61 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -127,18 +127,12 @@
 
     /**
      * Test rollbacks of staged installs involving only apks with bad update.
-     * Trigger rollback phase. This is expected to fail due to watchdog
-     * rebooting the test out from under it.
+     * Trigger rollback phase.
      */
     @Test
     public void testBadApkOnly_Phase3() throws Exception {
         // One more crash to trigger rollback
         RollbackUtils.sendCrashBroadcast(TestApp.A, 1);
-
-        // We expect the device to be rebooted automatically. Wait for that to happen.
-        // This device method will fail and the host will catch the assertion.
-        // If reboot doesn't happen, the host will fail the assertion.
-        Thread.sleep(TimeUnit.SECONDS.toMillis(120));
     }
 
     /**
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 4afebb5..282f012 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -22,7 +22,6 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
-import static org.testng.Assert.assertThrows;
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
@@ -136,7 +135,10 @@
         getDevice().reboot();
         runPhase("testBadApkOnly_Phase2");
 
-        assertThrows(AssertionError.class, () -> runPhase("testBadApkOnly_Phase3"));
+        // Trigger rollback and wait for reboot to happen
+        runPhase("testBadApkOnly_Phase3");
+        assertTrue(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(2)));
+
         getDevice().waitForDeviceAvailable();
 
         runPhase("testBadApkOnly_Phase4");
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
index 8f7bebb..6eb4587 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
@@ -139,7 +139,7 @@
                 mTaskView2.reparentTask(ti.token);
             }
         }
-        public void taskVanished(IWindowContainer wc) {
+        public void taskVanished(ActivityManager.RunningTaskInfo ti) {
         }
         public void transactionReady(int id, SurfaceControl.Transaction t) {
             mergedTransaction.merge(t);
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
index bd17751..ade5c2e 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
@@ -49,7 +49,7 @@
             } catch (Exception e) {
             }
         }
-        public void taskVanished(IWindowContainer wc) {
+        public void taskVanished(ActivityManager.RunningTaskInfo ti) {
         }
         public void transactionReady(int id, SurfaceControl.Transaction t) {
         }
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/CaptivePortalTest.java b/tests/net/common/java/android/net/CaptivePortalTest.java
index ca4ba63..7a60cc1 100644
--- a/tests/net/common/java/android/net/CaptivePortalTest.java
+++ b/tests/net/common/java/android/net/CaptivePortalTest.java
@@ -18,19 +18,26 @@
 
 import static org.junit.Assert.assertEquals;
 
+import android.os.Build;
 import android.os.RemoteException;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class CaptivePortalTest {
+    @Rule
+    public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
+
     private static final int DEFAULT_TIMEOUT_MS = 5000;
     private static final String TEST_PACKAGE_NAME = "com.google.android.test";
 
@@ -84,6 +91,7 @@
         assertEquals(result.mCode, CaptivePortal.APP_RETURN_WANTED_AS_IS);
     }
 
+    @IgnoreUpTo(Build.VERSION_CODES.Q)
     @Test
     public void testReevaluateNetwork() {
         final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.reevaluateNetwork());
diff --git a/tests/net/common/java/android/net/LinkAddressTest.java b/tests/net/common/java/android/net/LinkAddressTest.java
index 06c6301..99dac14 100644
--- a/tests/net/common/java/android/net/LinkAddressTest.java
+++ b/tests/net/common/java/android/net/LinkAddressTest.java
@@ -28,8 +28,8 @@
 import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
 
 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 static org.junit.Assert.assertEquals;
@@ -38,11 +38,17 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.os.Build;
 import android.os.SystemClock;
 
 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;
 
@@ -57,6 +63,8 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class LinkAddressTest {
+    @Rule
+    public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
 
     private static final String V4 = "192.0.2.1";
     private static final String V6 = "2001:db8::1";
@@ -318,15 +326,29 @@
 
         l = new LinkAddress(V6_ADDRESS, 64, 123, 456);
         assertParcelingIsLossless(l);
-        l = new LinkAddress(V6_ADDRESS, 64, 123, 456,
-                1L, 3600000L);
-        assertParcelingIsLossless(l);
 
         l = new LinkAddress(V4 + "/28", IFA_F_PERMANENT, RT_SCOPE_LINK);
-        assertParcelSane(l, 6);
+        assertParcelingIsLossless(l);
     }
 
-    @Test
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testLifetimeParceling() {
+        final LinkAddress l = new LinkAddress(V6_ADDRESS, 64, 123, 456, 1L, 3600000L);
+        assertParcelingIsLossless(l);
+    }
+
+    @Test @IgnoreAfter(Build.VERSION_CODES.Q)
+    public void testFieldCount_Q() {
+        assertFieldCountEquals(4, LinkAddress.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(6, LinkAddress.class);
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
     public void testDeprecationTime() {
         try {
             new LinkAddress(V6_ADDRESS, 64, 0, 456,
@@ -347,7 +369,7 @@
         } catch (IllegalArgumentException expected) { }
     }
 
-    @Test
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
     public void testExpirationTime() {
         try {
             new LinkAddress(V6_ADDRESS, 64, 0, 456,
@@ -366,10 +388,13 @@
     public void testGetFlags() {
         LinkAddress l = new LinkAddress(V6_ADDRESS, 64, 123, RT_SCOPE_HOST);
         assertEquals(123, l.getFlags());
+    }
 
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testGetFlags_Deprecation() {
         // Test if deprecated bit was added/remove automatically based on the provided deprecation
         // time
-        l = new LinkAddress(V6_ADDRESS, 64, 0, RT_SCOPE_HOST,
+        LinkAddress l = new LinkAddress(V6_ADDRESS, 64, 0, RT_SCOPE_HOST,
                 1L, LinkAddress.LIFETIME_PERMANENT);
         // Check if the flag is added automatically.
         assertTrue((l.getFlags() & IFA_F_DEPRECATED) != 0);
@@ -458,8 +483,11 @@
                             (IFA_F_TEMPORARY|IFA_F_TENTATIVE|IFA_F_OPTIMISTIC),
                             RT_SCOPE_UNIVERSE);
         assertGlobalPreferred(l, "v6,global,tempaddr+optimistic");
+    }
 
-        l = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED,
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testIsGlobalPreferred_DeprecatedInFuture() {
+        final LinkAddress l = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED,
                 RT_SCOPE_UNIVERSE, SystemClock.elapsedRealtime() + 100000,
                 SystemClock.elapsedRealtime() + 200000);
         // Although the deprecated bit is set, but the deprecation time is in the future, test
diff --git a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt b/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
index d250ad3..173dbd1 100644
--- a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
+++ b/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
@@ -16,16 +16,23 @@
 
 package android.net
 
+import android.os.Build
 import androidx.test.filters.SmallTest
 import androidx.test.runner.AndroidJUnit4
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
 import com.android.testutils.assertParcelSane
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @RunWith(AndroidJUnit4::class)
 @SmallTest
 class NetworkAgentConfigTest {
-    @Test
+    @Rule @JvmField
+    val ignoreRule = DevSdkIgnoreRule()
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
     fun testParcelNetworkAgentConfig() {
         val config = NetworkAgentConfig.Builder().apply {
             setExplicitlySelected(true)
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/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index c2c3ba3..8c0c36b 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -2751,9 +2751,6 @@
 
         // Expect NET_CAPABILITY_VALIDATED onAvailable callback.
         validatedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
-        // Expect no notification to be shown when captive portal disappears by itself
-        verify(mNotificationManager, never()).notifyAsUser(
-                anyString(), eq(NotificationType.LOGGED_IN.eventId), any(), any());
 
         // Break network connectivity.
         // Expect NET_CAPABILITY_VALIDATED onLost callback.
@@ -2815,8 +2812,6 @@
         mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
         validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
         captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
-        verify(mNotificationManager, times(1)).notifyAsUser(anyString(),
-                eq(NotificationType.LOGGED_IN.eventId), any(), eq(UserHandle.ALL));
 
         mCm.unregisterNetworkCallback(validatedCallback);
         mCm.unregisterNetworkCallback(captivePortalCallback);
diff --git a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
index d57f225..47db5d4 100644
--- a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
@@ -238,20 +238,6 @@
     }
 
     @Test
-    public void testSameLevelNotifications() {
-        final int id = 101;
-        final String tag = NetworkNotificationManager.tagFor(id);
-
-        mManager.showNotification(id, LOGGED_IN, mWifiNai, mCellNai, null, false);
-        verify(mNotificationManager, times(1))
-                .notifyAsUser(eq(tag), eq(LOGGED_IN.eventId), any(), any());
-
-        mManager.showNotification(id, LOST_INTERNET, mWifiNai, mCellNai, null, false);
-        verify(mNotificationManager, times(1))
-                .notifyAsUser(eq(tag), eq(LOST_INTERNET.eventId), any(), any());
-    }
-
-    @Test
     public void testClearNotificationByType() {
         final int id = 101;
         final String tag = NetworkNotificationManager.tagFor(id);
@@ -259,31 +245,25 @@
         // clearNotification(int id, NotificationType notifyType) will check if given type is equal
         // to previous type or not. If they are equal then clear the notification; if they are not
         // equal then return.
-
-        mManager.showNotification(id, LOGGED_IN, mWifiNai, mCellNai, null, false);
+        mManager.showNotification(id, NO_INTERNET, mWifiNai, mCellNai, null, false);
         verify(mNotificationManager, times(1))
-                .notifyAsUser(eq(tag), eq(LOGGED_IN.eventId), any(), any());
+                .notifyAsUser(eq(tag), eq(NO_INTERNET.eventId), any(), any());
 
-        // Previous notification is LOGGED_IN and given type is LOGGED_IN too. The notification
+        // Previous notification is NO_INTERNET and given type is NO_INTERNET too. The notification
         // should be cleared.
-        mManager.clearNotification(id, LOGGED_IN);
+        mManager.clearNotification(id, NO_INTERNET);
         verify(mNotificationManager, times(1))
-                .cancelAsUser(eq(tag), eq(LOGGED_IN.eventId), any());
+                .cancelAsUser(eq(tag), eq(NO_INTERNET.eventId), any());
 
-        mManager.showNotification(id, LOGGED_IN, mWifiNai, mCellNai, null, false);
-        verify(mNotificationManager, times(2))
-                .notifyAsUser(eq(tag), eq(LOGGED_IN.eventId), any(), any());
-
-        // LOST_INTERNET notification popup after LOGGED_IN notification.
-        mManager.showNotification(id, LOST_INTERNET, mWifiNai, mCellNai, null, false);
+        // SIGN_IN is popped-up.
+        mManager.showNotification(id, SIGN_IN, mWifiNai, mCellNai, null, false);
         verify(mNotificationManager, times(1))
-                .notifyAsUser(eq(tag), eq(LOST_INTERNET.eventId), any(), any());
+                .notifyAsUser(eq(tag), eq(SIGN_IN.eventId), any(), any());
 
-        // Previous notification is LOST_INTERNET and given type is LOGGED_IN. The notification
-        // shouldn't be cleared.
-        mManager.clearNotification(id, LOGGED_IN);
-        // LOST_INTERNET shouldn't be cleared.
+        // The notification type is not matching previous one, PARTIAL_CONNECTIVITY won't be
+        // cleared.
+        mManager.clearNotification(id, PARTIAL_CONNECTIVITY);
         verify(mNotificationManager, never())
-                .cancelAsUser(eq(tag), eq(LOST_INTERNET.eventId), any());
+                .cancelAsUser(eq(tag), eq(PARTIAL_CONNECTIVITY.eventId), any());
     }
 }
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/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/IScoreChangeCallback.aidl b/wifi/java/android/net/wifi/IScoreUpdateObserver.aidl
similarity index 82%
rename from wifi/java/android/net/wifi/IScoreChangeCallback.aidl
rename to wifi/java/android/net/wifi/IScoreUpdateObserver.aidl
index d691f41..775fed7 100644
--- a/wifi/java/android/net/wifi/IScoreChangeCallback.aidl
+++ b/wifi/java/android/net/wifi/IScoreUpdateObserver.aidl
@@ -21,9 +21,9 @@
  *
  * @hide
  */
-oneway interface IScoreChangeCallback
+oneway interface IScoreUpdateObserver
 {
-    void onScoreChange(int sessionId, int score);
+    void notifyScoreUpdate(int sessionId, int score);
 
-    void onTriggerUpdateOfWifiUsabilityStats(int sessionId);
+    void triggerUpdateOfWifiUsabilityStats(int sessionId);
 }
diff --git a/wifi/java/android/net/wifi/IWifiConnectedNetworkScorer.aidl b/wifi/java/android/net/wifi/IWifiConnectedNetworkScorer.aidl
index d9a3b01..f96d037 100644
--- a/wifi/java/android/net/wifi/IWifiConnectedNetworkScorer.aidl
+++ b/wifi/java/android/net/wifi/IWifiConnectedNetworkScorer.aidl
@@ -16,7 +16,7 @@
 
 package android.net.wifi;
 
-import android.net.wifi.IScoreChangeCallback;
+import android.net.wifi.IScoreUpdateObserver;
 
 /**
  * Interface for Wi-Fi connected network scorer.
@@ -25,9 +25,9 @@
  */
 oneway interface IWifiConnectedNetworkScorer
 {
-    void start(int sessionId);
+    void onStart(int sessionId);
 
-    void stop(int sessionId);
+    void onStop(int sessionId);
 
-    void setScoreChangeCallback(IScoreChangeCallback cbImpl);
+    void onSetScoreUpdateObserver(IScoreUpdateObserver observerImpl);
 }
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 9256c57..70542b5 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -16,16 +16,15 @@
 
 package android.net.wifi;
 
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.net.wifi.WifiAnnotations.ChannelWidth;
+import android.net.wifi.WifiAnnotations.WifiStandard;
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -313,17 +312,6 @@
      */
     public static final int WIFI_STANDARD_11AX = 6;
 
-    /** @hide */
-    @IntDef(prefix = { "WIFI_STANDARD_" }, value = {
-            WIFI_STANDARD_UNKNOWN,
-            WIFI_STANDARD_LEGACY,
-            WIFI_STANDARD_11N,
-            WIFI_STANDARD_11AC,
-            WIFI_STANDARD_11AX
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface WifiStandard{}
-
     /**
      * AP wifi standard.
      */
@@ -368,7 +356,7 @@
      * {@link #CHANNEL_WIDTH_80MHZ}, {@link #CHANNEL_WIDTH_160MHZ}
      * or {@link #CHANNEL_WIDTH_80MHZ_PLUS_MHZ}.
      */
-    public int channelWidth;
+    public @ChannelWidth int channelWidth;
 
     /**
      * Not used if the AP bandwidth is 20 MHz
diff --git a/wifi/java/android/net/wifi/WifiAnnotations.java b/wifi/java/android/net/wifi/WifiAnnotations.java
index 05e5b1d..acda7e0 100644
--- a/wifi/java/android/net/wifi/WifiAnnotations.java
+++ b/wifi/java/android/net/wifi/WifiAnnotations.java
@@ -61,6 +61,26 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface Bandwidth {}
 
+    @IntDef(prefix = { "CHANNEL_WIDTH_" }, value = {
+            ScanResult.CHANNEL_WIDTH_20MHZ,
+            ScanResult.CHANNEL_WIDTH_40MHZ,
+            ScanResult.CHANNEL_WIDTH_80MHZ,
+            ScanResult.CHANNEL_WIDTH_160MHZ,
+            ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ChannelWidth{}
+
+    @IntDef(prefix = { "WIFI_STANDARD_" }, value = {
+            ScanResult.WIFI_STANDARD_UNKNOWN,
+            ScanResult.WIFI_STANDARD_LEGACY,
+            ScanResult.WIFI_STANDARD_11N,
+            ScanResult.WIFI_STANDARD_11AC,
+            ScanResult.WIFI_STANDARD_11AX,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface WifiStandard{}
+
     @IntDef(prefix = { "PROTOCOL_" }, value = {
             ScanResult.PROTOCOL_NONE,
             ScanResult.PROTOCOL_WPA,
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 5a7bf4b..e0b433d 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -498,11 +498,15 @@
                 allowedProtocols.set(WifiConfiguration.Protocol.RSN);
                 allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE);
                 allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
+                allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_256);
                 allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
+                allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
                 requirePmf = true;
                 break;
             case SECURITY_TYPE_EAP_SUITE_B:
                 allowedProtocols.set(WifiConfiguration.Protocol.RSN);
+                allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
+                allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
                 allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SUITE_B_192);
                 allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_256);
                 allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
@@ -515,7 +519,9 @@
                 allowedProtocols.set(WifiConfiguration.Protocol.RSN);
                 allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE);
                 allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
+                allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_256);
                 allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
+                allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
                 requirePmf = true;
                 break;
             case SECURITY_TYPE_WAPI_PSK:
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 142854a..70c5e72 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -103,7 +103,7 @@
     /**
      * Wi-Fi standard for the connection
      */
-    private @ScanResult.WifiStandard int mWifiStandard;
+    private @WifiAnnotations.WifiStandard int mWifiStandard;
 
     /**
      * The unit in which links speeds are expressed.
@@ -518,7 +518,7 @@
      * Sets the Wi-Fi standard
      * @hide
      */
-    public void setWifiStandard(@ScanResult.WifiStandard int wifiStandard) {
+    public void setWifiStandard(@WifiAnnotations.WifiStandard int wifiStandard) {
         mWifiStandard = wifiStandard;
     }
 
@@ -526,7 +526,7 @@
      * Get connection Wi-Fi standard
      * @return the connection Wi-Fi standard
      */
-    public @ScanResult.WifiStandard int getWifiStandard() {
+    public @WifiAnnotations.WifiStandard int getWifiStandard() {
         return mWifiStandard;
     }
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 9703fa6..96beacd 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2563,7 +2563,7 @@
      *        valid values from {@link ScanResult}'s {@code WIFI_STANDARD_}
      * @return {@code true} if supported, {@code false} otherwise.
      */
-    public boolean isWifiStandardSupported(@ScanResult.WifiStandard int standard) {
+    public boolean isWifiStandardSupported(@WifiAnnotations.WifiStandard int standard) {
         try {
             return mService.isWifiStandardSupported(standard);
         } catch (RemoteException e) {
@@ -2735,27 +2735,30 @@
     }
 
     /**
-     * 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,
+                    networkSuggestionsToMatch, scanResults,
                     mContext.getOpPackageName(), mContext.getFeatureId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -2826,7 +2829,7 @@
      */
     @Nullable
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public String getCountryCode() {
         try {
             return mService.getCountryCode();
@@ -3372,7 +3375,7 @@
      */
     @NonNull
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE)
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public SoftApConfiguration getSoftApConfiguration() {
         try {
             return mService.getSoftApConfiguration();
@@ -4989,7 +4992,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public void factoryReset() {
         try {
             mService.factoryReset(mContext.getOpPackageName());
@@ -5986,22 +5989,22 @@
     }
 
     /**
-     * Callback interface for framework to receive network status changes and trigger of updating
+     * Callback interface for framework to receive network status updates and trigger of updating
      * {@link WifiUsabilityStatsEntry}.
      *
      * @hide
      */
     @SystemApi
-    public interface ScoreChangeCallback {
+    public interface ScoreUpdateObserver {
         /**
          * Called by applications to indicate network status.
          *
          * @param sessionId The ID to indicate current Wi-Fi network connection obtained from
-         *                  {@link WifiConnectedNetworkScorer#start(int)}.
+         *                  {@link WifiConnectedNetworkScorer#onStart(int)}.
          * @param score The score representing link quality of current Wi-Fi network connection.
          *              Populated by connected network scorer in applications..
          */
-        void onScoreChange(int sessionId, int score);
+        void notifyScoreUpdate(int sessionId, int score);
 
         /**
          * Called by applications to trigger an update of {@link WifiUsabilityStatsEntry}.
@@ -6009,36 +6012,36 @@
          * {@link addOnWifiUsabilityStatsListener(Executor, OnWifiUsabilityStatsListener)}.
          *
          * @param sessionId The ID to indicate current Wi-Fi network connection obtained from
-         *                  {@link WifiConnectedNetworkScorer#start(int)}.
+         *                  {@link WifiConnectedNetworkScorer#onStart(int)}.
          */
-        void onTriggerUpdateOfWifiUsabilityStats(int sessionId);
+        void triggerUpdateOfWifiUsabilityStats(int sessionId);
     }
 
     /**
-     * Callback proxy for {@link ScoreChangeCallback} objects.
+     * Callback proxy for {@link ScoreUpdateObserver} objects.
      *
      * @hide
      */
-    private class ScoreChangeCallbackProxy implements ScoreChangeCallback {
-        private final IScoreChangeCallback mScoreChangeCallback;
+    private class ScoreUpdateObserverProxy implements ScoreUpdateObserver {
+        private final IScoreUpdateObserver mScoreUpdateObserver;
 
-        private ScoreChangeCallbackProxy(IScoreChangeCallback callback) {
-            mScoreChangeCallback = callback;
+        private ScoreUpdateObserverProxy(IScoreUpdateObserver observer) {
+            mScoreUpdateObserver = observer;
         }
 
         @Override
-        public void onScoreChange(int sessionId, int score) {
+        public void notifyScoreUpdate(int sessionId, int score) {
             try {
-                mScoreChangeCallback.onScoreChange(sessionId, score);
+                mScoreUpdateObserver.notifyScoreUpdate(sessionId, score);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
         }
 
         @Override
-        public void onTriggerUpdateOfWifiUsabilityStats(int sessionId) {
+        public void triggerUpdateOfWifiUsabilityStats(int sessionId) {
             try {
-                mScoreChangeCallback.onTriggerUpdateOfWifiUsabilityStats(sessionId);
+                mScoreUpdateObserver.triggerUpdateOfWifiUsabilityStats(sessionId);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -6058,21 +6061,21 @@
          * Called by framework to indicate the start of a network connection.
          * @param sessionId The ID to indicate current Wi-Fi network connection.
          */
-        void start(int sessionId);
+        void onStart(int sessionId);
 
         /**
          * Called by framework to indicate the end of a network connection.
          * @param sessionId The ID to indicate current Wi-Fi network connection obtained from
-         *                  {@link WifiConnectedNetworkScorer#start(int)}.
+         *                  {@link WifiConnectedNetworkScorer#onStart(int)}.
          */
-        void stop(int sessionId);
+        void onStop(int sessionId);
 
         /**
          * Framework sets callback for score change events after application sets its scorer.
-         * @param cbImpl The instance for {@link WifiManager#ScoreChangeCallback}. Should be
+         * @param observerImpl The instance for {@link WifiManager#ScoreUpdateObserver}. Should be
          * implemented and instantiated by framework.
          */
-        void setScoreChangeCallback(@NonNull ScoreChangeCallback cbImpl);
+        void onSetScoreUpdateObserver(@NonNull ScoreUpdateObserver observerImpl);
     }
 
     /**
@@ -6090,32 +6093,32 @@
         }
 
         @Override
-        public void start(int sessionId) {
+        public void onStart(int sessionId) {
             if (mVerboseLoggingEnabled) {
-                Log.v(TAG, "WifiConnectedNetworkScorer: " + "start: sessionId=" + sessionId);
+                Log.v(TAG, "WifiConnectedNetworkScorer: " + "onStart: sessionId=" + sessionId);
             }
             Binder.clearCallingIdentity();
-            mExecutor.execute(() -> mScorer.start(sessionId));
+            mExecutor.execute(() -> mScorer.onStart(sessionId));
         }
 
         @Override
-        public void stop(int sessionId) {
+        public void onStop(int sessionId) {
             if (mVerboseLoggingEnabled) {
-                Log.v(TAG, "WifiConnectedNetworkScorer: " + "stop: sessionId=" + sessionId);
+                Log.v(TAG, "WifiConnectedNetworkScorer: " + "onStop: sessionId=" + sessionId);
             }
             Binder.clearCallingIdentity();
-            mExecutor.execute(() -> mScorer.stop(sessionId));
+            mExecutor.execute(() -> mScorer.onStop(sessionId));
         }
 
         @Override
-        public void setScoreChangeCallback(IScoreChangeCallback cbImpl) {
+        public void onSetScoreUpdateObserver(IScoreUpdateObserver observerImpl) {
             if (mVerboseLoggingEnabled) {
                 Log.v(TAG, "WifiConnectedNetworkScorer: "
-                        + "setScoreChangeCallback: cbImpl=" + cbImpl);
+                        + "onSetScoreUpdateObserver: observerImpl=" + observerImpl);
             }
             Binder.clearCallingIdentity();
-            mExecutor.execute(() -> mScorer.setScoreChangeCallback(
-                    new ScoreChangeCallbackProxy(cbImpl)));
+            mExecutor.execute(() -> mScorer.onSetScoreUpdateObserver(
+                    new ScoreUpdateObserverProxy(observerImpl)));
         }
     }
 
diff --git a/wifi/java/android/net/wifi/nl80211/DeviceWiphyCapabilities.java b/wifi/java/android/net/wifi/nl80211/DeviceWiphyCapabilities.java
index a045aad..bb0cc97 100644
--- a/wifi/java/android/net/wifi/nl80211/DeviceWiphyCapabilities.java
+++ b/wifi/java/android/net/wifi/nl80211/DeviceWiphyCapabilities.java
@@ -19,6 +19,8 @@
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.net.wifi.ScanResult;
+import android.net.wifi.WifiAnnotations.ChannelWidth;
+import android.net.wifi.WifiAnnotations.WifiStandard;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
@@ -28,6 +30,9 @@
 /**
  * DeviceWiphyCapabilities for wificond
  *
+ * Contains the WiFi physical layer attributes and capabilities of the device.
+ * It is used to collect these attributes from the device driver via wificond.
+ *
  * @hide
  */
 @SystemApi
@@ -61,7 +66,7 @@
      *        valid values from {@link ScanResult}'s {@code WIFI_STANDARD_}
      * @return {@code true} if supported, {@code false} otherwise.
      */
-    public boolean isWifiStandardSupported(int standard) {
+    public boolean isWifiStandardSupported(@WifiStandard int standard) {
         switch (standard) {
             case ScanResult.WIFI_STANDARD_LEGACY:
                 return true;
@@ -84,7 +89,7 @@
      *        valid values from {@link ScanResult}'s {@code WIFI_STANDARD_}
      * @param support {@code true} if supported, {@code false} otherwise.
      */
-    public void setWifiStandardSupport(int standard, boolean support) {
+    public void setWifiStandardSupport(@WifiStandard int standard, boolean support) {
         switch (standard) {
             case ScanResult.WIFI_STANDARD_11N:
                 m80211nSupported = support;
@@ -107,7 +112,7 @@
      *
      * @return {@code true} if supported, {@code false} otherwise.
      */
-    public boolean isChannelWidthSupported(int chWidth) {
+    public boolean isChannelWidthSupported(@ChannelWidth int chWidth) {
         switch (chWidth) {
             case ScanResult.CHANNEL_WIDTH_20MHZ:
                 return true;
@@ -131,8 +136,10 @@
      * @param chWidth valid values are {@link ScanResult#CHANNEL_WIDTH_160MHZ} and
      *        {@link ScanResult#CHANNEL_WIDTH_80MHZ_PLUS_MHZ}
      * @param support {@code true} if supported, {@code false} otherwise.
+     *
+     * @hide
      */
-    public void setChannelWidthSupported(int chWidth, boolean support) {
+    public void setChannelWidthSupported(@ChannelWidth int chWidth, boolean support) {
         switch (chWidth) {
             case ScanResult.CHANNEL_WIDTH_160MHZ:
                 mChannelWidth160MhzSupported = support;
@@ -159,6 +166,8 @@
      * Set maximum number of transmit spatial streams
      *
      * @param streams number of spatial streams
+     *
+     * @hide
      */
     public void setMaxNumberTxSpatialStreams(int streams) {
         mMaxNumberTxSpatialStreams = streams;
@@ -177,6 +186,8 @@
      * Set maximum number of receive spatial streams
      *
      * @param streams number of streams
+     *
+     * @hide
      */
     public void setMaxNumberRxSpatialStreams(int streams) {
         mMaxNumberRxSpatialStreams = streams;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 9c2cad9..a310ff6 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -1805,9 +1805,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.CONNECTIVITY_INTERNAL,
-            android.Manifest.permission.CONFIGURE_WIFI_DISPLAY})
+    @RequiresPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
     public void setMiracastMode(@MiracastMode int mode) {
         try {
             mService.setMiracastMode(mode);
diff --git a/wifi/tests/src/android/net/wifi/ScanResultTest.java b/wifi/tests/src/android/net/wifi/ScanResultTest.java
index b5c74d1..4c22d5d 100644
--- a/wifi/tests/src/android/net/wifi/ScanResultTest.java
+++ b/wifi/tests/src/android/net/wifi/ScanResultTest.java
@@ -42,7 +42,7 @@
     public static final int TEST_LEVEL = -56;
     public static final int TEST_FREQUENCY = 2412;
     public static final long TEST_TSF = 04660l;
-    public static final @ScanResult.WifiStandard int TEST_WIFI_STANDARD =
+    public static final @WifiAnnotations.WifiStandard int TEST_WIFI_STANDARD =
             ScanResult.WIFI_STANDARD_11AC;
 
     /**
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 047a64b..e210e4f 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -423,7 +423,9 @@
 
         assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SAE));
         assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.CCMP));
+        assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.GCMP_256));
         assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.CCMP));
+        assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.GCMP_256));
         assertTrue(config.requirePmf);
     }
 
@@ -440,7 +442,9 @@
 
         assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE));
         assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.CCMP));
+        assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.GCMP_256));
         assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.CCMP));
+        assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.GCMP_256));
         assertTrue(config.requirePmf);
     }
 
@@ -456,6 +460,8 @@
         config.setSecurityParams(SECURITY_TYPE_EAP_SUITE_B);
 
         assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SUITE_B_192));
+        assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP));
+        assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X));
         assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.GCMP_256));
         assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.GCMP_256));
         assertTrue(config.allowedGroupManagementCiphers
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 76ac837..90d6241 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -2349,23 +2349,24 @@
     }
 
     /**
-     * Verify that Wi-Fi connected scorer receives score change callback after registeration.
+     * Verify that Wi-Fi connected scorer receives score update observer after registeration.
      */
     @Test
-    public void verifyScorerReceiveScoreChangeCallbackAfterRegistration() throws Exception {
+    public void verifyScorerReceiveScoreUpdateObserverAfterRegistration() throws Exception {
         mExecutor = new SynchronousExecutor();
         mWifiManager.setWifiConnectedNetworkScorer(mExecutor, mWifiConnectedNetworkScorer);
         ArgumentCaptor<IWifiConnectedNetworkScorer.Stub> scorerCaptor =
                 ArgumentCaptor.forClass(IWifiConnectedNetworkScorer.Stub.class);
         verify(mWifiService).setWifiConnectedNetworkScorer(any(IBinder.class),
                 scorerCaptor.capture());
-        scorerCaptor.getValue().setScoreChangeCallback(any());
+        scorerCaptor.getValue().onSetScoreUpdateObserver(any());
         mLooper.dispatchAll();
-        verify(mWifiConnectedNetworkScorer).setScoreChangeCallback(any());
+        verify(mWifiConnectedNetworkScorer).onSetScoreUpdateObserver(any());
     }
 
     /**
-     * Verify that Wi-Fi connected scorer receives session ID when start/stop methods are called.
+     * Verify that Wi-Fi connected scorer receives session ID when onStart/onStop methods
+     * are called.
      */
     @Test
     public void verifyScorerReceiveSessionIdWhenStartStopIsCalled() throws Exception {
@@ -2375,11 +2376,11 @@
                 ArgumentCaptor.forClass(IWifiConnectedNetworkScorer.Stub.class);
         verify(mWifiService).setWifiConnectedNetworkScorer(any(IBinder.class),
                 callbackCaptor.capture());
-        callbackCaptor.getValue().start(0);
-        callbackCaptor.getValue().stop(10);
+        callbackCaptor.getValue().onStart(0);
+        callbackCaptor.getValue().onStop(10);
         mLooper.dispatchAll();
-        verify(mWifiConnectedNetworkScorer).start(0);
-        verify(mWifiConnectedNetworkScorer).stop(10);
+        verify(mWifiConnectedNetworkScorer).onStart(0);
+        verify(mWifiConnectedNetworkScorer).onStop(10);
     }
 
     @Test