Merge "AudioService - remove sendBroadcastToAll() from setMasterMuteInternalNoCallerCheck()"
diff --git a/Android.bp b/Android.bp
index 135a9ee..5070b5e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -467,6 +467,14 @@
     srcs: [":framework-all-sources"],
     installable: false,
     libs: ["app-compat-annotations"],
+}
+
+java_library {
+    name: "framework-annotation-proc",
+    defaults: ["framework-aidl-export-defaults"],
+    srcs: [":framework-all-sources"],
+    libs: ["app-compat-annotations"],
+    installable: false,
     plugins: [
         "unsupportedappusage-annotation-processor",
         "compat-changeid-annotation-processor",
@@ -475,7 +483,7 @@
 
 platform_compat_config {
     name: "framework-platform-compat-config",
-    src: ":framework-all",
+    src: ":framework-annotation-proc",
 }
 
 // A library including just UnsupportedAppUsage.java classes.
@@ -1600,7 +1608,7 @@
 
 genrule {
     name: "framework-annotation-proc-index",
-    srcs: [":framework-all"],
+    srcs: [":framework-annotation-proc"],
     cmd: "unzip -qp $(in) unsupportedappusage/unsupportedappusage_index.csv > $(out)",
     out: ["unsupportedappusage_index.csv"],
     dist: {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 593e494..0a4e020 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -57,6 +57,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -2688,13 +2689,14 @@
         }
 
         @Override
-        protected int handleShellCommand(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
-                @NonNull FileDescriptor err, @NonNull String[] args) {
+        protected int handleShellCommand(@NonNull ParcelFileDescriptor in,
+                @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
+                @NonNull String[] args) {
             return (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
-                    this, in, out, err, args);
+                    this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(),
+                    args);
         }
 
-
         /**
          * <b>For internal system user only!</b>
          * Returns a list of all currently-executing jobs.
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 5cb9834..a0615aa 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -1802,7 +1802,7 @@
         e.writeString(Build.BRAND);
         e.writeString(Build.PRODUCT);
         e.writeString(Build.DEVICE);
-        e.writeString(Build.VERSION.RELEASE_OR_CODENAME);
+        e.writeString(Build.VERSION.RELEASE);
         e.writeString(Build.ID);
         e.writeString(Build.VERSION.INCREMENTAL);
         e.writeString(Build.TYPE);
diff --git a/api/current.txt b/api/current.txt
index 3785bc9..5d2a1bd 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9650,8 +9650,9 @@
     method public static boolean isSyncPending(android.accounts.Account, String);
     method @NonNull public android.graphics.Bitmap loadThumbnail(@NonNull android.net.Uri, @NonNull android.util.Size, @Nullable android.os.CancellationSignal) throws java.io.IOException;
     method public void notifyChange(@NonNull android.net.Uri, @Nullable android.database.ContentObserver);
-    method public void notifyChange(@NonNull android.net.Uri, @Nullable android.database.ContentObserver, boolean);
+    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 @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;
@@ -24153,6 +24154,9 @@
     field public static final String TAG_MODEL = "Model";
     field public static final String TAG_NEW_SUBFILE_TYPE = "NewSubfileType";
     field public static final String TAG_OECF = "OECF";
+    field public static final String TAG_OFFSET_TIME = "OffsetTime";
+    field public static final String TAG_OFFSET_TIME_DIGITIZED = "OffsetTimeDigitized";
+    field public static final String TAG_OFFSET_TIME_ORIGINAL = "OffsetTimeOriginal";
     field public static final String TAG_ORF_ASPECT_FRAME = "AspectFrame";
     field public static final String TAG_ORF_PREVIEW_IMAGE_LENGTH = "PreviewImageLength";
     field public static final String TAG_ORF_PREVIEW_IMAGE_START = "PreviewImageStart";
@@ -34389,7 +34393,6 @@
     field public static final String INCREMENTAL;
     field public static final int PREVIEW_SDK_INT;
     field public static final String RELEASE;
-    field @NonNull public static final String RELEASE_OR_CODENAME;
     field @Deprecated public static final String SDK;
     field public static final int SDK_INT;
     field public static final String SECURITY_PATCH;
@@ -35356,6 +35359,8 @@
   public class RemoteException extends android.util.AndroidException {
     ctor public RemoteException();
     ctor public RemoteException(String);
+    method @NonNull public RuntimeException rethrowAsRuntimeException();
+    method @NonNull public RuntimeException rethrowFromSystemServer();
   }
 
   public class ResultReceiver implements android.os.Parcelable {
@@ -45225,6 +45230,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.os.ParcelUuid createSubscriptionGroup(@NonNull java.util.List<java.lang.Integer>);
     method @Deprecated public static android.telephony.SubscriptionManager from(android.content.Context);
     method public java.util.List<android.telephony.SubscriptionInfo> getAccessibleSubscriptionInfoList();
+    method public static int getActiveDataSubscriptionId();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfo(int);
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getActiveSubscriptionInfoCount();
     method public int getActiveSubscriptionInfoCountMax();
diff --git a/api/system-current.txt b/api/system-current.txt
index e924252..9420a28 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1414,6 +1414,7 @@
     method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public android.content.Intent registerReceiverForAllUsers(@Nullable android.content.BroadcastReceiver, @NonNull android.content.IntentFilter, @Nullable String, @Nullable android.os.Handler);
     method public abstract void sendBroadcast(android.content.Intent, @Nullable String, @Nullable android.os.Bundle);
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.os.Bundle);
+    method public void sendBroadcastMultiplePermissions(@NonNull android.content.Intent, @NonNull String[]);
     method public abstract void sendOrderedBroadcast(@NonNull android.content.Intent, @Nullable String, @Nullable android.os.Bundle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle);
     field public static final String APP_PREDICTION_SERVICE = "app_prediction";
@@ -6820,7 +6821,7 @@
     method public android.service.euicc.DownloadSubscriptionResult onDownloadSubscription(int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean, boolean, @Nullable android.os.Bundle);
     method @Deprecated public int onDownloadSubscription(int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean, boolean);
     method @Deprecated public abstract int onEraseSubscriptions(int);
-    method public int onEraseSubscriptionsWithOptions(int, @android.telephony.euicc.EuiccCardManager.ResetOption int);
+    method public int onEraseSubscriptions(int, @android.telephony.euicc.EuiccCardManager.ResetOption int);
     method public abstract android.service.euicc.GetDefaultDownloadableSubscriptionListResult onGetDefaultDownloadableSubscriptionList(int, boolean);
     method public abstract android.service.euicc.GetDownloadableSubscriptionMetadataResult onGetDownloadableSubscriptionMetadata(int, android.telephony.euicc.DownloadableSubscription, boolean);
     method public abstract String onGetEid(int);
@@ -8951,7 +8952,7 @@
   public class EuiccManager {
     method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void continueOperation(android.content.Intent, android.os.Bundle);
     method @Deprecated @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void eraseSubscriptions(@NonNull android.app.PendingIntent);
-    method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void eraseSubscriptionsWithOptions(@android.telephony.euicc.EuiccCardManager.ResetOption int, @NonNull android.app.PendingIntent);
+    method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void eraseSubscriptions(@android.telephony.euicc.EuiccCardManager.ResetOption int, @NonNull android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDefaultDownloadableSubscriptionList(android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDownloadableSubscriptionMetadata(android.telephony.euicc.DownloadableSubscription, android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public int getOtaStatus();
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index f0b3546..e858e6a 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1215,7 +1215,8 @@
         }
 
         // Rewrite the R 'constants' for all library apks.
-        SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers();
+        SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers(
+                false, false);
         final int N = packageIdentifiers.size();
         for (int i = 0; i < N; i++) {
             final int id = packageIdentifiers.keyAt(i);
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 2657cc5..61c8db5d 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -65,6 +65,7 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Size;
+import android.util.SparseArray;
 
 import com.android.internal.util.MimeIconUtils;
 import com.android.internal.util.Preconditions;
@@ -2381,15 +2382,15 @@
      *            true.
      * @param syncToNetwork If true, same as {@link #NOTIFY_SYNC_TO_NETWORK}.
      * @see #requestSync(android.accounts.Account, String, android.os.Bundle)
+     * @deprecated callers should consider migrating to
+     *             {@link #notifyChange(Uri, ContentObserver, int)}, as it
+     *             offers support for many more options than just
+     *             {@link #NOTIFY_SYNC_TO_NETWORK}.
      */
+    @Deprecated
     public void notifyChange(@NonNull Uri uri, @Nullable ContentObserver observer,
             boolean syncToNetwork) {
-        Preconditions.checkNotNull(uri, "uri");
-        notifyChange(
-                ContentProvider.getUriWithoutUserId(uri),
-                observer,
-                syncToNetwork,
-                ContentProvider.getUserIdFromUri(uri, mContext.getUserId()));
+        notifyChange(uri, observer, syncToNetwork ? NOTIFY_SYNC_TO_NETWORK : 0);
     }
 
     /**
@@ -2398,10 +2399,10 @@
      * To observe events sent through this call, use
      * {@link #registerContentObserver(Uri, boolean, ContentObserver)}.
      * <p>
-     * If syncToNetwork is true, this will attempt to schedule a local sync
-     * using the sync adapter that's registered for the authority of the
-     * provided uri. No account will be passed to the sync adapter, so all
-     * matching accounts will be synchronized.
+     * If {@link #NOTIFY_SYNC_TO_NETWORK} is set, this will attempt to schedule
+     * a local sync using the sync adapter that's registered for the authority
+     * of the provided uri. No account will be passed to the sync adapter, so
+     * all matching accounts will be synchronized.
      * <p>
      * Starting in {@link android.os.Build.VERSION_CODES#O}, all content
      * notifications must be backed by a valid {@link ContentProvider}.
@@ -2427,21 +2428,71 @@
     }
 
     /**
+     * Notify registered observers that several rows have been updated.
+     * <p>
+     * To observe events sent through this call, use
+     * {@link #registerContentObserver(Uri, boolean, ContentObserver)}.
+     * <p>
+     * If {@link #NOTIFY_SYNC_TO_NETWORK} is set, this will attempt to schedule
+     * a local sync using the sync adapter that's registered for the authority
+     * of the provided uri. No account will be passed to the sync adapter, so
+     * all matching accounts will be synchronized.
+     * <p>
+     * Starting in {@link android.os.Build.VERSION_CODES#O}, all content
+     * notifications must be backed by a valid {@link ContentProvider}.
+     *
+     * @param uris The uris of the content that was changed.
+     * @param observer The observer that originated the change, may be
+     *            <code>null</null>. The observer that originated the change
+     *            will only receive the notification if it has requested to
+     *            receive self-change notifications by implementing
+     *            {@link ContentObserver#deliverSelfNotifications()} to return
+     *            true.
+     * @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,
+            @NotifyFlags int flags) {
+        Preconditions.checkNotNull(uris, "uris");
+
+        // Cluster based on user ID
+        final SparseArray<ArrayList<Uri>> clusteredByUser = new SparseArray<>();
+        for (Uri uri : uris) {
+            final int userId = ContentProvider.getUserIdFromUri(uri, mContext.getUserId());
+            ArrayList<Uri> list = clusteredByUser.get(userId);
+            if (list == null) {
+                list = new ArrayList<>();
+                clusteredByUser.put(userId, list);
+            }
+            list.add(ContentProvider.getUriWithoutUserId(uri));
+        }
+
+        for (int i = 0; i < clusteredByUser.size(); i++) {
+            final int userId = clusteredByUser.keyAt(i);
+            final ArrayList<Uri> list = clusteredByUser.valueAt(i);
+            notifyChange(list.toArray(new Uri[list.size()]), observer, flags, userId);
+        }
+    }
+
+    /**
      * Notify registered observers within the designated user(s) that a row was updated.
      *
+     * @deprecated callers should consider migrating to
+     *             {@link #notifyChange(Uri, ContentObserver, int)}, as it
+     *             offers support for many more options than just
+     *             {@link #NOTIFY_SYNC_TO_NETWORK}.
      * @hide
      */
+    @Deprecated
     public void notifyChange(@NonNull Uri uri, ContentObserver observer, boolean syncToNetwork,
             @UserIdInt int userHandle) {
-        try {
-            getContentService().notifyChange(
-                    uri, observer == null ? null : observer.getContentObserver(),
-                    observer != null && observer.deliverSelfNotifications(),
-                    syncToNetwork ? NOTIFY_SYNC_TO_NETWORK : 0,
-                    userHandle, mTargetSdkVersion, mContext.getPackageName());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        notifyChange(uri, observer, syncToNetwork ? NOTIFY_SYNC_TO_NETWORK : 0, userHandle);
+    }
+
+    /** {@hide} */
+    public void notifyChange(@NonNull Uri uri, ContentObserver observer, @NotifyFlags int flags,
+            @UserIdInt int userHandle) {
+        notifyChange(new Uri[] { uri }, observer, flags, userHandle);
     }
 
     /**
@@ -2449,11 +2500,11 @@
      *
      * @hide
      */
-    public void notifyChange(@NonNull Uri uri, ContentObserver observer, @NotifyFlags int flags,
+    public void notifyChange(@NonNull Uri[] uris, ContentObserver observer, @NotifyFlags int flags,
             @UserIdInt int userHandle) {
         try {
             getContentService().notifyChange(
-                    uri, observer == null ? null : observer.getContentObserver(),
+                    uris, observer == null ? null : observer.getContentObserver(),
                     observer != null && observer.deliverSelfNotifications(), flags,
                     userHandle, mTargetSdkVersion, mContext.getPackageName());
         } catch (RemoteException e) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 41b773e..03b4913 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2072,7 +2072,7 @@
      *               Intent will receive the broadcast.
      * @param receiverPermissions Array of names of permissions that a receiver must hold
      *                            in order to receive your broadcast.
-     *                            If null or empty, no permissions are required.
+     *                            If empty, no permissions are required.
      *
      * @see android.content.BroadcastReceiver
      * @see #registerReceiver
@@ -2081,8 +2081,11 @@
      * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
      * @hide
      */
-    public abstract void sendBroadcastMultiplePermissions(Intent intent,
-            String[] receiverPermissions);
+    @SystemApi
+    public void sendBroadcastMultiplePermissions(@NonNull Intent intent,
+            @NonNull String[] receiverPermissions) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
 
     /**
      * Broadcast the given intent to all interested BroadcastReceivers, allowing
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 5bdea52..b04f781 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -464,7 +464,8 @@
 
     /** @hide */
     @Override
-    public void sendBroadcastMultiplePermissions(Intent intent, String[] receiverPermissions) {
+    public void sendBroadcastMultiplePermissions(@NonNull Intent intent,
+            @NonNull String[] receiverPermissions) {
         mBase.sendBroadcastMultiplePermissions(intent, receiverPermissions);
     }
 
diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl
index a34a995..03c99e1 100644
--- a/core/java/android/content/IContentService.aidl
+++ b/core/java/android/content/IContentService.aidl
@@ -51,7 +51,7 @@
      *     hold the INTERACT_ACROSS_USERS_FULL permission.  Pseudousers USER_ALL
      *     USER_CURRENT are properly interpreted.
      */
-    void notifyChange(in Uri uri, IContentObserver observer,
+    void notifyChange(in Uri[] uris, IContentObserver observer,
             boolean observerWantsSelfNotifications, int flags,
             int userHandle, int targetSdkVersion, String callingPackage);
 
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 23e7720..070e282 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -1491,9 +1491,17 @@
      */
     @UnsupportedAppUsage
     public SparseArray<String> getAssignedPackageIdentifiers() {
+        return getAssignedPackageIdentifiers(true, true);
+    }
+
+    /**
+     * @hide
+     */
+    public SparseArray<String> getAssignedPackageIdentifiers(boolean includeOverlays,
+            boolean includeLoaders) {
         synchronized (this) {
             ensureValidLocked();
-            return nativeGetAssignedPackageIdentifiers(mObject);
+            return nativeGetAssignedPackageIdentifiers(mObject, includeOverlays, includeLoaders);
         }
     }
 
@@ -1557,7 +1565,7 @@
             int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout,
             int uiMode, int colorMode, int majorVersion);
     private static native @NonNull SparseArray<String> nativeGetAssignedPackageIdentifiers(
-            long ptr);
+            long ptr, boolean includeOverlays, boolean includeLoaders);
 
     // File native methods.
     private static native @Nullable String[] nativeList(long ptr, @NonNull String path)
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index a856975..ec39199 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -932,8 +932,14 @@
         }
 
         int result = -1;
-        try {
-            result = handleShellCommand(in, out, err, args);
+        try (ParcelFileDescriptor inPfd = ParcelFileDescriptor.dup(in);
+                ParcelFileDescriptor outPfd = ParcelFileDescriptor.dup(out);
+                ParcelFileDescriptor errPfd = ParcelFileDescriptor.dup(err)) {
+            result = handleShellCommand(inPfd, outPfd, errPfd, args);
+        } catch (IOException e) {
+            PrintWriter pw = new FastPrintWriter(new FileOutputStream(err));
+            pw.println("dup() failed: " + e.getMessage());
+            pw.flush();
         } finally {
             resultReceiver.send(result, null);
         }
@@ -954,9 +960,10 @@
      * @hide
      */
     // @SystemApi TODO Make it a system API.
-    protected int handleShellCommand(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
-            @NonNull FileDescriptor err, @NonNull String[] args) {
-        FileOutputStream ferr = new FileOutputStream(err);
+    protected int handleShellCommand(@NonNull ParcelFileDescriptor in,
+            @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
+            @NonNull String[] args) {
+        FileOutputStream ferr = new FileOutputStream(err.getFileDescriptor());
         PrintWriter pw = new FastPrintWriter(ferr);
         pw.println("No shell command implementation.");
         pw.flush();
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 7330796..400d981 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -240,13 +240,6 @@
         public static final String RELEASE = getString("ro.build.version.release");
 
         /**
-         * The version string we show to the user; may be {@link #RELEASE} or
-         * {@link #CODENAME} if not a final release build.
-         */
-        @NonNull public static final String RELEASE_OR_CODENAME = getString(
-                "ro.build.version.release_or_codename");
-
-        /**
          * The base OS build the product is based on.
          */
         public static final String BASE_OS = SystemProperties.get("ro.build.version.base_os", "");
diff --git a/core/java/android/os/RemoteException.java b/core/java/android/os/RemoteException.java
index 2e673a8..10ef279 100644
--- a/core/java/android/os/RemoteException.java
+++ b/core/java/android/os/RemoteException.java
@@ -16,7 +16,7 @@
 
 package android.os;
 
-import android.annotation.UnsupportedAppUsage;
+import android.annotation.NonNull;
 import android.util.AndroidException;
 
 /**
@@ -37,7 +37,15 @@
         super(message, cause, enableSuppression, writableStackTrace);
     }
 
-    /** {@hide} */
+    /**
+     * Rethrow this as an unchecked runtime exception.
+     * <p>
+     * Apps making calls into other processes may end up persisting internal
+     * state or making security decisions based on the perceived success or
+     * failure of a call, or any default values returned. For this reason, we
+     * want to strongly throw when there was trouble with the transaction.
+     */
+    @NonNull
     public RuntimeException rethrowAsRuntimeException() {
         throw new RuntimeException(this);
     }
@@ -52,10 +60,8 @@
      * state or making security decisions based on the perceived success or
      * failure of a call, or any default values returned. For this reason, we
      * want to strongly throw when there was trouble with the transaction.
-     *
-     * @hide
      */
-    @UnsupportedAppUsage
+    @NonNull
     public RuntimeException rethrowFromSystemServer() {
         if (this instanceof DeadObjectException) {
             throw new RuntimeException(new DeadSystemException());
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4773d98..ac53f1b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8375,7 +8375,6 @@
 
             INSTANT_APP_SETTINGS.add(ANDROID_ID);
 
-            INSTANT_APP_SETTINGS.add(PACKAGE_VERIFIER_USER_CONSENT);
             INSTANT_APP_SETTINGS.add(ALLOW_MOCK_LOCATION);
         }
 
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index 2d40ec4..bc6a9e8 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -546,7 +546,7 @@
      * @see android.telephony.euicc.EuiccManager#eraseSubscriptions
      *
      * @deprecated From R, callers should specify a flag for specific set of subscriptions to erase
-     * and use {@link #onEraseSubscriptionsWithOptions(int, int)} instead
+     * and use {@link #onEraseSubscriptions(int, int)} instead
      */
     @Deprecated
     public abstract int onEraseSubscriptions(int slotId);
@@ -563,7 +563,7 @@
      *     constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
      * @see android.telephony.euicc.EuiccManager#eraseSubscriptionsWithOptions
      */
-    public int onEraseSubscriptionsWithOptions(int slotIndex, @ResetOption int options) {
+    public int onEraseSubscriptions(int slotIndex, @ResetOption int options) {
         throw new UnsupportedOperationException(
                 "This method must be overridden to enable the ResetOption parameter");
     }
@@ -809,8 +809,7 @@
             mExecutor.execute(new Runnable() {
                 @Override
                 public void run() {
-                    int result = EuiccService.this.onEraseSubscriptionsWithOptions(
-                            slotIndex, options);
+                    int result = EuiccService.this.onEraseSubscriptions(slotIndex, options);
                     try {
                         callback.onComplete(result);
                     } catch (RemoteException e) {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index b815c64..3251127 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -90,7 +90,8 @@
             Rect sourceCrop, int width, int height, boolean useIdentityTransform, int rotation,
             boolean captureSecureLayers);
     private static native ScreenshotGraphicBuffer nativeCaptureLayers(IBinder displayToken,
-            long layerObject, Rect sourceCrop, float frameScale, long[] excludeLayerObjects);
+            long layerObject, Rect sourceCrop, float frameScale, long[] excludeLayerObjects,
+            int format);
     private static native long nativeMirrorSurface(long mirrorOfObject);
     private static native long nativeCreateTransaction();
     private static native long nativeGetNativeTransactionFinalizer();
@@ -1869,8 +1870,27 @@
      */
     public static ScreenshotGraphicBuffer captureLayers(SurfaceControl layer, Rect sourceCrop,
             float frameScale) {
+        return captureLayers(layer, sourceCrop, frameScale, PixelFormat.RGBA_8888);
+    }
+
+    /**
+     * Captures a layer and its children and returns a {@link GraphicBuffer} with the content.
+     *
+     * @param layer            The root layer to capture.
+     * @param sourceCrop       The portion of the root surface to capture; caller may pass in 'new
+     *                         Rect()' or null if no cropping is desired.
+     * @param frameScale       The desired scale of the returned buffer; the raw
+     *                         screen will be scaled up/down.
+     * @param format           The desired pixel format of the returned buffer.
+     *
+     * @return Returns a GraphicBuffer that contains the layer capture.
+     * @hide
+     */
+    public static ScreenshotGraphicBuffer captureLayers(SurfaceControl layer, Rect sourceCrop,
+            float frameScale, int format) {
         final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
-        return nativeCaptureLayers(displayToken, layer.mNativeObject, sourceCrop, frameScale, null);
+        return nativeCaptureLayers(displayToken, layer.mNativeObject, sourceCrop, frameScale, null,
+                format);
     }
 
     /**
@@ -1885,7 +1905,7 @@
             nativeExcludeObjects[i] = exclude[i].mNativeObject;
         }
         return nativeCaptureLayers(displayToken, layer.mNativeObject, sourceCrop, frameScale,
-                nativeExcludeObjects);
+                nativeExcludeObjects, PixelFormat.RGBA_8888);
     }
 
     /**
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index da43edd..fd3cd42 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -277,7 +277,7 @@
         result.append(System.getProperty("java.vm.version")); // such as 1.1.0
         result.append(" (Linux; U; Android ");
 
-        String version = Build.VERSION.RELEASE_OR_CODENAME; // "1.0" or "3.4b5"
+        String version = Build.VERSION.RELEASE; // "1.0" or "3.4b5"
         result.append(version.length() > 0 ? version : "1.0");
 
         // add the model for the release build
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 3c0971b..5c4dc23 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -556,7 +556,9 @@
   assetmanager->SetConfiguration(configuration);
 }
 
-static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
+static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr,
+                                                   jboolean includeOverlays,
+                                                   jboolean includeLoaders) {
   ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
 
   jobject sparse_array =
@@ -567,6 +569,10 @@
     return nullptr;
   }
 
+  // Optionally exclude overlays and loaders.
+  uint64_t exclusion_flags = ((includeOverlays) ? 0U : PROPERTY_OVERLAY)
+      | ((includeLoaders) ? 0U : PROPERTY_LOADER);
+
   assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) -> bool {
     jstring jpackage_name = env->NewStringUTF(package_name.c_str());
     if (jpackage_name == nullptr) {
@@ -577,7 +583,8 @@
     env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast<jint>(package_id),
                         jpackage_name);
     return true;
-  });
+  }, exclusion_flags);
+
   return sparse_array;
 }
 
@@ -1591,7 +1598,7 @@
     {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets},
     {"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIII)V",
      (void*)NativeSetConfiguration},
-    {"nativeGetAssignedPackageIdentifiers", "(J)Landroid/util/SparseArray;",
+    {"nativeGetAssignedPackageIdentifiers", "(JZZ)Landroid/util/SparseArray;",
      (void*)NativeGetAssignedPackageIdentifiers},
 
     // AssetManager file methods.
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index c807e90..f8a2744 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -274,7 +274,7 @@
 
 static jobject nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject displayTokenObj,
         jlong layerObject, jobject sourceCropObj, jfloat frameScale,
-        jlongArray excludeObjectArray) {
+        jlongArray excludeObjectArray, jint format) {
 
     auto layer = reinterpret_cast<SurfaceControl *>(layerObject);
     if (layer == NULL) {
@@ -311,8 +311,9 @@
         dataspace = pickDataspaceFromColorMode(colorMode);
     }
     status_t res = ScreenshotClient::captureChildLayers(layer->getHandle(), dataspace,
-                                                        ui::PixelFormat::RGBA_8888, sourceCrop,
-                                                        excludeHandles, frameScale, &buffer);
+                                                        static_cast<ui::PixelFormat>(format),
+                                                        sourceCrop, excludeHandles, frameScale,
+                                                        &buffer);
     if (res != NO_ERROR) {
         return NULL;
     }
@@ -1386,7 +1387,7 @@
             (void*)nativeScreenshot },
     {"nativeCaptureLayers",
             "(Landroid/os/IBinder;JLandroid/graphics/Rect;"
-            "F[J)"
+            "F[JI)"
             "Landroid/view/SurfaceControl$ScreenshotGraphicBuffer;",
             (void*)nativeCaptureLayers },
     {"nativeSetInputWindowInfo", "(JJLandroid/view/InputWindowHandle;)V",
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index ce2717b..8f084ab 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -126,12 +126,11 @@
     optional .com.android.server.wm.ConfigurationContainerProto configuration_container = 1 [deprecated=true];
     optional .com.android.server.wm.IdentifierProto identifier = 2;
     optional string state = 3;
-    optional bool visible_requested = 4;
+    optional bool visible = 4;
     optional bool front_of_task = 5;
     optional int32 proc_id = 6;
     optional bool translucent = 7;
     optional .com.android.server.wm.AppWindowTokenProto app_window_token = 8;
-    optional bool visible = 9;
 }
 
 message KeyguardControllerProto {
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 23566a6..653d381 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -235,8 +235,8 @@
     optional WindowContainerThumbnailProto thumbnail = 6;
     optional bool fills_parent = 7;
     optional bool app_stopped = 8;
-    optional bool visible_requested = 9;
-    optional bool client_visible = 10;
+    optional bool hidden_requested = 9;
+    optional bool client_hidden = 10;
     optional bool defer_hiding_client = 11;
     optional bool reported_drawn = 12;
     optional bool reported_visible = 13;
@@ -248,9 +248,8 @@
     optional IdentifierProto starting_window = 19;
     optional bool starting_displayed = 20;
     optional bool starting_moved = 21;
-    optional bool visible_set_from_transferred_starting_window = 22;
+    optional bool hidden_set_from_transferred_starting_window = 22;
     repeated .android.graphics.RectProto frozen_bounds = 23;
-    optional bool visible = 24;
 }
 
 /* represents WindowToken */
@@ -260,6 +259,7 @@
     optional WindowContainerProto window_container = 1;
     optional int32 hash_code = 2;
     repeated WindowStateProto windows = 3;
+    optional bool hidden = 4;
     optional bool waiting_to_show = 5;
     optional bool paused = 6;
 }
diff --git a/core/proto/android/stats/textclassifier/Android.bp b/core/proto/android/stats/textclassifier/Android.bp
new file mode 100644
index 0000000..bf90227
--- /dev/null
+++ b/core/proto/android/stats/textclassifier/Android.bp
@@ -0,0 +1,23 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_library_static {
+    name: "textclassifierprotoslite",
+    proto: {
+        type: "lite",
+    },
+    srcs: [
+        "*.proto",
+    ],
+}
\ No newline at end of file
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index efa42e5..cb0b599 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2652,6 +2652,9 @@
     <!-- The amount to scale fullscreen snapshots for Overview and snapshot starting windows. -->
     <item name="config_fullTaskSnapshotScale" format="float" type="dimen">1.0</item>
 
+    <!-- Feature flag to store TaskSnapshot in 16 bit pixel format to save memory. -->
+    <bool name="config_use16BitTaskSnapshotPixelFormat">false</bool>
+
     <!-- Determines whether recent tasks are provided to the user. Default device has recents
          property. If this is false, then the following recents config flags are ignored. -->
     <bool name="config_hasRecents">true</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 91a8ba4..28809da 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -356,6 +356,7 @@
   <java-symbol type="bool" name="config_enableNewAutoSelectNetworkUI"/>
   <java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
   <java-symbol type="dimen" name="config_fullTaskSnapshotScale" />
+  <java-symbol type="bool" name="config_use16BitTaskSnapshotPixelFormat" />
   <java-symbol type="bool" name="config_lowRamTaskSnapshotsAndRecents" />
   <java-symbol type="bool" name="config_hasRecents" />
   <java-symbol type="string" name="config_recentsComponentName" />
diff --git a/core/tests/coretests/src/android/os/BuildTest.java b/core/tests/coretests/src/android/os/BuildTest.java
index 2295eb9..decc768 100644
--- a/core/tests/coretests/src/android/os/BuildTest.java
+++ b/core/tests/coretests/src/android/os/BuildTest.java
@@ -60,7 +60,7 @@
         assertNotEmpty("BRAND", Build.BRAND);
         assertNotEmpty("MODEL", Build.MODEL);
         assertNotEmpty("VERSION.INCREMENTAL", Build.VERSION.INCREMENTAL);
-        assertNotEmpty("VERSION.RELEASE", Build.VERSION.RELEASE_OR_CODENAME);
+        assertNotEmpty("VERSION.RELEASE", Build.VERSION.RELEASE);
         assertNotEmpty("TYPE", Build.TYPE);
         Assert.assertNotNull("TAGS", Build.TAGS); // TAGS is allowed to be empty.
         assertNotEmpty("FINGERPRINT", Build.FINGERPRINT);
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 3c89bfd..753f8a0 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -55,12 +55,6 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
     },
-    "-2006946193": {
-      "message": "setClientVisible: %s clientVisible=%b Callers=%s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_APP_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/ActivityRecord.java"
-    },
     "-2002500255": {
       "message": "Defer removing snapshot surface in %dms",
       "level": "VERBOSE",
@@ -79,18 +73,18 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "-1976550065": {
-      "message": "commitVisibility: %s: visible=%b visibleRequested=%b",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_APP_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/ActivityRecord.java"
-    },
     "-1963461591": {
       "message": "Removing %s from %s",
       "level": "VERBOSE",
       "group": "WM_DEBUG_ADD_REMOVE",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-1958209312": {
+      "message": "Clear freezing of %s: hidden=%b freezing=%b",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_ORIENTATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-1953668890": {
       "message": "Can't start recents animation, nextAppTransition=%s",
       "level": "DEBUG",
@@ -313,6 +307,12 @@
       "group": "WM_DEBUG_ADD_REMOVE",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "-1456549051": {
+      "message": "setClientHidden: %s clientHidden=%b Callers=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_APP_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-1455600136": {
       "message": "Attempted to add Dream window with unknown token %s.  Aborting.",
       "level": "WARN",
@@ -547,6 +547,12 @@
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/WindowState.java"
     },
+    "-931184679": {
+      "message": "Changing app %s hidden=%b performLayout=%b",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_APP_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-928291778": {
       "message": "applyAnimation: anim=%s nextAppTransition=%d transit=%s Callers=%s",
       "level": "VERBOSE",
@@ -835,12 +841,6 @@
       "group": "WM_DEBUG_APP_TRANSITIONS",
       "at": "com\/android\/server\/wm\/AppTransitionController.java"
     },
-    "-374767836": {
-      "message": "setAppVisibility(%s, visible=%b): %s visible=%b mVisibleRequested=%b Callers=%s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_APP_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/ActivityRecord.java"
-    },
     "-371630969": {
       "message": "New wallpaper target=%s, oldWallpaper=%s, openingApps=%s, closingApps=%s",
       "level": "VERBOSE",
@@ -1207,6 +1207,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "358613119": {
+      "message": "setAppVisibility(%s, visible=%b): %s hidden=%b hiddenRequested=%b Callers=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_APP_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "371641947": {
       "message": "Window Manager Crash %s",
       "level": "WTF",
@@ -1267,12 +1273,6 @@
       "group": "WM_DEBUG_SCREEN_ON",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "466506262": {
-      "message": "Clear freezing of %s: visible=%b freezing=%b",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_ORIENTATION",
-      "at": "com\/android\/server\/wm\/ActivityRecord.java"
-    },
     "474000473": {
       "message": "No stack above target stack=%s",
       "level": "DEBUG",
@@ -1489,12 +1489,6 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
     },
-    "841702299": {
-      "message": "Changing app %s visible=%b performLayout=%b",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_APP_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/ActivityRecord.java"
-    },
     "845234215": {
       "message": "App is requesting an orientation, return %d for display id=%d",
       "level": "VERBOSE",
@@ -1507,6 +1501,12 @@
       "group": "WM_DEBUG_RECENTS_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RecentsAnimation.java"
     },
+    "857751535": {
+      "message": "commitVisibility: %s: hidden=%b hiddenRequested=%b",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_APP_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "873914452": {
       "message": "goodToGo()",
       "level": "DEBUG",
@@ -1903,12 +1903,6 @@
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
-    "1746778201": {
-      "message": "Set freezing of %s: visible=%b freezing=%b visibleRequested=%b. %s",
-      "level": "INFO",
-      "group": "WM_DEBUG_ORIENTATION",
-      "at": "com\/android\/server\/wm\/ActivityRecord.java"
-    },
     "1747941491": {
       "message": "SURFACE controller=%s alpha=%f matrix=[%f*%f,%f*%f][%f*%f,%f*%f]: %s",
       "level": "INFO",
@@ -1999,6 +1993,12 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
     },
+    "1966564525": {
+      "message": "Set freezing of %s: hidden=%b freezing=%b hiddenRequested=%b. %s",
+      "level": "INFO",
+      "group": "WM_DEBUG_ORIENTATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "1984470582": {
       "message": "Creating TaskScreenshotAnimatable: task: %s width: %d height: %d",
       "level": "DEBUG",
diff --git a/data/sounds/AudioPackage11.mk b/data/sounds/AudioPackage11.mk
index 99dfd0a..2392b33 100644
--- a/data/sounds/AudioPackage11.mk
+++ b/data/sounds/AudioPackage11.mk
@@ -32,7 +32,7 @@
 	$(LOCAL_PATH)/effects/ogg/Lock_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
 	$(LOCAL_PATH)/effects/ogg/Unlock_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
 	$(LOCAL_PATH)/effects/ogg/Trusted_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/effects/ogg/ChargingStarted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/ChargingStarted.ogg \.
+	$(LOCAL_PATH)/effects/ogg/ChargingStarted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/ChargingStarted.ogg \
 	$(LOCAL_PATH)/effects/ogg/InCallNotification.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/InCallNotification.ogg \
 	$(LOCAL_PATH)/effects/material/ogg/WirelessChargingStarted_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/WirelessChargingStarted.ogg \
 	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Adara.ogg \
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 538319c..a7e17d1 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -55,8 +55,8 @@
 import java.util.Collection;
 import java.util.List;
 import java.util.Locale;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
 
 import javax.security.auth.x500.X500Principal;
 
@@ -811,27 +811,22 @@
             throw new NullPointerException("context == null");
         }
         ensureNotOnMainThread(context);
-        final BlockingQueue<IKeyChainService> q = new LinkedBlockingQueue<IKeyChainService>(1);
+        final CountDownLatch countDownLatch = new CountDownLatch(1);
+        final AtomicReference<IKeyChainService> keyChainService = new AtomicReference<>();
         ServiceConnection keyChainServiceConnection = new ServiceConnection() {
             volatile boolean mConnectedAtLeastOnce = false;
             @Override public void onServiceConnected(ComponentName name, IBinder service) {
                 if (!mConnectedAtLeastOnce) {
                     mConnectedAtLeastOnce = true;
-                    try {
-                        q.put(IKeyChainService.Stub.asInterface(Binder.allowBlocking(service)));
-                    } catch (InterruptedException e) {
-                        // will never happen, since the queue starts with one available slot
-                    }
+                    keyChainService.set(
+                            IKeyChainService.Stub.asInterface(Binder.allowBlocking(service)));
+                    countDownLatch.countDown();
                 }
             }
             @Override public void onBindingDied(ComponentName name) {
                 if (!mConnectedAtLeastOnce) {
                     mConnectedAtLeastOnce = true;
-                    try {
-                        q.put(null);
-                    } catch (InterruptedException e) {
-                        // will never happen, since the queue starts with one available slot
-                    }
+                    countDownLatch.countDown();
                 }
             }
             @Override public void onServiceDisconnected(ComponentName name) {}
@@ -843,7 +838,8 @@
                 intent, keyChainServiceConnection, Context.BIND_AUTO_CREATE, user)) {
             throw new AssertionError("could not bind to KeyChainService");
         }
-        IKeyChainService service = q.take();
+        countDownLatch.await();
+        IKeyChainService service = keyChainService.get();
         if (service != null) {
             return new KeyChainConnection(context, keyChainServiceConnection, service);
         } else {
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 16dbbf6..18934fd 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -43,20 +43,22 @@
 ApkAssets::ApkAssets(ZipArchiveHandle unmanaged_handle,
                      const std::string& path,
                      time_t last_mod_time,
-                     bool for_loader)
+                     package_property_t property_flags)
     : zip_handle_(unmanaged_handle, ::CloseArchive), path_(path), last_mod_time_(last_mod_time),
-      for_loader_(for_loader) {
+      property_flags_(property_flags) {
 }
 
 std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system,
                                                  bool for_loader) {
-  return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, false /*load_as_shared_library*/,
-                  for_loader);
+  package_property_t flags = (system ? PROPERTY_SYSTEM : 0U) |
+                             (for_loader ? PROPERTY_LOADER : 0U);
+  return LoadImpl({} /*fd*/, path, nullptr, nullptr, flags);
 }
 
 std::unique_ptr<const ApkAssets> ApkAssets::LoadAsSharedLibrary(const std::string& path,
                                                                 bool system) {
-  return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, true /*load_as_shared_library*/);
+  package_property_t flags = PROPERTY_DYNAMIC | (system ? PROPERTY_SYSTEM : 0U);
+  return LoadImpl({} /*fd*/, path, nullptr, nullptr, flags);
 }
 
 std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
@@ -74,27 +76,33 @@
     LOG(ERROR) << "failed to load IDMAP " << idmap_path;
     return {};
   }
-  return LoadImpl({} /*fd*/, loaded_idmap->OverlayApkPath(), std::move(idmap_asset),
-                  std::move(loaded_idmap), system, true /*load_as_shared_library*/);
+
+  return LoadImpl({} /*fd*/, loaded_idmap->OverlayApkPath(),
+                  std::move(idmap_asset),
+                  std::move(loaded_idmap),
+                  PROPERTY_OVERLAY | (system ? PROPERTY_SYSTEM : 0U));
 }
 
 std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(unique_fd fd,
                                                        const std::string& friendly_name,
                                                        bool system, bool force_shared_lib,
                                                        bool for_loader) {
+  package_property_t flags = (system ? PROPERTY_SYSTEM : 0U) |
+                             (force_shared_lib ? PROPERTY_DYNAMIC : 0U) |
+                             (for_loader ? PROPERTY_LOADER : 0U);
   return LoadImpl(std::move(fd), friendly_name, nullptr /*idmap_asset*/, nullptr /*loaded_idmap*/,
-                  system, force_shared_lib, for_loader);
+                  flags);
 }
 
 std::unique_ptr<const ApkAssets> ApkAssets::LoadArsc(const std::string& path,
                                                      bool for_loader) {
-  return LoadArscImpl({} /*fd*/, path, for_loader);
+  return LoadArscImpl({} /*fd*/, path, for_loader ? PROPERTY_LOADER : 0U);
 }
 
 std::unique_ptr<const ApkAssets> ApkAssets::LoadArsc(unique_fd fd,
                                                      const std::string& friendly_name,
                                                      bool for_loader) {
-  return LoadArscImpl(std::move(fd), friendly_name, for_loader);
+  return LoadArscImpl(std::move(fd), friendly_name, for_loader ? PROPERTY_LOADER : 0U);
 }
 
 std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) {
@@ -120,8 +128,7 @@
 
 std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
     unique_fd fd, const std::string& path, std::unique_ptr<Asset> idmap_asset,
-    std::unique_ptr<const LoadedIdmap> loaded_idmap, bool system, bool load_as_shared_library,
-    bool for_loader) {
+    std::unique_ptr<const LoadedIdmap> loaded_idmap, package_property_t property_flags) {
   ::ZipArchiveHandle unmanaged_handle;
   int32_t result;
   if (fd >= 0) {
@@ -141,7 +148,7 @@
 
   // Wrap the handle in a unique_ptr so it gets automatically closed.
   std::unique_ptr<ApkAssets>
-      loaded_apk(new ApkAssets(unmanaged_handle, path, last_mod_time, for_loader));
+      loaded_apk(new ApkAssets(unmanaged_handle, path, last_mod_time, property_flags));
 
   // Find the resource table.
   ::ZipEntry entry;
@@ -170,9 +177,8 @@
   const StringPiece data(
       reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
       loaded_apk->resources_asset_->getLength());
-  loaded_apk->loaded_arsc_ =
-      LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(), system, load_as_shared_library,
-                       for_loader);
+  loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(),
+                                              property_flags);
   if (loaded_apk->loaded_arsc_ == nullptr) {
     LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'.";
     return {};
@@ -184,7 +190,7 @@
 
 std::unique_ptr<const ApkAssets> ApkAssets::LoadArscImpl(unique_fd fd,
                                                          const std::string& path,
-                                                         bool for_loader) {
+                                                         package_property_t property_flags) {
   std::unique_ptr<Asset> resources_asset;
 
   if (fd >= 0) {
@@ -201,13 +207,14 @@
 
   time_t last_mod_time = getFileModDate(path.c_str());
 
-  std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(nullptr, path, last_mod_time, for_loader));
+  std::unique_ptr<ApkAssets> loaded_apk(
+      new ApkAssets(nullptr, path, last_mod_time, property_flags));
   loaded_apk->resources_asset_ = std::move(resources_asset);
 
   const StringPiece data(
       reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
       loaded_apk->resources_asset_->getLength());
-  loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, false, false, for_loader);
+  loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, property_flags);
   if (loaded_apk->loaded_arsc_ == nullptr) {
     LOG(ERROR) << "Failed to load '" << kResourcesArsc << path;
     return {};
@@ -320,8 +327,8 @@
 }
 
 bool ApkAssets::IsUpToDate() const {
-  // Loaders are invalidated by the app, not the system, so assume up to date
-  if (for_loader_) {
+  if (IsLoader()) {
+    // Loaders are invalidated by the app, not the system, so assume up to date.
     return true;
   }
 
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index 2b69c92..773353d 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -44,8 +44,8 @@
 }
 
 OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap)
-                                     : data_header_(loaded_idmap->data_header_),
-                                       idmap_string_pool_(loaded_idmap->string_pool_.get()) { };
+    : data_header_(loaded_idmap->data_header_),
+      idmap_string_pool_(loaded_idmap->string_pool_.get()) { };
 
 OverlayStringPool::~OverlayStringPool() {
   uninit();
@@ -188,11 +188,12 @@
                          const Idmap_data_header* data_header,
                          const Idmap_target_entry* target_entries,
                          const Idmap_overlay_entry* overlay_entries,
-                         ResStringPool* string_pool) : header_(header),
-                                                       data_header_(data_header),
-                                                       target_entries_(target_entries),
-                                                       overlay_entries_(overlay_entries),
-                                                       string_pool_(string_pool) {
+                         ResStringPool* string_pool)
+     : header_(header),
+       data_header_(data_header),
+       target_entries_(target_entries),
+       overlay_entries_(overlay_entries),
+       string_pool_(string_pool) {
 
   size_t length = strnlen(reinterpret_cast<const char*>(header_->overlay_path),
                           arraysize(header_->overlay_path));
@@ -264,7 +265,7 @@
     }
   }
 
-   // Can't use make_unique because LoadedImpl constructor is private.
+  // Can't use make_unique because LoadedIdmap constructor is private.
   std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>(
       new LoadedIdmap(header, data_header, target_entries, overlay_entries,
                       idmap_string_pool.release()));
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index c896241..e35c024 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -397,9 +397,7 @@
 }
 
 std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
-                                                         bool system,
-                                                         bool load_as_shared_library,
-                                                         bool for_loader) {
+                                                         package_property_t property_flags) {
   ATRACE_NAME("LoadedPackage::Load");
   std::unique_ptr<LoadedPackage> loaded_package(new LoadedPackage());
 
@@ -413,17 +411,24 @@
     return {};
   }
 
-  loaded_package->system_ = system;
+  if ((property_flags & PROPERTY_SYSTEM) != 0) {
+    loaded_package->property_flags_ |= PROPERTY_SYSTEM;
+  }
+
+  if ((property_flags & PROPERTY_LOADER) != 0) {
+    loaded_package->property_flags_ |= PROPERTY_LOADER;
+  }
+
+  if ((property_flags & PROPERTY_OVERLAY) != 0) {
+    // Overlay resources must have an exclusive resource id space for referencing internal
+    // resources.
+    loaded_package->property_flags_ |= PROPERTY_OVERLAY | PROPERTY_DYNAMIC;
+  }
 
   loaded_package->package_id_ = dtohl(header->id);
   if (loaded_package->package_id_ == 0 ||
-      (loaded_package->package_id_ == kAppPackageId && load_as_shared_library)) {
-    // Package ID of 0 means this is a shared library.
-    loaded_package->dynamic_ = true;
-  }
-
-  if (for_loader) {
-    loaded_package->custom_loader_ = true;
+      (loaded_package->package_id_ == kAppPackageId && (property_flags & PROPERTY_DYNAMIC) != 0)) {
+    loaded_package->property_flags_ |= PROPERTY_DYNAMIC;
   }
 
   if (header->header.headerSize >= sizeof(ResTable_package)) {
@@ -677,7 +682,7 @@
 }
 
 bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
-                           bool load_as_shared_library, bool for_loader) {
+                           package_property_t property_flags) {
   const ResTable_header* header = chunk.header<ResTable_header>();
   if (header == nullptr) {
     LOG(ERROR) << "RES_TABLE_TYPE too small.";
@@ -720,7 +725,7 @@
         packages_seen++;
 
         std::unique_ptr<const LoadedPackage> loaded_package =
-            LoadedPackage::Load(child_chunk, system_, load_as_shared_library, for_loader);
+            LoadedPackage::Load(child_chunk, property_flags);
         if (!loaded_package) {
           return false;
         }
@@ -744,24 +749,18 @@
 
 std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const StringPiece& data,
                                                    const LoadedIdmap* loaded_idmap,
-                                                   bool system,
-                                                   bool load_as_shared_library,
-                                                   bool for_loader) {
+                                                   package_property_t property_flags) {
   ATRACE_NAME("LoadedArsc::Load");
 
   // Not using make_unique because the constructor is private.
   std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc());
-  loaded_arsc->system_ = system;
 
   ChunkIterator iter(data.data(), data.size());
   while (iter.HasNext()) {
     const Chunk chunk = iter.Next();
     switch (chunk.type()) {
       case RES_TABLE_TYPE:
-        if (!loaded_arsc->LoadTable(chunk,
-                                    loaded_idmap,
-                                    load_as_shared_library,
-                                    for_loader)) {
+        if (!loaded_arsc->LoadTable(chunk, loaded_idmap, property_flags)) {
           return {};
         }
         break;
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index 2047287..af802b0 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -76,10 +76,10 @@
   // Takes ownership of the file descriptor.
   static std::unique_ptr<const ApkAssets> LoadArsc(base::unique_fd fd,
                                                    const std::string& friendly_name,
-                                                   bool resource_loader = false);
+                                                   bool for_loader = false);
 
   // Creates a totally empty ApkAssets with no resources table and no file entries.
-  static std::unique_ptr<const ApkAssets> LoadEmpty(bool resource_loader = false);
+  static std::unique_ptr<const ApkAssets> LoadEmpty(bool for_loader = false);
 
   std::unique_ptr<Asset> Open(const std::string& path,
                               Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const;
@@ -100,12 +100,12 @@
     return loaded_idmap_.get();
   }
 
-  inline bool IsOverlay() const {
-    return idmap_asset_.get() != nullptr;
+  inline bool IsLoader() const {
+    return (property_flags_ & PROPERTY_LOADER) != 0;
   }
 
-  inline bool IsLoader() const {
-    return for_loader_;
+  inline bool IsOverlay() const {
+    return (property_flags_ & PROPERTY_OVERLAY) != 0;
   }
 
   bool IsUpToDate() const;
@@ -119,24 +119,23 @@
   static std::unique_ptr<const ApkAssets> LoadImpl(base::unique_fd fd, const std::string& path,
                                                    std::unique_ptr<Asset> idmap_asset,
                                                    std::unique_ptr<const LoadedIdmap> loaded_idmap,
-                                                   bool system, bool load_as_shared_library,
-                                                   bool resource_loader = false);
+                                                   package_property_t property_flags);
 
   static std::unique_ptr<const ApkAssets> LoadArscImpl(base::unique_fd fd,
                                                        const std::string& path,
-                                                       bool resource_loader = false);
+                                                       package_property_t property_flags);
 
   ApkAssets(ZipArchiveHandle unmanaged_handle,
             const std::string& path,
             time_t last_mod_time,
-            bool for_loader = false);
+            package_property_t property_flags);
 
   using ZipArchivePtr = std::unique_ptr<ZipArchive, void (*)(ZipArchiveHandle)>;
 
   ZipArchivePtr zip_handle_;
   const std::string path_;
   time_t last_mod_time_;
-  bool for_loader_;
+  package_property_t property_flags_ = 0U;
   std::unique_ptr<Asset> resources_asset_;
   std::unique_ptr<Asset> idmap_asset_;
   std::unique_ptr<const LoadedArsc> loaded_arsc_;
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 20e4023..00cbbca 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -263,10 +263,13 @@
   // Creates a new Theme from this AssetManager.
   std::unique_ptr<Theme> NewTheme();
 
-  void ForEachPackage(const std::function<bool(const std::string&, uint8_t)> func) const {
+  void ForEachPackage(const std::function<bool(const std::string&, uint8_t)> func,
+                      package_property_t excluded_property_flags = 0U) const {
     for (const PackageGroup& package_group : package_groups_) {
-      if (!func(package_group.packages_.front().loaded_package_->GetPackageName(),
-           package_group.dynamic_ref_table->mAssignedPackageId)) {
+      const auto loaded_package = package_group.packages_.front().loaded_package_;
+      if ((loaded_package->GetPropertyFlags() & excluded_property_flags) == 0U
+          && !func(loaded_package->GetPackageName(),
+                   package_group.dynamic_ref_table->mAssignedPackageId)) {
         return;
       }
     }
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index ba1beaa..6cbda07 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -69,6 +69,14 @@
   }
 };
 
+using package_property_t = uint32_t;
+enum : package_property_t {
+  PROPERTY_DYNAMIC = 1,
+  PROPERTY_LOADER = 2,
+  PROPERTY_OVERLAY = 4,
+  PROPERTY_SYSTEM = 8,
+};
+
 // TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of
 // ResTable_type pointers.
 // TypeSpecPtr is a managed pointer that knows how to delete itself.
@@ -131,9 +139,8 @@
     return iterator(this, resource_ids_.size() + 1, 0);
   }
 
-  static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk, bool system,
-                                                   bool load_as_shared_library,
-                                                   bool load_as_custom_loader);
+  static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk,
+                                                   package_property_t property_flags);
 
   ~LoadedPackage();
 
@@ -170,17 +177,26 @@
 
   // Returns true if this package is dynamic (shared library) and needs to have an ID assigned.
   inline bool IsDynamic() const {
-    return dynamic_;
+    return (property_flags_ & PROPERTY_DYNAMIC) != 0;
+  }
+
+  // Returns true if this package is a Runtime Resource Overlay.
+  inline bool IsOverlay() const {
+    return (property_flags_ & PROPERTY_OVERLAY) != 0;
   }
 
   // Returns true if this package originates from a system provided resource.
   inline bool IsSystem() const {
-    return system_;
+    return (property_flags_ & PROPERTY_SYSTEM) != 0;
   }
 
-  // Returns true if this package is a custom loader and should behave like an overlay
+  // Returns true if this package is a custom loader and should behave like an overlay.
   inline bool IsCustomLoader() const {
-    return custom_loader_;
+    return (property_flags_ & PROPERTY_LOADER) != 0;
+  }
+
+  inline package_property_t GetPropertyFlags() const {
+    return property_flags_;
   }
 
   // Returns the map of package name to package ID used in this LoadedPackage. At runtime, a
@@ -248,12 +264,10 @@
   ResStringPool type_string_pool_;
   ResStringPool key_string_pool_;
   std::string package_name_;
+  bool defines_overlayable_ = false;
   int package_id_ = -1;
   int type_id_offset_ = 0;
-  bool dynamic_ = false;
-  bool system_ = false;
-  bool custom_loader_ = false;
-  bool defines_overlayable_ = false;
+  package_property_t property_flags_ = 0U;
 
   ByteBucketArray<TypeSpecPtr> type_specs_;
   ByteBucketArray<uint32_t> resource_ids_;
@@ -274,9 +288,7 @@
   // ID.
   static std::unique_ptr<const LoadedArsc> Load(const StringPiece& data,
                                                 const LoadedIdmap* loaded_idmap = nullptr,
-                                                bool system = false,
-                                                bool load_as_shared_library = false,
-                                                bool for_loader = false);
+                                                package_property_t property_flags = 0U);
 
   // Create an empty LoadedArsc. This is used when an APK has no resources.arsc.
   static std::unique_ptr<const LoadedArsc> CreateEmpty();
@@ -296,28 +308,15 @@
     return packages_;
   }
 
-  // Returns true if this is a system provided resource.
-  inline bool IsSystem() const {
-    return system_;
-  }
-
  private:
   DISALLOW_COPY_AND_ASSIGN(LoadedArsc);
 
   LoadedArsc() = default;
-  bool LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library,
-                 bool for_loader);
-
-  static std::unique_ptr<const LoadedArsc> LoadData(std::unique_ptr<LoadedArsc>& loaded_arsc,
-                                                    const char* data,
-                                                    size_t length,
-                                                    const LoadedIdmap* loaded_idmap = nullptr,
-                                                    bool load_as_shared_library = false,
-                                                    bool for_loader = false);
+  bool LoadTable(
+      const Chunk& chunk, const LoadedIdmap* loaded_idmap, package_property_t property_flags);
 
   std::unique_ptr<ResStringPool> global_string_pool_ = util::make_unique<ResStringPool>();
   std::vector<std::unique_ptr<const LoadedPackage>> packages_;
-  bool system_ = false;
 };
 
 }  // namespace android
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index 82dd335..8615069 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -144,8 +144,7 @@
                                       "resources.arsc", &contents));
 
   std::unique_ptr<const LoadedArsc> loaded_arsc =
-      LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, false /*system*/,
-                       true /*load_as_shared_library*/);
+      LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, PROPERTY_DYNAMIC);
   ASSERT_THAT(loaded_arsc, NotNull());
 
   const auto& packages = loaded_arsc->GetPackages();
@@ -227,9 +226,7 @@
   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk",
                                       "resources.arsc", &contents));
 
-  std::unique_ptr<const LoadedArsc> loaded_arsc =
-      LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, false /*system*/,
-                       false /*load_as_shared_library*/);
+  std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
 
   ASSERT_THAT(loaded_arsc, NotNull());
   const LoadedPackage* package = loaded_arsc->GetPackageById(
@@ -346,7 +343,7 @@
       asset->getLength());
 
   std::unique_ptr<const LoadedArsc> loaded_arsc =
-      LoadedArsc::Load(data, nullptr, false, false, true);
+      LoadedArsc::Load(data, nullptr, PROPERTY_LOADER);
   ASSERT_THAT(loaded_arsc, NotNull());
 
   const LoadedPackage* package =
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 39a7e25..5030e66 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -2642,10 +2642,47 @@
 
         @Override
         public void onRemoved() {
-            unregister();
-            synchronized (mListeners) {
-                mListeners.remove(mListener, this);
+            // TODO: onRemoved is necessary to GC hanging listeners, but introduces some interesting
+            //  broken edge cases. luckily these edge cases are quite unlikely. consider the
+            //  following interleaving for instance:
+            //    1) client adds single shot location request (A)
+            //    2) client gets removal callback, and schedules it for execution
+            //    3) client replaces single shot request with a different location request (B)
+            //    4) prior removal callback is executed, removing location request (B) incorrectly
+            //  what's needed is a way to identify which listener a callback belongs to. currently
+            //  we reuse the same transport object for the same listeners (so that we don't leak
+            //  transport objects on the server side). there seem to be two solutions:
+            //    1) when reregistering a request, first unregister the current transport, then
+            //       register with a new transport object (never reuse transport objects) - the
+            //       downside is that this breaks the server's knowledge that the request is the
+            //       same object, and thus breaks optimizations such as reusing the same transport
+            //       state.
+            //    2) pass some other type of marker in addition to the transport (for example an
+            //       incrementing integer representing the transport "version"), and pass this
+            //       marker back into callbacks so that each callback knows which transport
+            //       "version" it belongs to and can not execute itself if the version does not
+            //       match.
+            //  (1) seems like the preferred solution as it's simpler to implement and the above
+            //  mentioned server optimizations are not terribly important (they can be bypassed by
+            //  clients that use a new listener every time anyways).
+
+            Executor currentExecutor = mExecutor;
+            if (currentExecutor == null) {
+                // we've already been unregistered, no work to do anyways
+                return;
             }
+
+            // must be executed on the same executor so callback execution cannot be reordered
+            currentExecutor.execute(() -> {
+                if (currentExecutor != mExecutor) {
+                    return;
+                }
+
+                unregister();
+                synchronized (mListeners) {
+                    mListeners.remove(mListener, this);
+                }
+            });
         }
 
         private void locationCallbackFinished() {
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index d6a4ea7..da8e402 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -237,11 +237,56 @@
     public static final String TAG_NEW_SUBFILE_TYPE = "NewSubfileType";
     /** Type is String. */
     public static final String TAG_OECF = "OECF";
-    /** Type is String. {@hide} */
+    /**
+     *  <p>A tag used to record the offset from UTC (the time difference from Universal Time
+     *  Coordinated including daylight saving time) of the time of DateTime tag. The format when
+     *  recording the offset is "±HH:MM". The part of "±" shall be recorded as "+" or "-". When
+     *  the offsets are unknown, all the character spaces except colons (":") should be filled
+     *  with blank characters, or else the Interoperability field should be filled with blank
+     *  characters. The character string length is 7 Bytes including NULL for termination. When
+     *  the field is left blank, it is treated as unknown.</p>
+     *
+     *  <ul>
+     *      <li>Tag = 36880</li>
+     *      <li>Type = String</li>
+     *      <li>Length = 7</li>
+     *      <li>Default = None</li>
+     *  </ul>
+     */
     public static final String TAG_OFFSET_TIME = "OffsetTime";
-    /** Type is String. {@hide} */
+    /**
+     *  <p>A tag used to record the offset from UTC (the time difference from Universal Time
+     *  Coordinated including daylight saving time) of the time of DateTimeOriginal tag. The format
+     *  when recording the offset is "±HH:MM". The part of "±" shall be recorded as "+" or "-". When
+     *  the offsets are unknown, all the character spaces except colons (":") should be filled
+     *  with blank characters, or else the Interoperability field should be filled with blank
+     *  characters. The character string length is 7 Bytes including NULL for termination. When
+     *  the field is left blank, it is treated as unknown.</p>
+     *
+     *  <ul>
+     *      <li>Tag = 36881</li>
+     *      <li>Type = String</li>
+     *      <li>Length = 7</li>
+     *      <li>Default = None</li>
+     *  </ul>
+     */
     public static final String TAG_OFFSET_TIME_ORIGINAL = "OffsetTimeOriginal";
-    /** Type is String. {@hide} */
+    /**
+     *  <p>A tag used to record the offset from UTC (the time difference from Universal Time
+     *  Coordinated including daylight saving time) of the time of DateTimeDigitized tag. The format
+     *  when recording the offset is "±HH:MM". The part of "±" shall be recorded as "+" or "-". When
+     *  the offsets are unknown, all the character spaces except colons (":") should be filled
+     *  with blank characters, or else the Interoperability field should be filled with blank
+     *  characters. The character string length is 7 Bytes including NULL for termination. When
+     *  the field is left blank, it is treated as unknown.</p>
+     *
+     *  <ul>
+     *      <li>Tag = 36882</li>
+     *      <li>Type = String</li>
+     *      <li>Length = 7</li>
+     *      <li>Default = None</li>
+     *  </ul>
+     */
     public static final String TAG_OFFSET_TIME_DIGITIZED = "OffsetTimeDigitized";
     /** Type is int. */
     public static final String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index ce0e89d..c91325a 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -17,6 +17,7 @@
 package android.media.tv.tuner;
 
 import android.annotation.Nullable;
+import android.media.tv.tuner.TunerConstants.DemuxPidType;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -35,6 +36,7 @@
     private static final int MSG_ON_FRONTEND_EVENT = 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;
 
     static {
         System.loadLibrary("media_tv_tuner");
@@ -45,6 +47,9 @@
     private Frontend mFrontend;
     private EventHandler mHandler;
 
+    private List<Integer> mLnbIds;
+    private Lnb mLnb;
+
     public Tuner() {
         nativeSetup();
     }
@@ -76,6 +81,12 @@
 
     private native Filter nativeOpenFilter(int type, int subType, int bufferSize);
 
+    private native List<Integer> nativeGetLnbIds();
+    private native Lnb nativeOpenLnbById(int id);
+
+    private native Descrambler nativeOpenDescrambler();
+
+    private native Dvr nativeOpenDvr(int type, int bufferSize);
 
     /**
      * Frontend Callback.
@@ -89,6 +100,16 @@
     }
 
     /**
+     * LNB Callback.
+     */
+    public interface LnbCallback {
+        /**
+         * Invoked when there is a LNB event.
+         */
+        void onEvent(int lnbEventType);
+    }
+
+    /**
      * Frontend Callback.
      */
     public interface FilterCallback {
@@ -98,6 +119,20 @@
         void onFilterStatus(int status);
     }
 
+    /**
+     * DVR Callback.
+     */
+    public interface DvrCallback {
+        /**
+         * Invoked when record status changed.
+         */
+        void onRecordStatus(int status);
+        /**
+         * Invoked when playback status changed.
+         */
+        void onPlaybackStatus(int status);
+    }
+
     @Nullable
     private EventHandler createEventHandler() {
         Looper looper;
@@ -129,6 +164,11 @@
                     }
                     break;
                 }
+                case MSG_ON_LNB_EVENT: {
+                    if (mLnb != null && mLnb.mCallback != null) {
+                        mLnb.mCallback.onEvent(msg.arg1);
+                    }
+                }
                 default:
                     // fall through
             }
@@ -232,4 +272,103 @@
         }
         return filter;
     }
+
+    protected class Lnb {
+        private int mId;
+        private LnbCallback mCallback;
+
+        private Lnb(int id) {
+            mId = id;
+        }
+
+        public void setCallback(@Nullable LnbCallback callback) {
+            mCallback = callback;
+            if (mCallback == null) {
+                return;
+            }
+            if (mHandler == null) {
+                mHandler = createEventHandler();
+            }
+        }
+    }
+
+    private List<Integer> getLnbIds() {
+        mLnbIds = nativeGetLnbIds();
+        return mLnbIds;
+    }
+
+    private Lnb openLnbById(int id) {
+        if (mLnbIds == null) {
+            mLnbIds = getLnbIds();
+        }
+        if (!mLnbIds.contains(id)) {
+            return null;
+        }
+        mLnb = nativeOpenLnbById(id);
+        return mLnb;
+    }
+
+    private void onLnbEvent(int eventType) {
+        if (mHandler != null) {
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_LNB_EVENT, eventType, 0));
+        }
+    }
+
+    protected class Descrambler {
+        private long mNativeContext;
+
+        private native boolean nativeAddPid(int pidType, int pid, Filter filter);
+        private native boolean nativeRemovePid(int pidType, int pid, Filter filter);
+
+        private Descrambler() {}
+
+        private boolean addPid(@DemuxPidType int pidType, int pid, Filter filter) {
+            return nativeAddPid(pidType, pid, filter);
+        }
+
+        private boolean removePid(@DemuxPidType int pidType, int pid, Filter filter) {
+            return nativeRemovePid(pidType, pid, filter);
+        }
+
+    }
+
+    private Descrambler openDescrambler() {
+        Descrambler descrambler = nativeOpenDescrambler();
+        return descrambler;
+    }
+
+    // TODO: consider splitting Dvr to Playback and Recording
+    protected class Dvr {
+        private long mNativeContext;
+        private DvrCallback mCallback;
+
+        private native boolean nativeAttachFilter(Filter filter);
+        private native boolean nativeDetachFilter(Filter filter);
+        private native boolean nativeStartDvr();
+        private native boolean nativeStopDvr();
+        private native boolean nativeFlushDvr();
+
+        private Dvr() {}
+
+        public boolean attachFilter(Filter filter) {
+            return nativeAttachFilter(filter);
+        }
+        public boolean detachFilter(Filter filter) {
+            return nativeDetachFilter(filter);
+        }
+        public boolean start() {
+            return nativeStartDvr();
+        }
+        public boolean stop() {
+            return nativeStopDvr();
+        }
+        public boolean flush() {
+            return nativeFlushDvr();
+        }
+    }
+
+    private Dvr openDvr(int type, int bufferSize) {
+        Dvr dvr = nativeOpenDvr(type, bufferSize);
+        return dvr;
+    }
 }
diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java
index 411882e..01f9367d 100644
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ b/media/java/android/media/tv/tuner/TunerConstants.java
@@ -67,6 +67,14 @@
     public static final int DATA_FORMAT_ES = Constants.DataFormat.ES;
     public static final int DATA_FORMAT_SHV_TLV = Constants.DataFormat.SHV_TLV;
 
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({DEMUX_T_PID, DEMUX_MMPT_PID})
+    public @interface DemuxPidType {}
+
+    public static final int DEMUX_T_PID = 1;
+    public static final int DEMUX_MMPT_PID = 2;
+
     private TunerConstants() {
     }
 }
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index ad71d70..f5202fc 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -28,22 +28,64 @@
 using ::android::hardware::Void;
 using ::android::hardware::hidl_vec;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
+using ::android::hardware::tv::tuner::V1_0::DemuxMmtpPid;
+using ::android::hardware::tv::tuner::V1_0::DemuxTpid;
 using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
 using ::android::hardware::tv::tuner::V1_0::ITuner;
 using ::android::hardware::tv::tuner::V1_0::Result;
 
 struct fields_t {
-    jfieldID context;
+    jfieldID tunerContext;
     jfieldID filterContext;
+    jfieldID descramblerContext;
+    jfieldID dvrContext;
     jmethodID frontendInitID;
     jmethodID filterInitID;
+    jmethodID dvrInitID;
     jmethodID onFrontendEventID;
     jmethodID onFilterStatusID;
+    jmethodID lnbInitID;
+    jmethodID onLnbEventID;
+    jmethodID descramblerInitID;
 };
 
 static fields_t gFields;
 
 namespace android {
+/////////////// LnbCallback ///////////////////////
+LnbCallback::LnbCallback(jweak tunerObj, LnbId id) : mObject(tunerObj), mId(id) {}
+
+Return<void> LnbCallback::onEvent(LnbEventType lnbEventType) {
+    ALOGD("LnbCallback::onEvent, type=%d", lnbEventType);
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    env->CallVoidMethod(
+            mObject,
+            gFields.onLnbEventID,
+            (jint)lnbEventType);
+    return Void();
+}
+Return<void> LnbCallback::onDiseqcMessage(const hidl_vec<uint8_t>& /*diseqcMessage*/) {
+    ALOGD("LnbCallback::onDiseqcMessage");
+    return Void();
+}
+
+/////////////// DvrCallback ///////////////////////
+Return<void> DvrCallback::onRecordStatus(RecordStatus /*status*/) {
+    ALOGD("DvrCallback::onRecordStatus");
+    return Void();
+}
+
+Return<void> DvrCallback::onPlaybackStatus(PlaybackStatus /*status*/) {
+    ALOGD("DvrCallback::onPlaybackStatus");
+    return Void();
+}
+
+void DvrCallback::setDvr(const jobject dvr) {
+    ALOGD("FilterCallback::setDvr");
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    mDvr = env->NewWeakGlobalRef(dvr);
+}
+
 /////////////// FilterCallback ///////////////////////
 //TODO: implement filter callback
 Return<void> FilterCallback::onFilterEvent(const DemuxFilterEvent& /*filterEvent*/) {
@@ -175,6 +217,52 @@
             (jint) jId);
 }
 
+jobject JTuner::getLnbIds() {
+    ALOGD("JTuner::getLnbIds()");
+    mTuner->getLnbIds([&](Result, const hidl_vec<FrontendId>& lnbIds) {
+        mLnbIds = lnbIds;
+    });
+    if (mLnbIds.size() == 0) {
+        ALOGW("Lnb isn't available");
+        return NULL;
+    }
+
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    jclass arrayListClazz = env->FindClass("java/util/ArrayList");
+    jmethodID arrayListAdd = env->GetMethodID(arrayListClazz, "add", "(Ljava/lang/Object;)Z");
+    jobject obj = env->NewObject(arrayListClazz, env->GetMethodID(arrayListClazz, "<init>", "()V"));
+
+    jclass integerClazz = env->FindClass("java/lang/Integer");
+    jmethodID intInit = env->GetMethodID(integerClazz, "<init>", "(I)V");
+
+    for (int i=0; i < mLnbIds.size(); i++) {
+       jobject idObj = env->NewObject(integerClazz, intInit, mLnbIds[i]);
+       env->CallBooleanMethod(obj, arrayListAdd, idObj);
+    }
+    return obj;
+}
+
+jobject JTuner::openLnbById(int id) {
+    sp<ILnb> lnbSp;
+    mTuner->openLnbById(id, [&](Result, const sp<ILnb>& lnb) {
+        lnbSp = lnb;
+    });
+    if (lnbSp == nullptr) {
+        ALOGE("Failed to open lnb");
+        return NULL;
+    }
+    mLnb = lnbSp;
+    sp<LnbCallback> lnbCb = new LnbCallback(mObject, id);
+    mLnb->setCallback(lnbCb);
+
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    return env->NewObject(
+            env->FindClass("android/media/tv/tuner/Tuner$Lnb"),
+            gFields.lnbInitID,
+            mObject,
+            id);
+}
+
 bool JTuner::openDemux() {
     if (mTuner == nullptr) {
         return false;
@@ -193,6 +281,33 @@
     return true;
 }
 
+jobject JTuner::openDescrambler() {
+    ALOGD("JTuner::openDescrambler");
+    if (mTuner == nullptr) {
+        return NULL;
+    }
+    sp<IDescrambler> descramblerSp;
+    mTuner->openDescrambler([&](Result, const sp<IDescrambler>& descrambler) {
+        descramblerSp = descrambler;
+    });
+
+    if (descramblerSp == NULL) {
+        return NULL;
+    }
+
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    jobject descramblerObj =
+            env->NewObject(
+                    env->FindClass("android/media/tv/tuner/Tuner$Descrambler"),
+                    gFields.descramblerInitID,
+                    mObject);
+
+    descramblerSp->incStrong(descramblerObj);
+    env->SetLongField(descramblerObj, gFields.descramblerContext, (jlong)descramblerSp.get());
+
+    return descramblerObj;
+}
+
 jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) {
     if (mDemux == NULL) {
         if (!openDemux()) {
@@ -231,6 +346,39 @@
     return filterObj;
 }
 
+jobject JTuner::openDvr(DvrType type, int bufferSize) {
+    ALOGD("JTuner::openDvr");
+    if (mDemux == NULL) {
+        if (!openDemux()) {
+            return NULL;
+        }
+    }
+    sp<IDvr> dvrSp;
+    sp<DvrCallback> callback = new DvrCallback();
+    mDemux->openDvr(type, bufferSize, callback,
+            [&](Result, const sp<IDvr>& dvr) {
+                dvrSp = dvr;
+            });
+
+    if (dvrSp == NULL) {
+        return NULL;
+    }
+
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    jobject dvrObj =
+            env->NewObject(
+                    env->FindClass("android/media/tv/tuner/Tuner$Dvr"),
+                    gFields.dvrInitID,
+                    mObject);
+
+    dvrSp->incStrong(dvrObj);
+    env->SetLongField(dvrObj, gFields.dvrContext, (jlong)dvrSp.get());
+
+    callback->setDvr(dvrObj);
+
+    return dvrObj;
+}
+
 }  // namespace android
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -238,7 +386,7 @@
 using namespace android;
 
 static sp<JTuner> setTuner(JNIEnv *env, jobject thiz, const sp<JTuner> &tuner) {
-    sp<JTuner> old = (JTuner *)env->GetLongField(thiz, gFields.context);
+    sp<JTuner> old = (JTuner *)env->GetLongField(thiz, gFields.tunerContext);
 
     if (tuner != NULL) {
         tuner->incStrong(thiz);
@@ -246,38 +394,71 @@
     if (old != NULL) {
         old->decStrong(thiz);
     }
-    env->SetLongField(thiz, gFields.context, (jlong)tuner.get());
+    env->SetLongField(thiz, gFields.tunerContext, (jlong)tuner.get());
 
     return old;
 }
 
 static sp<JTuner> getTuner(JNIEnv *env, jobject thiz) {
-    return (JTuner *)env->GetLongField(thiz, gFields.context);
+    return (JTuner *)env->GetLongField(thiz, gFields.tunerContext);
+}
+
+static sp<IDescrambler> getDescrambler(JNIEnv *env, jobject descrambler) {
+    return (IDescrambler *)env->GetLongField(descrambler, gFields.descramblerContext);
+}
+
+static DemuxPid getDemuxPid(int pidType, int pid) {
+    DemuxPid demuxPid;
+    if ((int)pidType == 1) {
+        demuxPid.tPid(static_cast<DemuxTpid>(pid));
+    } else if ((int)pidType == 2) {
+        demuxPid.mmtpPid(static_cast<DemuxMmtpPid>(pid));
+    }
+    return demuxPid;
 }
 
 static sp<IFilter> getFilter(JNIEnv *env, jobject filter) {
     return (IFilter *)env->GetLongField(filter, gFields.filterContext);
 }
 
+static sp<IDvr> getDvr(JNIEnv *env, jobject dvr) {
+    return (IDvr *)env->GetLongField(dvr, gFields.dvrContext);
+}
+
 static void android_media_tv_Tuner_native_init(JNIEnv *env) {
     jclass clazz = env->FindClass("android/media/tv/tuner/Tuner");
     CHECK(clazz != NULL);
 
-    gFields.context = env->GetFieldID(clazz, "mNativeContext", "J");
-    CHECK(gFields.context != NULL);
+    gFields.tunerContext = env->GetFieldID(clazz, "mNativeContext", "J");
+    CHECK(gFields.tunerContext != NULL);
 
     gFields.onFrontendEventID = env->GetMethodID(clazz, "onFrontendEvent", "(I)V");
 
+    gFields.onLnbEventID = env->GetMethodID(clazz, "onLnbEvent", "(I)V");
+
     jclass frontendClazz = env->FindClass("android/media/tv/tuner/Tuner$Frontend");
     gFields.frontendInitID =
             env->GetMethodID(frontendClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;I)V");
 
+    jclass lnbClazz = env->FindClass("android/media/tv/tuner/Tuner$Lnb");
+    gFields.lnbInitID =
+            env->GetMethodID(lnbClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;I)V");
+
     jclass filterClazz = env->FindClass("android/media/tv/tuner/Tuner$Filter");
     gFields.filterContext = env->GetFieldID(filterClazz, "mNativeContext", "J");
     gFields.filterInitID =
             env->GetMethodID(filterClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;I)V");
     gFields.onFilterStatusID =
             env->GetMethodID(filterClazz, "onFilterStatus", "(I)V");
+
+    jclass descramblerClazz = env->FindClass("android/media/tv/tuner/Tuner$Descrambler");
+    gFields.descramblerContext = env->GetFieldID(descramblerClazz, "mNativeContext", "J");
+    gFields.descramblerInitID =
+            env->GetMethodID(descramblerClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;)V");
+
+    jclass dvrClazz = env->FindClass("android/media/tv/tuner/Tuner$Dvr");
+    gFields.dvrContext = env->GetFieldID(dvrClazz, "mNativeContext", "J");
+    gFields.dvrInitID = env->GetMethodID(dvrClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;)V");
 }
 
 static void android_media_tv_Tuner_native_setup(JNIEnv *env, jobject thiz) {
@@ -295,6 +476,16 @@
     return tuner->openFrontendById(id);
 }
 
+static jobject android_media_tv_Tuner_get_lnb_ids(JNIEnv *env, jobject thiz) {
+    sp<JTuner> tuner = getTuner(env, thiz);
+    return tuner->getLnbIds();
+}
+
+static jobject android_media_tv_Tuner_open_lnb_by_id(JNIEnv *env, jobject thiz, jint id) {
+    sp<JTuner> tuner = getTuner(env, thiz);
+    return tuner->openLnbById(id);
+}
+
 static jobject android_media_tv_Tuner_open_filter(
         JNIEnv *env, jobject thiz, jint type, jint subType, jint bufferSize) {
     sp<JTuner> tuner = getTuner(env, thiz);
@@ -335,6 +526,85 @@
     return filterSp->flush() == Result::SUCCESS;
 }
 
+static jobject android_media_tv_Tuner_open_descrambler(JNIEnv *env, jobject thiz) {
+    sp<JTuner> tuner = getTuner(env, thiz);
+    return tuner->openDescrambler();
+}
+
+static bool android_media_tv_Tuner_add_pid(
+        JNIEnv *env, jobject descrambler, jint pidType, jint pid, jobject filter) {
+    sp<IDescrambler> descramblerSp = getDescrambler(env, descrambler);
+    if (descramblerSp == NULL) {
+        return false;
+    }
+    sp<IFilter> filterSp = getFilter(env, filter);
+    Result result = descramblerSp->addPid(getDemuxPid((int)pidType, (int)pid), filterSp);
+    return result == Result::SUCCESS;
+}
+
+static bool android_media_tv_Tuner_remove_pid(
+        JNIEnv *env, jobject descrambler, jint pidType, jint pid, jobject filter) {
+    sp<IDescrambler> descramblerSp = getDescrambler(env, descrambler);
+    if (descramblerSp == NULL) {
+        return false;
+    }
+    sp<IFilter> filterSp = getFilter(env, filter);
+    Result result = descramblerSp->removePid(getDemuxPid((int)pidType, (int)pid), filterSp);
+    return result == Result::SUCCESS;
+}
+
+static jobject android_media_tv_Tuner_open_dvr(JNIEnv *env, jobject thiz, jint type, jint bufferSize) {
+    sp<JTuner> tuner = getTuner(env, thiz);
+    return tuner->openDvr(static_cast<DvrType>(type), bufferSize);
+}
+
+static bool android_media_tv_Tuner_attach_filter(JNIEnv *env, jobject dvr, jobject filter) {
+    sp<IDvr> dvrSp = getDvr(env, dvr);
+    sp<IFilter> filterSp = getFilter(env, filter);
+    if (dvrSp == NULL || filterSp == NULL) {
+        return false;
+    }
+    Result result = dvrSp->attachFilter(filterSp);
+    return result == Result::SUCCESS;
+}
+
+static bool android_media_tv_Tuner_detach_filter(JNIEnv *env, jobject dvr, jobject filter) {
+    sp<IDvr> dvrSp = getDvr(env, dvr);
+    sp<IFilter> filterSp = getFilter(env, filter);
+    if (dvrSp == NULL || filterSp == NULL) {
+        return false;
+    }
+    Result result = dvrSp->detachFilter(filterSp);
+    return result == Result::SUCCESS;
+}
+
+static bool android_media_tv_Tuner_start_dvr(JNIEnv *env, jobject dvr) {
+    sp<IDvr> dvrSp = getDvr(env, dvr);
+    if (dvrSp == NULL) {
+        ALOGD("Failed to start dvr: dvr not found");
+        return false;
+    }
+    return dvrSp->start() == Result::SUCCESS;
+}
+
+static bool android_media_tv_Tuner_stop_dvr(JNIEnv *env, jobject dvr) {
+    sp<IDvr> dvrSp = getDvr(env, dvr);
+    if (dvrSp == NULL) {
+        ALOGD("Failed to stop dvr: dvr not found");
+        return false;
+    }
+    return dvrSp->stop() == Result::SUCCESS;
+}
+
+static bool android_media_tv_Tuner_flush_dvr(JNIEnv *env, jobject dvr) {
+    sp<IDvr> dvrSp = getDvr(env, dvr);
+    if (dvrSp == NULL) {
+        ALOGD("Failed to flush dvr: dvr not found");
+        return false;
+    }
+    return dvrSp->flush() == Result::SUCCESS;
+}
+
 static const JNINativeMethod gTunerMethods[] = {
     { "nativeInit", "()V", (void *)android_media_tv_Tuner_native_init },
     { "nativeSetup", "()V", (void *)android_media_tv_Tuner_native_setup },
@@ -344,6 +614,14 @@
             (void *)android_media_tv_Tuner_open_frontend_by_id },
     { "nativeOpenFilter", "(III)Landroid/media/tv/tuner/Tuner$Filter;",
             (void *)android_media_tv_Tuner_open_filter },
+    { "nativeGetLnbIds", "()Ljava/util/List;",
+            (void *)android_media_tv_Tuner_get_lnb_ids },
+    { "nativeOpenLnbById", "(I)Landroid/media/tv/tuner/Tuner$Lnb;",
+            (void *)android_media_tv_Tuner_open_lnb_by_id },
+    { "nativeOpenDescrambler", "()Landroid/media/tv/tuner/Tuner$Descrambler;",
+            (void *)android_media_tv_Tuner_open_descrambler },
+    { "nativeOpenDvr", "(II)Landroid/media/tv/tuner/Tuner$Dvr;",
+            (void *)android_media_tv_Tuner_open_dvr },
 };
 
 static const JNINativeMethod gFilterMethods[] = {
@@ -352,6 +630,23 @@
     { "nativeFlushFilter", "()Z", (void *)android_media_tv_Tuner_flush_filter },
 };
 
+static const JNINativeMethod gDescramblerMethods[] = {
+    { "nativeAddPid", "(IILandroid/media/tv/tuner/Tuner$Filter;)Z",
+            (void *)android_media_tv_Tuner_add_pid },
+    { "nativeRemovePid", "(IILandroid/media/tv/tuner/Tuner$Filter;)Z",
+            (void *)android_media_tv_Tuner_remove_pid },
+};
+
+static const JNINativeMethod gDvrMethods[] = {
+    { "nativeAttachFilter", "(Landroid/media/tv/tuner/Tuner$Filter;)Z",
+            (void *)android_media_tv_Tuner_attach_filter },
+    { "nativeDetachFilter", "(Landroid/media/tv/tuner/Tuner$Filter;)Z",
+            (void *)android_media_tv_Tuner_detach_filter },
+    { "nativeStartDvr", "()Z", (void *)android_media_tv_Tuner_start_dvr },
+    { "nativeStopDvr", "()Z", (void *)android_media_tv_Tuner_stop_dvr },
+    { "nativeFlushDvr", "()Z", (void *)android_media_tv_Tuner_flush_dvr },
+};
+
 static bool register_android_media_tv_Tuner(JNIEnv *env) {
     if (AndroidRuntime::registerNativeMethods(
             env, "android/media/tv/tuner/Tuner", gTunerMethods, NELEM(gTunerMethods)) != JNI_OK) {
@@ -365,6 +660,20 @@
         ALOGE("Failed to register filter native methods");
         return false;
     }
+    if (AndroidRuntime::registerNativeMethods(
+            env, "android/media/tv/tuner/Tuner$Descrambler",
+            gDescramblerMethods,
+            NELEM(gDescramblerMethods)) != JNI_OK) {
+        ALOGE("Failed to register descrambler native methods");
+        return false;
+    }
+    if (AndroidRuntime::registerNativeMethods(
+            env, "android/media/tv/tuner/Tuner$Dvr",
+            gDvrMethods,
+            NELEM(gDvrMethods)) != JNI_OK) {
+        ALOGE("Failed to register dvr native methods");
+        return false;
+    }
     return true;
 }
 
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index ab48761..3bf6ec8 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -28,19 +28,47 @@
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterStatus;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
+using ::android::hardware::tv::tuner::V1_0::DemuxPid;
+using ::android::hardware::tv::tuner::V1_0::DvrType;
 using ::android::hardware::tv::tuner::V1_0::FrontendEventType;
 using ::android::hardware::tv::tuner::V1_0::FrontendId;
 using ::android::hardware::tv::tuner::V1_0::FrontendScanMessage;
 using ::android::hardware::tv::tuner::V1_0::FrontendScanMessageType;
 using ::android::hardware::tv::tuner::V1_0::IDemux;
+using ::android::hardware::tv::tuner::V1_0::IDescrambler;
+using ::android::hardware::tv::tuner::V1_0::IDvr;
+using ::android::hardware::tv::tuner::V1_0::IDvrCallback;
 using ::android::hardware::tv::tuner::V1_0::IFilter;
 using ::android::hardware::tv::tuner::V1_0::IFilterCallback;
 using ::android::hardware::tv::tuner::V1_0::IFrontend;
 using ::android::hardware::tv::tuner::V1_0::IFrontendCallback;
+using ::android::hardware::tv::tuner::V1_0::ILnb;
+using ::android::hardware::tv::tuner::V1_0::ILnbCallback;
 using ::android::hardware::tv::tuner::V1_0::ITuner;
+using ::android::hardware::tv::tuner::V1_0::LnbEventType;
+using ::android::hardware::tv::tuner::V1_0::LnbId;
+using ::android::hardware::tv::tuner::V1_0::PlaybackStatus;
+using ::android::hardware::tv::tuner::V1_0::RecordStatus;
 
 namespace android {
 
+struct LnbCallback : public ILnbCallback {
+    LnbCallback(jweak tunerObj, LnbId id);
+    virtual Return<void> onEvent(LnbEventType lnbEventType);
+    virtual Return<void> onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage);
+    jweak mObject;
+    LnbId mId;
+};
+
+struct DvrCallback : public IDvrCallback {
+    virtual Return<void> onRecordStatus(RecordStatus status);
+    virtual Return<void> onPlaybackStatus(PlaybackStatus status);
+
+    void setDvr(const jobject dvr);
+private:
+    jweak mDvr;
+};
+
 struct FilterCallback : public IFilterCallback {
     virtual Return<void> onFilterEvent(const DemuxFilterEvent& filterEvent);
     virtual Return<void> onFilterStatus(const DemuxFilterStatus status);
@@ -67,7 +95,12 @@
     sp<ITuner> getTunerService();
     jobject getFrontendIds();
     jobject openFrontendById(int id);
+    jobject getLnbIds();
+    jobject openLnbById(int id);
     jobject openFilter(DemuxFilterType type, int bufferSize);
+    jobject openDescrambler();
+    jobject openDvr(DvrType type, int bufferSize);
+
 protected:
     bool openDemux();
     virtual ~JTuner();
@@ -78,6 +111,8 @@
     static sp<ITuner> mTuner;
     hidl_vec<FrontendId> mFeIds;
     sp<IFrontend> mFe;
+    hidl_vec<LnbId> mLnbIds;
+    sp<ILnb> mLnb;
     sp<IDemux> mDemux;
     int mDemuxId;
 };
diff --git a/media/lib/tvremote/java/com/android/media/tv/remoteprovider/TvRemoteProvider.java b/media/lib/tvremote/java/com/android/media/tv/remoteprovider/TvRemoteProvider.java
index 35322ad..0bf0f97 100644
--- a/media/lib/tvremote/java/com/android/media/tv/remoteprovider/TvRemoteProvider.java
+++ b/media/lib/tvremote/java/com/android/media/tv/remoteprovider/TvRemoteProvider.java
@@ -19,18 +19,18 @@
 import android.content.Context;
 import android.media.tv.ITvRemoteProvider;
 import android.media.tv.ITvRemoteServiceInput;
-import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
 import android.os.RemoteException;
 import android.util.Log;
 
+import java.util.LinkedList;
+
 /**
  * Base class for emote providers implemented in unbundled service.
  * <p/>
  * This object is not thread safe.  It is only intended to be accessed on the
  * {@link Context#getMainLooper main looper thread} of an application.
+ * The callback {@link #onInputBridgeConnected()} may be called from a different thread.
  * </p><p>
  * IMPORTANT: This class is effectively a system API for unbundled emote service, and
  * must remain API stable. See README.txt in the root of this package for more information.
@@ -50,11 +50,9 @@
 
     private static final String TAG = "TvRemoteProvider";
     private static final boolean DEBUG_KEYS = false;
-    private static final int MSG_SET_SERVICE_INPUT = 1;
-    private static final int MSG_SEND_INPUTBRIDGE_CONNECTED = 2;
     private final Context mContext;
     private final ProviderStub mStub;
-    private final ProviderHandler mHandler;
+    private final LinkedList<Runnable> mOpenBridgeRunnables;
     private ITvRemoteServiceInput mRemoteServiceInput;
 
     /**
@@ -67,7 +65,7 @@
     public TvRemoteProvider(Context context) {
         mContext = context.getApplicationContext();
         mStub = new ProviderStub();
-        mHandler = new ProviderHandler(mContext.getMainLooper());
+        mOpenBridgeRunnables = new LinkedList<Runnable>();
     }
 
     /**
@@ -77,7 +75,6 @@
         return mContext;
     }
 
-
     /**
      * Gets the Binder associated with the provider.
      * <p>
@@ -105,7 +102,11 @@
      * @param tvServiceInput sink defined in framework service
      */
     private void setRemoteServiceInputSink(ITvRemoteServiceInput tvServiceInput) {
-        mRemoteServiceInput = tvServiceInput;
+        synchronized (mOpenBridgeRunnables) {
+            mRemoteServiceInput = tvServiceInput;
+        }
+        mOpenBridgeRunnables.forEach(Runnable::run);
+        mOpenBridgeRunnables.clear();
     }
 
     /**
@@ -125,8 +126,25 @@
      */
     public void openRemoteInputBridge(IBinder token, String name, int width, int height,
                                       int maxPointers) throws RuntimeException {
+        synchronized (mOpenBridgeRunnables) {
+            if (mRemoteServiceInput == null) {
+                Log.d(TAG, "Delaying openRemoteInputBridge() for " + name);
+
+                mOpenBridgeRunnables.add(() -> {
+                    try {
+                        mRemoteServiceInput.openInputBridge(
+                                token, name, width, height, maxPointers);
+                        Log.d(TAG, "Delayed openRemoteInputBridge() for " + name + ": success");
+                    } catch (RemoteException re) {
+                        Log.e(TAG, "Delayed openRemoteInputBridge() for " + name + ": failure", re);
+                    }
+                });
+                return;
+            }
+        }
         try {
             mRemoteServiceInput.openInputBridge(token, name, width, height, maxPointers);
+            Log.d(TAG, "openRemoteInputBridge() for " + name + ": success");
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -271,33 +289,12 @@
     private final class ProviderStub extends ITvRemoteProvider.Stub {
         @Override
         public void setRemoteServiceInputSink(ITvRemoteServiceInput tvServiceInput) {
-            mHandler.obtainMessage(MSG_SET_SERVICE_INPUT, tvServiceInput).sendToTarget();
+            TvRemoteProvider.this.setRemoteServiceInputSink(tvServiceInput);
         }
 
         @Override
         public void onInputBridgeConnected(IBinder token) {
-            mHandler.obtainMessage(MSG_SEND_INPUTBRIDGE_CONNECTED, 0, 0,
-                    (IBinder) token).sendToTarget();
-        }
-    }
-
-    private final class ProviderHandler extends Handler {
-        public ProviderHandler(Looper looper) {
-            super(looper, null, true);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_SET_SERVICE_INPUT: {
-                    setRemoteServiceInputSink((ITvRemoteServiceInput) msg.obj);
-                    break;
-                }
-                case MSG_SEND_INPUTBRIDGE_CONNECTED: {
-                    onInputBridgeConnected((IBinder) msg.obj);
-                    break;
-                }
-            }
+            TvRemoteProvider.this.onInputBridgeConnected(token);
         }
     }
 }
diff --git a/media/lib/tvremote/tests/Android.bp b/media/lib/tvremote/tests/Android.bp
new file mode 100644
index 0000000..f00eed0
--- /dev/null
+++ b/media/lib/tvremote/tests/Android.bp
@@ -0,0 +1,15 @@
+android_test {
+    name: "TvRemoteTests",
+    srcs: ["src/**/*.java"],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+        "com.android.media.tv.remoteprovider",
+    ],
+    static_libs: [
+        "mockito-target-minus-junit4",
+    ],
+    platform_apis: true,
+    certificate: "platform",
+    privileged: true,
+}
diff --git a/media/lib/tvremote/tests/AndroidManifest.xml b/media/lib/tvremote/tests/AndroidManifest.xml
new file mode 100644
index 0000000..4f843f7
--- /dev/null
+++ b/media/lib/tvremote/tests/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.media.tv.remoteprovider">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+             android:targetPackage="com.android.media.tv.remoteprovider"
+             android:label="Tests for TV Remote"/>
+</manifest>
diff --git a/media/lib/tvremote/tests/src/com/android/media/tv/remoteprovider/TvRemoteProviderTest.java b/media/lib/tvremote/tests/src/com/android/media/tv/remoteprovider/TvRemoteProviderTest.java
new file mode 100644
index 0000000..c9ce5613
--- /dev/null
+++ b/media/lib/tvremote/tests/src/com/android/media/tv/remoteprovider/TvRemoteProviderTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media.tv.remoteprovider;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.content.Context;
+import android.media.tv.ITvRemoteProvider;
+import android.media.tv.ITvRemoteServiceInput;
+import android.os.Binder;
+import android.os.IBinder;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.util.ArrayList;
+
+public class TvRemoteProviderTest extends AndroidTestCase {
+    private static final String TAG = TvRemoteProviderTest.class.getSimpleName();
+
+    @SmallTest
+    public void testOpenRemoteInputBridge() throws Exception {
+        Binder tokenA = new Binder();
+        Binder tokenB = new Binder();
+        Binder tokenC = new Binder();
+
+        class LocalTvRemoteProvider extends TvRemoteProvider {
+            private final ArrayList<IBinder> mTokens = new ArrayList<IBinder>();
+
+            LocalTvRemoteProvider(Context context) {
+                super(context);
+            }
+
+            @Override
+            public void onInputBridgeConnected(IBinder token) {
+                mTokens.add(token);
+            }
+
+            public boolean verifyTokens() {
+                return mTokens.size() == 3
+                    && mTokens.contains(tokenA)
+                    && mTokens.contains(tokenB)
+                    && mTokens.contains(tokenC);
+            }
+        }
+
+        LocalTvRemoteProvider tvProvider = new LocalTvRemoteProvider(getContext());
+        ITvRemoteProvider binder = (ITvRemoteProvider) tvProvider.getBinder();
+
+        ITvRemoteServiceInput tvServiceInput = mock(ITvRemoteServiceInput.class);
+        doAnswer((i) -> {
+            binder.onInputBridgeConnected(i.getArgument(0));
+            return null;
+        }).when(tvServiceInput).openInputBridge(any(), any(), anyInt(), anyInt(), anyInt());
+
+        tvProvider.openRemoteInputBridge(tokenA, "A", 1, 1, 1);
+        tvProvider.openRemoteInputBridge(tokenB, "B", 1, 1, 1);
+        binder.setRemoteServiceInputSink(tvServiceInput);
+        tvProvider.openRemoteInputBridge(tokenC, "C", 1, 1, 1);
+
+        verify(tvServiceInput).openInputBridge(tokenA, "A", 1, 1, 1);
+        verify(tvServiceInput).openInputBridge(tokenB, "B", 1, 1, 1);
+        verify(tvServiceInput).openInputBridge(tokenC, "C", 1, 1, 1);
+        verifyNoMoreInteractions(tvServiceInput);
+
+        assertTrue(tvProvider.verifyTokens());
+    }
+}
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 91297b0..9d93c9b 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -36,7 +36,6 @@
 
     srcs: [
         "asset_manager.cpp",
-        "choreographer.cpp",
         "configuration.cpp",
         "hardware_buffer_jni.cpp",
         "input.cpp",
@@ -80,7 +79,7 @@
         "libarect",
     ],
 
-    whole_static_libs: ["libnativewindow"],
+    whole_static_libs: ["libnativedisplay", "libnativewindow"],
 
     export_static_lib_headers: ["libarect"],
 
@@ -142,4 +141,4 @@
         "aidl/com/android/internal/compat/IPlatformCompatNative.aidl",
     ],
     path: "aidl",
-}
\ No newline at end of file
+}
diff --git a/native/android/choreographer.cpp b/native/android/choreographer.cpp
deleted file mode 100644
index 63e0734..0000000
--- a/native/android/choreographer.cpp
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "Choreographer"
-//#define LOG_NDEBUG 0
-
-#include <cinttypes>
-#include <queue>
-#include <thread>
-
-#include <android/choreographer.h>
-#include <androidfw/DisplayEventDispatcher.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/SurfaceComposerClient.h>
-#include <utils/Looper.h>
-#include <utils/Mutex.h>
-#include <utils/Timers.h>
-
-namespace android {
-
-static inline const char* toString(bool value) {
-    return value ? "true" : "false";
-}
-
-struct FrameCallback {
-    AChoreographer_frameCallback callback;
-    AChoreographer_frameCallback64 callback64;
-    void* data;
-    nsecs_t dueTime;
-
-    inline bool operator<(const FrameCallback& rhs) const {
-        // Note that this is intentionally flipped because we want callbacks due sooner to be at
-        // the head of the queue
-        return dueTime > rhs.dueTime;
-    }
-};
-
-
-class Choreographer : public DisplayEventDispatcher, public MessageHandler {
-public:
-    void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
-                                  AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay);
-
-    enum {
-        MSG_SCHEDULE_CALLBACKS = 0,
-        MSG_SCHEDULE_VSYNC = 1
-    };
-    virtual void handleMessage(const Message& message) override;
-
-    static Choreographer* getForThread();
-
-protected:
-    virtual ~Choreographer() = default;
-
-private:
-    explicit Choreographer(const sp<Looper>& looper);
-    Choreographer(const Choreographer&) = delete;
-
-    void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override;
-    void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
-    void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
-                               int32_t configId) override;
-
-    void scheduleCallbacks();
-
-    // Protected by mLock
-    std::priority_queue<FrameCallback> mCallbacks;
-
-    mutable Mutex mLock;
-
-    const sp<Looper> mLooper;
-    const std::thread::id mThreadId;
-};
-
-
-static thread_local Choreographer* gChoreographer;
-Choreographer* Choreographer::getForThread() {
-    if (gChoreographer == nullptr) {
-        sp<Looper> looper = Looper::getForThread();
-        if (!looper.get()) {
-            ALOGW("No looper prepared for thread");
-            return nullptr;
-        }
-        gChoreographer = new Choreographer(looper);
-        status_t result = gChoreographer->initialize();
-        if (result != OK) {
-            ALOGW("Failed to initialize");
-            return nullptr;
-        }
-    }
-    return gChoreographer;
-}
-
-Choreographer::Choreographer(const sp<Looper>& looper) :
-    DisplayEventDispatcher(looper), mLooper(looper), mThreadId(std::this_thread::get_id()) {
-}
-
-void Choreographer::postFrameCallbackDelayed(
-        AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay) {
-    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-    FrameCallback callback{cb, cb64, data, now + delay};
-    {
-        AutoMutex _l{mLock};
-        mCallbacks.push(callback);
-    }
-    if (callback.dueTime <= now) {
-        if (std::this_thread::get_id() != mThreadId) {
-            Message m{MSG_SCHEDULE_VSYNC};
-            mLooper->sendMessage(this, m);
-        } else {
-            scheduleVsync();
-        }
-    } else {
-        Message m{MSG_SCHEDULE_CALLBACKS};
-        mLooper->sendMessageDelayed(delay, this, m);
-    }
-}
-
-void Choreographer::scheduleCallbacks() {
-    AutoMutex _{mLock};
-    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-    if (mCallbacks.top().dueTime <= now) {
-        ALOGV("choreographer %p ~ scheduling vsync", this);
-        scheduleVsync();
-        return;
-    }
-}
-
-// TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the
-// internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for
-// the internal display implicitly.
-void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t) {
-    std::vector<FrameCallback> callbacks{};
-    {
-        AutoMutex _l{mLock};
-        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-        while (!mCallbacks.empty() && mCallbacks.top().dueTime < now) {
-            callbacks.push_back(mCallbacks.top());
-            mCallbacks.pop();
-        }
-    }
-    for (const auto& cb : callbacks) {
-        if (cb.callback64 != nullptr) {
-            cb.callback64(timestamp, cb.data);
-        } else if (cb.callback != nullptr) {
-            cb.callback(timestamp, cb.data);
-        }
-    }
-}
-
-void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) {
-    ALOGV("choreographer %p ~ received hotplug event (displayId=%"
-            ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", connected=%s), ignoring.",
-            this, displayId, toString(connected));
-}
-
-void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId,
-                                          int32_t configId) {
-    ALOGV("choreographer %p ~ received config changed event (displayId=%"
-            ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", configId=%s), ignoring.",
-            this, displayId, toString(configId));
-}
-
-void Choreographer::handleMessage(const Message& message) {
-    switch (message.what) {
-    case MSG_SCHEDULE_CALLBACKS:
-        scheduleCallbacks();
-        break;
-    case MSG_SCHEDULE_VSYNC:
-        scheduleVsync();
-        break;
-    }
-}
-
-}
-
-/* Glue for the NDK interface */
-
-using android::Choreographer;
-
-static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* choreographer) {
-    return reinterpret_cast<Choreographer*>(choreographer);
-}
-
-static inline AChoreographer* Choreographer_to_AChoreographer(Choreographer* choreographer) {
-    return reinterpret_cast<AChoreographer*>(choreographer);
-}
-
-AChoreographer* AChoreographer_getInstance() {
-    return Choreographer_to_AChoreographer(Choreographer::getForThread());
-}
-
-void AChoreographer_postFrameCallback(AChoreographer* choreographer,
-        AChoreographer_frameCallback callback, void* data) {
-    AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
-            callback, nullptr, data, 0);
-}
-void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
-        AChoreographer_frameCallback callback, void* data, long delayMillis) {
-    AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
-            callback, nullptr, data, ms2ns(delayMillis));
-}
-void AChoreographer_postFrameCallback64(AChoreographer* choreographer,
-        AChoreographer_frameCallback64 callback, void* data) {
-    AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
-            nullptr, callback, data, 0);
-}
-void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
-        AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) {
-    AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
-            nullptr, callback, data, ms2ns(delayMillis));
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 91d0026..818fdea 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -38,7 +38,6 @@
 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.notification.collection.NotificationData;
 import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -101,7 +100,7 @@
     abstract DockManager bindDockManager(DockManagerImpl dockManager);
 
     @Binds
-    abstract NotificationData.KeyguardEnvironment bindKeyguardEnvironment(
+    abstract NotificationEntryManager.KeyguardEnvironment bindKeyguardEnvironment(
             KeyguardEnvironmentImpl keyguardEnvironment);
 
     @Binds
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
index 53a88a9..ed945e7 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -19,8 +19,9 @@
 import android.service.notification.StatusBarNotification;
 
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
 import com.android.systemui.statusbar.notification.logging.NotifLog;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -35,8 +36,12 @@
 public class CarNotificationEntryManager extends NotificationEntryManager {
 
     @Inject
-    public CarNotificationEntryManager(NotificationData notificationData, NotifLog notifLog) {
-        super(notificationData, notifLog);
+    public CarNotificationEntryManager(
+            NotifLog notifLog,
+            NotificationGroupManager groupManager,
+            NotificationRankingManager rankingManager,
+            KeyguardEnvironment keyguardEnvironment) {
+        super(notifLog, groupManager, rankingManager, keyguardEnvironment);
     }
 
     @Override
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 7765935..f5d1ccf 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -154,7 +154,6 @@
     private static final String TABLE_SYSTEM = "system";
     private static final String TABLE_SECURE = "secure";
     private static final String TABLE_GLOBAL = "global";
-    private static final String TABLE_CONFIG = "config";
 
     // Old tables no longer exist.
     private static final String TABLE_FAVORITES = "favorites";
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index b9fe933..91b22d1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -46,6 +46,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SystemUIFactory;
@@ -617,6 +618,15 @@
                 StatsLog.write(StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
                     StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS);
                 mLockPatternUtils.reportSuccessfulPasswordAttempt(userId);
+                // Force a garbage collection in an attempt to erase any lockscreen password left in
+                // memory. Do it asynchronously with a 5-sec delay to avoid making the keyguard
+                // dismiss animation janky.
+                ThreadUtils.postOnBackgroundThread(() -> {
+                    try {
+                        Thread.sleep(5000);
+                    } catch (InterruptedException ignored) { }
+                    Runtime.getRuntime().gc();
+                });
             } else {
                 StatsLog.write(StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
                     StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE);
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index dca5c8a..1a0690c 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -75,10 +75,10 @@
 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.collection.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ChannelEditorDialogController;
 import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
index 385de4a..15a5c27 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
@@ -139,8 +139,7 @@
         // Update appOp if there's an associated pending or visible notification:
         final String foregroundKey = getStandardLayoutKey(userId, packageName);
         if (foregroundKey != null) {
-            final NotificationEntry entry = mEntryManager.getPendingOrCurrentNotif(
-                    foregroundKey);
+            final NotificationEntry entry = mEntryManager.getPendingOrActiveNotif(foregroundKey);
             if (entry != null
                     && uid == entry.getSbn().getUid()
                     && packageName.equals(entry.getSbn().getPackageName())) {
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
index 4516996..170c25a 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
@@ -71,7 +71,7 @@
     private final Runnable mHideHandles = this::hideHandles;
     private final Runnable mShowAndGo = this::showAndGoInternal;
     private final Provider<AssistHandleViewController> mAssistHandleViewController;
-    private final PhenotypeHelper mPhenotypeHelper;
+    private final DeviceConfigHelper mDeviceConfigHelper;
     private final Map<AssistHandleBehavior, BehaviorController> mBehaviorMap;
 
     private boolean mHandlesShowing = false;
@@ -90,7 +90,7 @@
             AssistUtils assistUtils,
             @Named(ASSIST_HANDLE_THREAD_NAME) Handler handler,
             Provider<AssistHandleViewController> assistHandleViewController,
-            PhenotypeHelper phenotypeHelper,
+            DeviceConfigHelper deviceConfigHelper,
             Map<AssistHandleBehavior, BehaviorController> behaviorMap,
             NavigationModeController navigationModeController,
             DumpController dumpController) {
@@ -98,14 +98,14 @@
         mAssistUtils = assistUtils;
         mHandler = handler;
         mAssistHandleViewController = assistHandleViewController;
-        mPhenotypeHelper = phenotypeHelper;
+        mDeviceConfigHelper = deviceConfigHelper;
         mBehaviorMap = behaviorMap;
 
         mInGesturalMode = QuickStepContract.isGesturalMode(
                 navigationModeController.addListener(this::handleNavigationModeChange));
 
         setBehavior(getBehaviorMode());
-        mPhenotypeHelper.addOnPropertiesChangedListener(
+        mDeviceConfigHelper.addOnPropertiesChangedListener(
                 mHandler::post,
                 (properties) -> {
                     if (properties.getKeyset().contains(
@@ -205,19 +205,19 @@
     }
 
     private long getShownFrequencyThreshold() {
-        return mPhenotypeHelper.getLong(
+        return mDeviceConfigHelper.getLong(
                 SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS,
                 DEFAULT_SHOWN_FREQUENCY_THRESHOLD_MS);
     }
 
     private long getShowAndGoDuration() {
-        return mPhenotypeHelper.getLong(
+        return mDeviceConfigHelper.getLong(
                 SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS,
                 DEFAULT_SHOW_AND_GO_DURATION_MS);
     }
 
     private String getBehaviorMode() {
-        return mPhenotypeHelper.getString(
+        return mDeviceConfigHelper.getString(
                 SystemUiDeviceConfigFlags.ASSIST_HANDLES_BEHAVIOR_MODE,
                 DEFAULT_BEHAVIOR.toString());
     }
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
index 46ae84a..9793d72 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
@@ -155,7 +155,7 @@
 
     private final Clock mClock;
     private final Handler mHandler;
-    private final PhenotypeHelper mPhenotypeHelper;
+    private final DeviceConfigHelper mDeviceConfigHelper;
     private final Lazy<StatusBarStateController> mStatusBarStateController;
     private final Lazy<ActivityManagerWrapper> mActivityManagerWrapper;
     private final Lazy<OverviewProxyService> mOverviewProxyService;
@@ -189,7 +189,7 @@
     AssistHandleReminderExpBehavior(
             @Named(UPTIME_NAME) Clock clock,
             @Named(ASSIST_HANDLE_THREAD_NAME) Handler handler,
-            PhenotypeHelper phenotypeHelper,
+            DeviceConfigHelper deviceConfigHelper,
             Lazy<StatusBarStateController> statusBarStateController,
             Lazy<ActivityManagerWrapper> activityManagerWrapper,
             Lazy<OverviewProxyService> overviewProxyService,
@@ -199,7 +199,7 @@
             Lazy<BroadcastDispatcher> broadcastDispatcher) {
         mClock = clock;
         mHandler = handler;
-        mPhenotypeHelper = phenotypeHelper;
+        mDeviceConfigHelper = deviceConfigHelper;
         mStatusBarStateController = statusBarStateController;
         mActivityManagerWrapper = activityManagerWrapper;
         mOverviewProxyService = overviewProxyService;
@@ -465,55 +465,55 @@
     }
 
     private long getLearningTimeMs() {
-        return mPhenotypeHelper.getLong(
+        return mDeviceConfigHelper.getLong(
                 SystemUiDeviceConfigFlags.ASSIST_HANDLES_LEARN_TIME_MS,
                 DEFAULT_LEARNING_TIME_MS);
     }
 
     private int getLearningCount() {
-        return mPhenotypeHelper.getInt(
+        return mDeviceConfigHelper.getInt(
                 SystemUiDeviceConfigFlags.ASSIST_HANDLES_LEARN_COUNT,
                 DEFAULT_LEARNING_COUNT);
     }
 
     private long getShowAndGoDelayedShortDelayMs() {
-        return mPhenotypeHelper.getLong(
+        return mDeviceConfigHelper.getLong(
                 SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DELAYED_SHORT_DELAY_MS,
                 DEFAULT_SHOW_AND_GO_DELAYED_SHORT_DELAY_MS);
     }
 
     private long getShowAndGoDelayedLongDelayMs() {
-        return mPhenotypeHelper.getLong(
+        return mDeviceConfigHelper.getLong(
                 SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DELAYED_LONG_DELAY_MS,
                 DEFAULT_SHOW_AND_GO_DELAYED_LONG_DELAY_MS);
     }
 
     private long getShowAndGoDelayResetTimeoutMs() {
-        return mPhenotypeHelper.getLong(
+        return mDeviceConfigHelper.getLong(
                 SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DELAY_RESET_TIMEOUT_MS,
                 DEFAULT_SHOW_AND_GO_DELAY_RESET_TIMEOUT_MS);
     }
 
     private boolean getSuppressOnLockscreen() {
-        return mPhenotypeHelper.getBoolean(
+        return mDeviceConfigHelper.getBoolean(
                 SystemUiDeviceConfigFlags.ASSIST_HANDLES_SUPPRESS_ON_LOCKSCREEN,
                 DEFAULT_SUPPRESS_ON_LOCKSCREEN);
     }
 
     private boolean getSuppressOnLauncher() {
-        return mPhenotypeHelper.getBoolean(
+        return mDeviceConfigHelper.getBoolean(
                 SystemUiDeviceConfigFlags.ASSIST_HANDLES_SUPPRESS_ON_LAUNCHER,
                 DEFAULT_SUPPRESS_ON_LAUNCHER);
     }
 
     private boolean getSuppressOnApps() {
-        return mPhenotypeHelper.getBoolean(
+        return mDeviceConfigHelper.getBoolean(
                 SystemUiDeviceConfigFlags.ASSIST_HANDLES_SUPPRESS_ON_APPS,
                 DEFAULT_SUPPRESS_ON_APPS);
     }
 
     private boolean getShowWhenTaught() {
-        return mPhenotypeHelper.getBoolean(
+        return mDeviceConfigHelper.getBoolean(
                 SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_WHEN_TAUGHT,
                 DEFAULT_SHOW_WHEN_TAUGHT);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/assist/PhenotypeHelper.java b/packages/SystemUI/src/com/android/systemui/assist/DeviceConfigHelper.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/assist/PhenotypeHelper.java
rename to packages/SystemUI/src/com/android/systemui/assist/DeviceConfigHelper.java
index 05a01dd..86b7c74 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/PhenotypeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/DeviceConfigHelper.java
@@ -28,15 +28,15 @@
 import javax.inject.Singleton;
 
 /**
- * Wrapper class for retrieving phenotype flag values.
+ * Wrapper class for retrieving System UI device configuration values.
  *
  * Can be mocked in tests for ease of testing the effects of particular values.
  */
 @Singleton
-public class PhenotypeHelper {
+public class DeviceConfigHelper {
 
     @Inject
-    public PhenotypeHelper() {}
+    public DeviceConfigHelper() {}
 
     public long getLong(String name, long defaultValue) {
         return whitelistIpcs(() ->
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 9f7bdd4..db1185f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -47,6 +47,7 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.RemoteException;
@@ -78,10 +79,10 @@
 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.NotificationData;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ZenModeController;
@@ -352,14 +353,13 @@
      * @param userId the id of the user
      */
     private void restoreBubbles(@UserIdInt int userId) {
-        NotificationData notificationData =
-                mNotificationEntryManager.getNotificationData();
         ArraySet<String> savedBubbleKeys = mSavedBubbleKeysPerUser.get(userId);
         if (savedBubbleKeys == null) {
             // There were no bubbles saved for this used.
             return;
         }
-        for (NotificationEntry e : notificationData.getNotificationsForCurrentUser()) {
+        for (NotificationEntry e :
+                mNotificationEntryManager.getActiveNotificationsForCurrentUser()) {
             if (savedBubbleKeys.contains(e.getKey())
                     && mNotificationInterruptionStateProvider.shouldBubbleUp(e)
                     && canLaunchInActivityView(mContext, e)) {
@@ -458,7 +458,7 @@
     public boolean isBubbleNotificationSuppressedFromShade(String key) {
         boolean isBubbleAndSuppressed = mBubbleData.hasBubbleWithKey(key)
                 && !mBubbleData.getBubbleWithKey(key).showInShadeWhenBubble();
-        NotificationEntry entry = mNotificationEntryManager.getNotificationData().get(key);
+        NotificationEntry entry = mNotificationEntryManager.getActiveNotificationUnfiltered(key);
         String groupKey = entry != null ? entry.getSbn().getGroupKey() : null;
         boolean isSuppressedSummary = mBubbleData.isSummarySuppressed(groupKey);
         boolean isSummary = key.equals(mBubbleData.getSummaryKey(groupKey));
@@ -571,7 +571,8 @@
             new NotificationRemoveInterceptor() {
             @Override
             public boolean onNotificationRemoveRequested(String key, int reason) {
-                NotificationEntry entry = mNotificationEntryManager.getNotificationData().get(key);
+                NotificationEntry entry =
+                        mNotificationEntryManager.getActiveNotificationUnfiltered(key);
                 String groupKey = entry != null ? entry.getSbn().getGroupKey() : null;
                 ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey);
 
@@ -768,7 +769,7 @@
                         String notifKey = mBubbleData.getSummaryKey(groupKey);
                         mBubbleData.removeSuppressedSummary(groupKey);
                         NotificationEntry entry =
-                                mNotificationEntryManager.getNotificationData().get(notifKey);
+                                mNotificationEntryManager.getActiveNotificationUnfiltered(notifKey);
                         mNotificationEntryManager.performRemoveNotification(
                                 entry.getSbn(), UNDEFINED_DISMISS_REASON);
                     }
@@ -1007,8 +1008,10 @@
             Log.w(TAG, "Unable to create bubble -- no intent: " + entry.getKey());
             return false;
         }
+        PackageManager packageManager = StatusBar.getPackageManagerForUser(
+                context, entry.getSbn().getUser().getIdentifier());
         ActivityInfo info =
-                intent.getIntent().resolveActivityInfo(context.getPackageManager(), 0);
+                intent.getIntent().resolveActivityInfo(packageManager, 0);
         if (info == null) {
             Log.w(TAG, "Unable to send as bubble, "
                     + entry.getKey() + " couldn't find activity info for intent: "
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
index 6744d74..7007f9d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
@@ -35,6 +35,7 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
 import com.android.systemui.statusbar.phone.DozeServiceHost;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.ManagedProfileController;
 import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -50,6 +51,7 @@
 import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
 import com.android.systemui.statusbar.policy.FlashlightController;
 import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.HotspotController;
 import com.android.systemui.statusbar.policy.HotspotControllerImpl;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -257,4 +259,7 @@
     @Binds
     public abstract VolumeComponent provideVolumeComponent(
             VolumeDialogComponent volumeDialogComponent);
+    /** */
+    @Binds
+    public abstract HeadsUpManager bindHeadsUpManager(HeadsUpManagerPhone headsUpManagerPhone);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 48c72d3..f1d02bb 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -33,7 +33,7 @@
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -74,7 +74,7 @@
     abstract DockManager bindDockManager(DockManagerImpl dockManager);
 
     @Binds
-    abstract NotificationData.KeyguardEnvironment bindKeyguardEnvironment(
+    abstract NotificationEntryManager.KeyguardEnvironment bindKeyguardEnvironment(
             KeyguardEnvironmentImpl keyguardEnvironment);
 
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 5de6d1c..0134aa3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -169,7 +169,7 @@
         if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)) {
             v.setText(mContext.getString(
                     com.android.internal.R.string.bugreport_status,
-                    Build.VERSION.RELEASE_OR_CODENAME,
+                    Build.VERSION.RELEASE,
                     Build.ID));
             v.setVisibility(View.VISIBLE);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteImageInBackgroundTask.java
new file mode 100644
index 0000000..8c48655
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteImageInBackgroundTask.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.net.Uri;
+import android.os.AsyncTask;
+
+/**
+ * An AsyncTask that deletes an image from the media store in the background.
+ */
+class DeleteImageInBackgroundTask extends AsyncTask<Uri, Void, Void> {
+    private Context mContext;
+
+    DeleteImageInBackgroundTask(Context context) {
+        mContext = context;
+    }
+
+    @Override
+    protected Void doInBackground(Uri... params) {
+        if (params.length != 1) return null;
+
+        Uri screenshotUri = params[0];
+        ContentResolver resolver = mContext.getContentResolver();
+        resolver.delete(screenshotUri, null, null);
+        return null;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 3ff6d0d..7963203 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -32,49 +32,31 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
-import android.app.ActivityTaskManager;
 import android.app.Notification;
-import android.app.Notification.BigPictureStyle;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
-import android.content.ClipData;
-import android.content.ClipDescription;
 import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
-import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Picture;
 import android.graphics.PixelFormat;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.media.MediaActionSound;
 import android.net.Uri;
 import android.os.AsyncTask;
-import android.os.Environment;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
-import android.os.Process;
-import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.provider.DeviceConfig;
-import android.provider.MediaStore;
-import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Slog;
@@ -90,25 +72,15 @@
 import android.widget.Toast;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.systemui.R;
 import com.android.systemui.SystemUI;
-import com.android.systemui.SystemUIFactory;
 import com.android.systemui.dagger.qualifiers.MainResources;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.util.NotificationChannels;
 
-import libcore.io.IoUtils;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
 import java.util.Collections;
-import java.util.Date;
 import java.util.List;
 import java.util.Optional;
 import java.util.concurrent.CompletableFuture;
@@ -131,7 +103,7 @@
     /**
      * POD used in the AsyncTask which saves an image in the background.
      */
-    private static class SaveImageInBackgroundData {
+    static class SaveImageInBackgroundData {
         public Context context;
         public Bitmap image;
         public Uri imageUri;
@@ -147,396 +119,12 @@
             imageUri = null;
             iconSize = 0;
         }
+
         void clearContext() {
             context = null;
         }
     }
 
-    /**
-     * An AsyncTask that saves an image to the media store in the background.
-     */
-    private static class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
-        private static final String TAG = "SaveImageInBackgroundTask";
-
-        private static final String SCREENSHOT_FILE_NAME_TEMPLATE = "Screenshot_%s.png";
-        private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)";
-
-        private final SaveImageInBackgroundData mParams;
-        private final NotificationManager mNotificationManager;
-        private final Notification.Builder mNotificationBuilder, mPublicNotificationBuilder;
-        private final String mImageFileName;
-        private final long mImageTime;
-        private final BigPictureStyle mNotificationStyle;
-        private final int mImageWidth;
-        private final int mImageHeight;
-        private final Handler mHandler;
-        private final ScreenshotNotificationSmartActionsProvider mSmartActionsProvider;
-
-        SaveImageInBackgroundTask(Context context, SaveImageInBackgroundData data,
-                NotificationManager nManager) {
-            Resources r = context.getResources();
-
-            // Prepare all the output metadata
-            mParams = data;
-            mImageTime = System.currentTimeMillis();
-            String imageDate = new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date(mImageTime));
-            mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, imageDate);
-
-            // Initialize screenshot notification smart actions provider.
-            mHandler = new Handler();
-            mSmartActionsProvider =
-                SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider();
-
-            // Create the large notification icon
-            mImageWidth = data.image.getWidth();
-            mImageHeight = data.image.getHeight();
-            int iconSize = data.iconSize;
-            int previewWidth = data.previewWidth;
-            int previewHeight = data.previewheight;
-
-            Paint paint = new Paint();
-            ColorMatrix desat = new ColorMatrix();
-            desat.setSaturation(0.25f);
-            paint.setColorFilter(new ColorMatrixColorFilter(desat));
-            Matrix matrix = new Matrix();
-            int overlayColor = 0x40FFFFFF;
-
-            matrix.setTranslate((previewWidth - mImageWidth) / 2,
-                    (previewHeight - mImageHeight) / 2);
-            Bitmap picture = generateAdjustedHwBitmap(data.image, previewWidth, previewHeight,
-                    matrix, paint, overlayColor);
-
-            // Note, we can't use the preview for the small icon, since it is non-square
-            float scale = (float) iconSize / Math.min(mImageWidth, mImageHeight);
-            matrix.setScale(scale, scale);
-            matrix.postTranslate((iconSize - (scale * mImageWidth)) / 2,
-                    (iconSize - (scale * mImageHeight)) / 2);
-            Bitmap icon = generateAdjustedHwBitmap(data.image, iconSize, iconSize, matrix, paint,
-                    overlayColor);
-
-            mNotificationManager = nManager;
-            final long now = System.currentTimeMillis();
-
-            // Setup the notification
-            mNotificationStyle = new Notification.BigPictureStyle()
-                    .bigPicture(picture.createAshmemBitmap());
-
-            // The public notification will show similar info but with the actual screenshot omitted
-            mPublicNotificationBuilder =
-                    new Notification.Builder(context, NotificationChannels.SCREENSHOTS_HEADSUP)
-                            .setContentTitle(r.getString(R.string.screenshot_saving_title))
-                            .setSmallIcon(R.drawable.stat_notify_image)
-                            .setCategory(Notification.CATEGORY_PROGRESS)
-                            .setWhen(now)
-                            .setShowWhen(true)
-                            .setColor(r.getColor(
-                                    com.android.internal.R.color.system_notification_accent_color));
-            SystemUI.overrideNotificationAppName(context, mPublicNotificationBuilder, true);
-
-            mNotificationBuilder = new Notification.Builder(context,
-                    NotificationChannels.SCREENSHOTS_HEADSUP)
-                    .setContentTitle(r.getString(R.string.screenshot_saving_title))
-                    .setSmallIcon(R.drawable.stat_notify_image)
-                    .setWhen(now)
-                    .setShowWhen(true)
-                    .setColor(r.getColor(
-                            com.android.internal.R.color.system_notification_accent_color))
-                    .setStyle(mNotificationStyle)
-                    .setPublicVersion(mPublicNotificationBuilder.build());
-            mNotificationBuilder.setFlag(Notification.FLAG_NO_CLEAR, true);
-            SystemUI.overrideNotificationAppName(context, mNotificationBuilder, true);
-
-            mNotificationManager.notify(SystemMessage.NOTE_GLOBAL_SCREENSHOT,
-                    mNotificationBuilder.build());
-
-            /**
-             * NOTE: The following code prepares the notification builder for updating the
-             * notification after the screenshot has been written to disk.
-             */
-
-            // On the tablet, the large icon makes the notification appear as if it is clickable
-            // (and on small devices, the large icon is not shown) so defer showing the large icon
-            // until we compose the final post-save notification below.
-            mNotificationBuilder.setLargeIcon(icon.createAshmemBitmap());
-            // But we still don't set it for the expanded view, allowing the smallIcon to show here.
-            mNotificationStyle.bigLargeIcon((Bitmap) null);
-        }
-
-        private int getUserHandleOfForegroundApplication(Context context) {
-            // This logic matches
-            // com.android.systemui.statusbar.phone.PhoneStatusBarPolicy#updateManagedProfile
-            try {
-                return ActivityTaskManager.getService().getLastResumedActivityUserId();
-            } catch (RemoteException e) {
-                Slog.w(TAG, "getUserHandleOfForegroundApplication: ", e);
-                return context.getUserId();
-            }
-        }
-
-        private boolean isManagedProfile(Context context) {
-            UserManager manager = UserManager.get(context);
-            UserInfo info = manager.getUserInfo(getUserHandleOfForegroundApplication(context));
-            return info.isManagedProfile();
-        }
-
-        /**
-         * Generates a new hardware bitmap with specified values, copying the content from the
-         * passed in bitmap.
-         */
-        private Bitmap generateAdjustedHwBitmap(Bitmap bitmap, int width, int height, Matrix matrix,
-                Paint paint, int color) {
-            Picture picture = new Picture();
-            Canvas canvas = picture.beginRecording(width, height);
-            canvas.drawColor(color);
-            canvas.drawBitmap(bitmap, matrix, paint);
-            picture.endRecording();
-            return Bitmap.createBitmap(picture);
-        }
-
-        @Override
-        protected Void doInBackground(Void... paramsUnused) {
-            if (isCancelled()) {
-                return null;
-            }
-
-            // By default, AsyncTask sets the worker thread to have background thread priority,
-            // so bump it back up so that we save a little quicker.
-            Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
-
-            Context context = mParams.context;
-            Bitmap image = mParams.image;
-            boolean smartActionsEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
-                    SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS, true);
-            CompletableFuture<List<Notification.Action>> smartActionsFuture = getSmartActionsFuture(
-                    context, image, mSmartActionsProvider, mHandler, smartActionsEnabled,
-                    isManagedProfile(context));
-
-            Resources r = context.getResources();
-
-            try {
-                // Save the screenshot to the MediaStore
-                final MediaStore.PendingParams params = new MediaStore.PendingParams(
-                        MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mImageFileName, "image/png");
-                params.setRelativePath(Environment.DIRECTORY_PICTURES + File.separator
-                        + Environment.DIRECTORY_SCREENSHOTS);
-
-                final Uri uri = MediaStore.createPending(context, params);
-                final MediaStore.PendingSession session = MediaStore.openPending(context, uri);
-                try {
-                    try (OutputStream out = session.openOutputStream()) {
-                        if (!image.compress(Bitmap.CompressFormat.PNG, 100, out)) {
-                            throw new IOException("Failed to compress");
-                        }
-                    }
-                    session.publish();
-                } catch (Exception e) {
-                    session.abandon();
-                    throw e;
-                } finally {
-                    IoUtils.closeQuietly(session);
-                }
-
-                // Note: Both the share and edit actions are proxied through ActionProxyReceiver in
-                // order to do some common work like dismissing the keyguard and sending
-                // closeSystemWindows
-
-                // Create a share intent, this will always go through the chooser activity first
-                // which should not trigger auto-enter PiP
-                String subjectDate = DateFormat.getDateTimeInstance().format(new Date(mImageTime));
-                String subject = String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate);
-                Intent sharingIntent = new Intent(Intent.ACTION_SEND);
-                sharingIntent.setType("image/png");
-                sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
-                // Include URI in ClipData also, so that grantPermission picks it up.
-                // We don't use setData here because some apps interpret this as "to:".
-                ClipData clipdata = new ClipData(new ClipDescription("content",
-                        new String[]{ClipDescription.MIMETYPE_TEXT_PLAIN}),
-                        new ClipData.Item(uri));
-                sharingIntent.setClipData(clipdata);
-                sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
-                sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
-                // Make sure pending intents for the system user are still unique across users
-                // by setting the (otherwise unused) request code to the current user id.
-                int requestCode = context.getUserId();
-
-                PendingIntent chooserAction = PendingIntent.getBroadcast(context, requestCode,
-                        new Intent(context, GlobalScreenshot.TargetChosenReceiver.class),
-                        PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
-                Intent sharingChooserIntent = Intent.createChooser(sharingIntent, null,
-                        chooserAction.getIntentSender())
-                        .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
-                        .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
-                // Create a share action for the notification
-                PendingIntent shareAction = PendingIntent.getBroadcastAsUser(context, requestCode,
-                        new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
-                                .putExtra(EXTRA_ACTION_INTENT, sharingChooserIntent)
-                                .putExtra(EXTRA_DISALLOW_ENTER_PIP, true)
-                                .setAction(Intent.ACTION_SEND),
-                        PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
-                Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder(
-                        R.drawable.ic_screenshot_share,
-                        r.getString(com.android.internal.R.string.share), shareAction);
-                mNotificationBuilder.addAction(shareActionBuilder.build());
-
-                // Create an edit intent, if a specific package is provided as the editor, then
-                // launch that directly
-                String editorPackage = context.getString(R.string.config_screenshotEditor);
-                Intent editIntent = new Intent(Intent.ACTION_EDIT);
-                if (!TextUtils.isEmpty(editorPackage)) {
-                    editIntent.setComponent(ComponentName.unflattenFromString(editorPackage));
-                }
-                editIntent.setType("image/png");
-                editIntent.setData(uri);
-                editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-                editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
-                // Create a edit action
-                PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, requestCode,
-                        new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
-                                .putExtra(EXTRA_ACTION_INTENT, editIntent)
-                                .putExtra(EXTRA_CANCEL_NOTIFICATION,
-                                        editIntent.getComponent() != null)
-                                .setAction(Intent.ACTION_EDIT),
-                        PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
-                Notification.Action.Builder editActionBuilder = new Notification.Action.Builder(
-                        R.drawable.ic_screenshot_edit,
-                        r.getString(com.android.internal.R.string.screenshot_edit), editAction);
-                mNotificationBuilder.addAction(editActionBuilder.build());
-                if (editAction != null && mParams.onEditReady != null) {
-                    mParams.onEditReady.apply(editAction);
-                }
-
-                // Create a delete action for the notification
-                PendingIntent deleteAction = PendingIntent.getBroadcast(context, requestCode,
-                        new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class)
-                                .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString()),
-                        PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
-                Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder(
-                        R.drawable.ic_screenshot_delete,
-                        r.getString(com.android.internal.R.string.delete), deleteAction);
-                mNotificationBuilder.addAction(deleteActionBuilder.build());
-
-                mParams.imageUri = uri;
-                mParams.image = null;
-                mParams.errorMsgResId = 0;
-
-                if (smartActionsEnabled) {
-                    int timeoutMs = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
-                            SystemUiDeviceConfigFlags
-                                    .SCREENSHOT_NOTIFICATION_SMART_ACTIONS_TIMEOUT_MS,
-                            1000);
-                    List<Notification.Action> smartActions = getSmartActions(smartActionsFuture,
-                            timeoutMs);
-                    for (Notification.Action action : smartActions) {
-                        mNotificationBuilder.addAction(action);
-                    }
-                }
-            } catch (Exception e) {
-                // IOException/UnsupportedOperationException may be thrown if external storage is
-                // not mounted
-                Slog.e(TAG, "unable to save screenshot", e);
-                mParams.clearImage();
-                mParams.errorMsgResId = R.string.screenshot_failed_to_save_text;
-            }
-
-            // Recycle the bitmap data
-            if (image != null) {
-                image.recycle();
-            }
-
-            return null;
-        }
-
-        @Override
-        protected void onPostExecute(Void params) {
-            if (mParams.errorMsgResId != 0) {
-                // Show a message that we've failed to save the image to disk
-                GlobalScreenshot.notifyScreenshotError(mParams.context, mNotificationManager,
-                        mParams.errorMsgResId);
-            } else {
-                if (mParams.onEditReady != null) {
-                    // Cancel the "saving screenshot" notification
-                    mNotificationManager.cancel(SystemMessage.NOTE_GLOBAL_SCREENSHOT);
-                } else {
-                    // Show the final notification to indicate screenshot saved
-                    Context context = mParams.context;
-                    Resources r = context.getResources();
-
-                    // Create the intent to show the screenshot in gallery
-                    Intent launchIntent = new Intent(Intent.ACTION_VIEW);
-                    launchIntent.setDataAndType(mParams.imageUri, "image/png");
-                    launchIntent.setFlags(
-                            Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
-                    final long now = System.currentTimeMillis();
-
-                    // Update the text and the icon for the existing notification
-                    mPublicNotificationBuilder
-                            .setContentTitle(r.getString(R.string.screenshot_saved_title))
-                            .setContentText(r.getString(R.string.screenshot_saved_text))
-                            .setContentIntent(
-                                    PendingIntent.getActivity(mParams.context, 0, launchIntent, 0))
-                            .setWhen(now)
-                            .setAutoCancel(true)
-                            .setColor(context.getColor(
-                                    com.android.internal.R.color.system_notification_accent_color));
-                    mNotificationBuilder
-                            .setContentTitle(r.getString(R.string.screenshot_saved_title))
-                            .setContentText(r.getString(R.string.screenshot_saved_text))
-                            .setContentIntent(PendingIntent.getActivity(mParams.context, 0,
-                                    launchIntent, 0))
-                            .setWhen(now)
-                            .setAutoCancel(true)
-                            .setColor(context.getColor(
-                                    com.android.internal.R.color.system_notification_accent_color))
-                            .setPublicVersion(mPublicNotificationBuilder.build())
-                            .setFlag(Notification.FLAG_NO_CLEAR, false);
-
-                    mNotificationManager.notify(SystemMessage.NOTE_GLOBAL_SCREENSHOT,
-                            mNotificationBuilder.build());
-                }
-            }
-            mParams.finisher.run();
-            mParams.clearContext();
-        }
-
-        @Override
-        protected void onCancelled(Void params) {
-            // If we are cancelled while the task is running in the background, we may get null
-            // params. The finisher is expected to always be called back, so just use the baked-in
-            // params from the ctor in any case.
-            mParams.finisher.run();
-            mParams.clearImage();
-            mParams.clearContext();
-
-            // Cancel the posted notification
-            mNotificationManager.cancel(SystemMessage.NOTE_GLOBAL_SCREENSHOT);
-        }
-    }
-
-    /**
-     * An AsyncTask that deletes an image from the media store in the background.
-     */
-    private static class DeleteImageInBackgroundTask extends AsyncTask<Uri, Void, Void> {
-        private Context mContext;
-
-        DeleteImageInBackgroundTask(Context context) {
-            mContext = context;
-        }
-
-        @Override
-        protected Void doInBackground(Uri... params) {
-            if (params.length != 1) return null;
-
-            Uri screenshotUri = params[0];
-            ContentResolver resolver = mContext.getContentResolver();
-            resolver.delete(screenshotUri, null, null);
-            return null;
-        }
-    }
-
     static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id";
     static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent";
     static final String EXTRA_CANCEL_NOTIFICATION = "android:screenshot_cancel_notification";
@@ -600,7 +188,6 @@
         }
     };
 
-
     /**
      * @param context everything needs a context :(
      */
@@ -631,25 +218,25 @@
                 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
                 WindowManager.LayoutParams.TYPE_SCREENSHOT,
                 WindowManager.LayoutParams.FLAG_FULLSCREEN
-                    | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                    | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
+                        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                        | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
                 PixelFormat.TRANSLUCENT);
         mWindowLayoutParams.setTitle("ScreenshotAnimation");
         mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
         mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         mNotificationManager =
-            (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
+                (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
         mDisplay = mWindowManager.getDefaultDisplay();
         mDisplayMetrics = new DisplayMetrics();
         mDisplay.getRealMetrics(mDisplayMetrics);
 
         // Get the various target sizes
         mNotificationIconSize =
-            resources.getDimensionPixelSize(android.R.dimen.notification_large_icon_height);
+                resources.getDimensionPixelSize(android.R.dimen.notification_large_icon_height);
 
         // Scale has to account for both sides of the bg
         mBgPadding = (float) resources.getDimensionPixelSize(R.dimen.global_screenshot_bg_padding);
-        mBgPaddingScale = mBgPadding /  mDisplayMetrics.widthPixels;
+        mBgPaddingScale = mBgPadding / mDisplayMetrics.widthPixels;
 
         // determine the optimal preview size
         int panelWidth = 0;
@@ -877,6 +464,7 @@
             mScreenshotAnimation.start();
         });
     }
+
     private ValueAnimator createScreenshotDropInAnimation() {
         final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION)
                 / SCREENSHOT_DROP_IN_DURATION);
@@ -926,6 +514,7 @@
                 mScreenshotFlash.setAlpha(0f);
                 mScreenshotFlash.setVisibility(View.VISIBLE);
             }
+
             @Override
             public void onAnimationEnd(android.animation.Animator animation) {
                 mScreenshotFlash.setVisibility(View.GONE);
@@ -936,7 +525,7 @@
             public void onAnimationUpdate(ValueAnimator animation) {
                 float t = (Float) animation.getAnimatedValue();
                 float scaleT = (SCREENSHOT_SCALE + mBgPaddingScale)
-                    - scaleInterpolator.getInterpolation(t)
+                        - scaleInterpolator.getInterpolation(t)
                         * (SCREENSHOT_SCALE - SCREENSHOT_DROP_IN_MIN_SCALE);
                 mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA);
                 mScreenshotView.setAlpha(t);
@@ -947,6 +536,7 @@
         });
         return anim;
     }
+
     private ValueAnimator createScreenshotDropOutAnimation(int w, int h, boolean statusBarVisible,
             boolean navBarVisible) {
         ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
@@ -968,7 +558,8 @@
                 public void onAnimationUpdate(ValueAnimator animation) {
                     float t = (Float) animation.getAnimatedValue();
                     float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale)
-                            - t * (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_FAST_DROP_OUT_MIN_SCALE);
+                            - t * (SCREENSHOT_DROP_IN_MIN_SCALE
+                            - SCREENSHOT_FAST_DROP_OUT_MIN_SCALE);
                     mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
                     mScreenshotView.setAlpha(1f - t);
                     mScreenshotView.setScaleX(scaleT);
@@ -995,8 +586,10 @@
             float halfScreenHeight = (h - 2f * mBgPadding) / 2f;
             final float offsetPct = SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET;
             final PointF finalPos = new PointF(
-                -halfScreenWidth + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenWidth,
-                -halfScreenHeight + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenHeight);
+                    -halfScreenWidth
+                            + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenWidth,
+                    -halfScreenHeight
+                            + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenHeight);
 
             // Animate the screenshot to the status bar
             anim.setDuration(SCREENSHOT_DROP_OUT_DURATION);
@@ -1005,7 +598,7 @@
                 public void onAnimationUpdate(ValueAnimator animation) {
                     float t = (Float) animation.getAnimatedValue();
                     float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale)
-                        - scaleInterpolator.getInterpolation(t)
+                            - scaleInterpolator.getInterpolation(t)
                             * (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_DROP_OUT_MIN_SCALE);
                     mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
                     mScreenshotView.setAlpha(1f - scaleInterpolator.getInterpolation(t));
@@ -1066,15 +659,15 @@
 
         // Repurpose the existing notification to notify the user of the error
         Notification.Builder b = new Notification.Builder(context, NotificationChannels.ALERTS)
-            .setTicker(r.getString(R.string.screenshot_failed_title))
-            .setContentTitle(r.getString(R.string.screenshot_failed_title))
-            .setContentText(errorMsg)
-            .setSmallIcon(R.drawable.stat_notify_image_error)
-            .setWhen(System.currentTimeMillis())
-            .setVisibility(Notification.VISIBILITY_PUBLIC) // ok to show outside lockscreen
-            .setCategory(Notification.CATEGORY_ERROR)
-            .setAutoCancel(true)
-            .setColor(context.getColor(
+                .setTicker(r.getString(R.string.screenshot_failed_title))
+                .setContentTitle(r.getString(R.string.screenshot_failed_title))
+                .setContentText(errorMsg)
+                .setSmallIcon(R.drawable.stat_notify_image_error)
+                .setWhen(System.currentTimeMillis())
+                .setVisibility(Notification.VISIBILITY_PUBLIC) // ok to show outside lockscreen
+                .setCategory(Notification.CATEGORY_ERROR)
+                .setAutoCancel(true)
+                .setColor(context.getColor(
                         com.android.internal.R.color.system_notification_accent_color));
         final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
                 Context.DEVICE_POLICY_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
new file mode 100644
index 0000000..083f971
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import android.app.ActivityTaskManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Picture;
+import android.media.ExifInterface;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.DeviceConfig;
+import android.provider.MediaStore;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
+import com.android.internal.messages.nano.SystemMessageProto;
+import com.android.systemui.R;
+import com.android.systemui.SystemUI;
+import com.android.systemui.SystemUIFactory;
+import com.android.systemui.util.NotificationChannels;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * An AsyncTask that saves an image to the media store in the background.
+ */
+class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
+    private static final String TAG = "SaveImageInBackgroundTask";
+
+    private static final String SCREENSHOT_FILE_NAME_TEMPLATE = "Screenshot_%s.png";
+    private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)";
+
+    private final GlobalScreenshot.SaveImageInBackgroundData mParams;
+    private final NotificationManager mNotificationManager;
+    private final Notification.Builder mNotificationBuilder, mPublicNotificationBuilder;
+    private final String mImageFileName;
+    private final long mImageTime;
+    private final Notification.BigPictureStyle mNotificationStyle;
+    private final int mImageWidth;
+    private final int mImageHeight;
+    private final Handler mHandler;
+    private final ScreenshotNotificationSmartActionsProvider mSmartActionsProvider;
+
+    SaveImageInBackgroundTask(Context context, GlobalScreenshot.SaveImageInBackgroundData data,
+            NotificationManager nManager) {
+        Resources r = context.getResources();
+
+        // Prepare all the output metadata
+        mParams = data;
+        mImageTime = System.currentTimeMillis();
+        String imageDate = new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date(mImageTime));
+        mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, imageDate);
+
+        // Initialize screenshot notification smart actions provider.
+        mHandler = new Handler();
+        mSmartActionsProvider =
+                SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider();
+
+        // Create the large notification icon
+        mImageWidth = data.image.getWidth();
+        mImageHeight = data.image.getHeight();
+        int iconSize = data.iconSize;
+        int previewWidth = data.previewWidth;
+        int previewHeight = data.previewheight;
+
+        Paint paint = new Paint();
+        ColorMatrix desat = new ColorMatrix();
+        desat.setSaturation(0.25f);
+        paint.setColorFilter(new ColorMatrixColorFilter(desat));
+        Matrix matrix = new Matrix();
+        int overlayColor = 0x40FFFFFF;
+
+        matrix.setTranslate((previewWidth - mImageWidth) / 2,
+                (previewHeight - mImageHeight) / 2);
+        Bitmap picture = generateAdjustedHwBitmap(data.image, previewWidth, previewHeight,
+                matrix, paint, overlayColor);
+
+        // Note, we can't use the preview for the small icon, since it is non-square
+        float scale = (float) iconSize / Math.min(mImageWidth, mImageHeight);
+        matrix.setScale(scale, scale);
+        matrix.postTranslate((iconSize - (scale * mImageWidth)) / 2,
+                (iconSize - (scale * mImageHeight)) / 2);
+        Bitmap icon = generateAdjustedHwBitmap(data.image, iconSize, iconSize, matrix, paint,
+                overlayColor);
+
+        mNotificationManager = nManager;
+        final long now = System.currentTimeMillis();
+
+        // Setup the notification
+        mNotificationStyle = new Notification.BigPictureStyle()
+                .bigPicture(picture.createAshmemBitmap());
+
+        // The public notification will show similar info but with the actual screenshot omitted
+        mPublicNotificationBuilder =
+                new Notification.Builder(context, NotificationChannels.SCREENSHOTS_HEADSUP)
+                        .setContentTitle(r.getString(R.string.screenshot_saving_title))
+                        .setSmallIcon(R.drawable.stat_notify_image)
+                        .setCategory(Notification.CATEGORY_PROGRESS)
+                        .setWhen(now)
+                        .setShowWhen(true)
+                        .setColor(r.getColor(
+                                com.android.internal.R.color.system_notification_accent_color));
+        SystemUI.overrideNotificationAppName(context, mPublicNotificationBuilder, true);
+
+        mNotificationBuilder = new Notification.Builder(context,
+                NotificationChannels.SCREENSHOTS_HEADSUP)
+                .setContentTitle(r.getString(R.string.screenshot_saving_title))
+                .setSmallIcon(R.drawable.stat_notify_image)
+                .setWhen(now)
+                .setShowWhen(true)
+                .setColor(r.getColor(
+                        com.android.internal.R.color.system_notification_accent_color))
+                .setStyle(mNotificationStyle)
+                .setPublicVersion(mPublicNotificationBuilder.build());
+        mNotificationBuilder.setFlag(Notification.FLAG_NO_CLEAR, true);
+        SystemUI.overrideNotificationAppName(context, mNotificationBuilder, true);
+
+        mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT,
+                mNotificationBuilder.build());
+
+        /**
+         * NOTE: The following code prepares the notification builder for updating the
+         * notification after the screenshot has been written to disk.
+         */
+
+        // On the tablet, the large icon makes the notification appear as if it is clickable
+        // (and on small devices, the large icon is not shown) so defer showing the large icon
+        // until we compose the final post-save notification below.
+        mNotificationBuilder.setLargeIcon(icon.createAshmemBitmap());
+        // But we still don't set it for the expanded view, allowing the smallIcon to show here.
+        mNotificationStyle.bigLargeIcon((Bitmap) null);
+    }
+
+    private int getUserHandleOfForegroundApplication(Context context) {
+        // This logic matches
+        // com.android.systemui.statusbar.phone.PhoneStatusBarPolicy#updateManagedProfile
+        try {
+            return ActivityTaskManager.getService().getLastResumedActivityUserId();
+        } catch (RemoteException e) {
+            Slog.w(TAG, "getUserHandleOfForegroundApplication: ", e);
+            return context.getUserId();
+        }
+    }
+
+    private boolean isManagedProfile(Context context) {
+        UserManager manager = UserManager.get(context);
+        UserInfo info = manager.getUserInfo(getUserHandleOfForegroundApplication(context));
+        return info.isManagedProfile();
+    }
+
+    /**
+     * Generates a new hardware bitmap with specified values, copying the content from the
+     * passed in bitmap.
+     */
+    private Bitmap generateAdjustedHwBitmap(Bitmap bitmap, int width, int height, Matrix matrix,
+            Paint paint, int color) {
+        Picture picture = new Picture();
+        Canvas canvas = picture.beginRecording(width, height);
+        canvas.drawColor(color);
+        canvas.drawBitmap(bitmap, matrix, paint);
+        picture.endRecording();
+        return Bitmap.createBitmap(picture);
+    }
+
+    @Override
+    protected Void doInBackground(Void... paramsUnused) {
+        if (isCancelled()) {
+            return null;
+        }
+
+        // By default, AsyncTask sets the worker thread to have background thread priority,
+        // so bump it back up so that we save a little quicker.
+        Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
+
+        Context context = mParams.context;
+        Bitmap image = mParams.image;
+        boolean smartActionsEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS, true);
+        CompletableFuture<List<Notification.Action>>
+                smartActionsFuture = GlobalScreenshot.getSmartActionsFuture(
+                context, image, mSmartActionsProvider, mHandler, smartActionsEnabled,
+                isManagedProfile(context));
+
+        Resources r = context.getResources();
+
+        try {
+            // Save the screenshot to the MediaStore
+            final MediaStore.PendingParams params = new MediaStore.PendingParams(
+                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mImageFileName, "image/png");
+            params.setRelativePath(Environment.DIRECTORY_PICTURES + File.separator
+                    + Environment.DIRECTORY_SCREENSHOTS);
+
+            final Uri uri = MediaStore.createPending(context, params);
+            final MediaStore.PendingSession session = MediaStore.openPending(context, uri);
+            try {
+                // First, write the actual data for our screenshot
+                try (OutputStream out = session.openOutputStream()) {
+                    if (!image.compress(Bitmap.CompressFormat.PNG, 100, out)) {
+                        throw new IOException("Failed to compress");
+                    }
+                }
+
+                // Next, write metadata to help index the screenshot
+                try (ParcelFileDescriptor pfd = session.open()) {
+                    final ExifInterface exif = new ExifInterface(pfd.getFileDescriptor());
+
+                    exif.setAttribute(ExifInterface.TAG_SOFTWARE,
+                            "Android " + Build.DISPLAY);
+
+                    exif.setAttribute(ExifInterface.TAG_IMAGE_WIDTH,
+                            Integer.toString(image.getWidth()));
+                    exif.setAttribute(ExifInterface.TAG_IMAGE_LENGTH,
+                            Integer.toString(image.getHeight()));
+
+                    final ZonedDateTime time = ZonedDateTime.ofInstant(
+                            Instant.ofEpochMilli(mImageTime), ZoneId.systemDefault());
+                    exif.setAttribute(ExifInterface.TAG_DATETIME_ORIGINAL,
+                            DateTimeFormatter.ofPattern("yyyy:MM:dd HH:mm:ss").format(time));
+                    exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME_ORIGINAL,
+                            DateTimeFormatter.ofPattern("SSS").format(time));
+
+                    if (Objects.equals(time.getOffset(), ZoneOffset.UTC)) {
+                        exif.setAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL, "+00:00");
+                    } else {
+                        exif.setAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL,
+                                DateTimeFormatter.ofPattern("XXX").format(time));
+                    }
+
+                    exif.saveAttributes();
+                }
+                session.publish();
+            } catch (Exception e) {
+                session.abandon();
+                throw e;
+            } finally {
+                IoUtils.closeQuietly(session);
+            }
+
+            // Note: Both the share and edit actions are proxied through ActionProxyReceiver in
+            // order to do some common work like dismissing the keyguard and sending
+            // closeSystemWindows
+
+            // Create a share intent, this will always go through the chooser activity first
+            // which should not trigger auto-enter PiP
+            String subjectDate = DateFormat.getDateTimeInstance().format(new Date(mImageTime));
+            String subject = String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate);
+            Intent sharingIntent = new Intent(Intent.ACTION_SEND);
+            sharingIntent.setType("image/png");
+            sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
+            // Include URI in ClipData also, so that grantPermission picks it up.
+            // We don't use setData here because some apps interpret this as "to:".
+            ClipData clipdata = new ClipData(new ClipDescription("content",
+                    new String[]{ClipDescription.MIMETYPE_TEXT_PLAIN}),
+                    new ClipData.Item(uri));
+            sharingIntent.setClipData(clipdata);
+            sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
+            sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+            // Make sure pending intents for the system user are still unique across users
+            // by setting the (otherwise unused) request code to the current user id.
+            int requestCode = context.getUserId();
+
+            PendingIntent chooserAction = PendingIntent.getBroadcast(context, requestCode,
+                    new Intent(context, GlobalScreenshot.TargetChosenReceiver.class),
+                    PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
+            Intent sharingChooserIntent = Intent.createChooser(sharingIntent, null,
+                    chooserAction.getIntentSender())
+                    .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
+                    .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+            // Create a share action for the notification
+            PendingIntent shareAction = PendingIntent.getBroadcastAsUser(context, requestCode,
+                    new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
+                            .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, sharingChooserIntent)
+                            .putExtra(GlobalScreenshot.EXTRA_DISALLOW_ENTER_PIP, true)
+                            .setAction(Intent.ACTION_SEND),
+                    PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
+            Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder(
+                    R.drawable.ic_screenshot_share,
+                    r.getString(com.android.internal.R.string.share), shareAction);
+            mNotificationBuilder.addAction(shareActionBuilder.build());
+
+            // Create an edit intent, if a specific package is provided as the editor, then
+            // launch that directly
+            String editorPackage = context.getString(R.string.config_screenshotEditor);
+            Intent editIntent = new Intent(Intent.ACTION_EDIT);
+            if (!TextUtils.isEmpty(editorPackage)) {
+                editIntent.setComponent(ComponentName.unflattenFromString(editorPackage));
+            }
+            editIntent.setType("image/png");
+            editIntent.setData(uri);
+            editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+            // Create a edit action
+            PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, requestCode,
+                    new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
+                            .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, editIntent)
+                            .putExtra(GlobalScreenshot.EXTRA_CANCEL_NOTIFICATION,
+                                    editIntent.getComponent() != null)
+                            .setAction(Intent.ACTION_EDIT),
+                    PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
+            Notification.Action.Builder editActionBuilder = new Notification.Action.Builder(
+                    R.drawable.ic_screenshot_edit,
+                    r.getString(com.android.internal.R.string.screenshot_edit), editAction);
+            mNotificationBuilder.addAction(editActionBuilder.build());
+            if (editAction != null && mParams.onEditReady != null) {
+                mParams.onEditReady.apply(editAction);
+            }
+
+            // Create a delete action for the notification
+            PendingIntent deleteAction = PendingIntent.getBroadcast(context, requestCode,
+                    new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class)
+                            .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString()),
+                    PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
+            Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder(
+                    R.drawable.ic_screenshot_delete,
+                    r.getString(com.android.internal.R.string.delete), deleteAction);
+            mNotificationBuilder.addAction(deleteActionBuilder.build());
+
+            mParams.imageUri = uri;
+            mParams.image = null;
+            mParams.errorMsgResId = 0;
+
+            if (smartActionsEnabled) {
+                int timeoutMs = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
+                        SystemUiDeviceConfigFlags
+                                .SCREENSHOT_NOTIFICATION_SMART_ACTIONS_TIMEOUT_MS,
+                        1000);
+                List<Notification.Action> smartActions = GlobalScreenshot.getSmartActions(
+                        smartActionsFuture,
+                        timeoutMs);
+                for (Notification.Action action : smartActions) {
+                    mNotificationBuilder.addAction(action);
+                }
+            }
+        } catch (Exception e) {
+            // IOException/UnsupportedOperationException may be thrown if external storage is
+            // not mounted
+            Slog.e(TAG, "unable to save screenshot", e);
+            mParams.clearImage();
+            mParams.errorMsgResId = R.string.screenshot_failed_to_save_text;
+        }
+
+        // Recycle the bitmap data
+        if (image != null) {
+            image.recycle();
+        }
+
+        return null;
+    }
+
+    @Override
+    protected void onPostExecute(Void params) {
+        if (mParams.errorMsgResId != 0) {
+            // Show a message that we've failed to save the image to disk
+            GlobalScreenshot.notifyScreenshotError(mParams.context, mNotificationManager,
+                    mParams.errorMsgResId);
+        } else {
+            if (mParams.onEditReady != null) {
+                // Cancel the "saving screenshot" notification
+                mNotificationManager.cancel(
+                        SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT);
+            } else {
+                // Show the final notification to indicate screenshot saved
+                Context context = mParams.context;
+                Resources r = context.getResources();
+
+                // Create the intent to show the screenshot in gallery
+                Intent launchIntent = new Intent(Intent.ACTION_VIEW);
+                launchIntent.setDataAndType(mParams.imageUri, "image/png");
+                launchIntent.setFlags(
+                        Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+                final long now = System.currentTimeMillis();
+
+                // Update the text and the icon for the existing notification
+                mPublicNotificationBuilder
+                        .setContentTitle(r.getString(R.string.screenshot_saved_title))
+                        .setContentText(r.getString(R.string.screenshot_saved_text))
+                        .setContentIntent(
+                                PendingIntent.getActivity(mParams.context, 0, launchIntent, 0))
+                        .setWhen(now)
+                        .setAutoCancel(true)
+                        .setColor(context.getColor(
+                                com.android.internal.R.color.system_notification_accent_color));
+                mNotificationBuilder
+                        .setContentTitle(r.getString(R.string.screenshot_saved_title))
+                        .setContentText(r.getString(R.string.screenshot_saved_text))
+                        .setContentIntent(PendingIntent.getActivity(mParams.context, 0,
+                                launchIntent, 0))
+                        .setWhen(now)
+                        .setAutoCancel(true)
+                        .setColor(context.getColor(
+                                com.android.internal.R.color.system_notification_accent_color))
+                        .setPublicVersion(mPublicNotificationBuilder.build())
+                        .setFlag(Notification.FLAG_NO_CLEAR, false);
+
+                mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT,
+                        mNotificationBuilder.build());
+            }
+        }
+        mParams.finisher.run();
+        mParams.clearContext();
+    }
+
+    @Override
+    protected void onCancelled(Void params) {
+        // If we are cancelled while the task is running in the background, we may get null
+        // params. The finisher is expected to always be called back, so just use the baked-in
+        // params from the ctor in any case.
+        mParams.finisher.run();
+        mParams.clearImage();
+        mParams.clearContext();
+
+        // Cancel the posted notification
+        mNotificationManager.cancel(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index c4de2d3..98a2675 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -110,8 +110,7 @@
                 }
 
                 String key = sbn.getKey();
-                boolean isUpdate =
-                        mEntryManager.getNotificationData().get(key) != null;
+                boolean isUpdate = mEntryManager.getActiveNotificationUnfiltered(key) != null;
                 // In case we don't allow child notifications, we ignore children of
                 // notifications that have a summary, since` we're not going to show them
                 // anyway. This is true also when the summary is canceled,
@@ -126,8 +125,7 @@
                     if (isUpdate) {
                         mEntryManager.removeNotification(key, rankingMap, UNDEFINED_DISMISS_REASON);
                     } else {
-                        mEntryManager.getNotificationData()
-                                .updateRanking(rankingMap, "onNotificationPosted");
+                        mEntryManager.updateRanking(rankingMap, "onNotificationPosted");
                     }
                     return;
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 571d3d7..021e7e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -15,6 +15,7 @@
  */
 package com.android.systemui.statusbar;
 
+import static android.app.Notification.VISIBILITY_SECRET;
 import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
 
 import static com.android.systemui.DejankUtils.whitelistIpcs;
@@ -126,7 +127,7 @@
                 updatePublicMode();
                 // The filtering needs to happen before the update call below in order to make sure
                 // the presenter has the updated notifications from the new user
-                getEntryManager().getNotificationData().filterAndSort("user switched");
+                getEntryManager().reapplyFilterAndSort("user switched");
                 mPresenter.onUserSwitched(mCurrentUserId);
 
                 for (UserChangedListener listener : mListeners) {
@@ -148,17 +149,17 @@
                     }
                 }
                 if (notificationKey != null) {
-                    final int count =
-                            getEntryManager().getNotificationData().getActiveNotifications().size();
-                    final int rank = getEntryManager().getNotificationData().getRank(notificationKey);
+                    NotificationEntry entry =
+                            getEntryManager().getActiveNotificationUnfiltered(notificationKey);
+                    final int count = getEntryManager().getActiveNotificationsCount();
+                    final int rank = entry != null ? entry.getRanking().getRank() : 0;
                     NotificationVisibility.NotificationLocation location =
-                            NotificationLogger.getNotificationLocation(
-                                    getEntryManager().getNotificationData().get(notificationKey));
+                            NotificationLogger.getNotificationLocation(entry);
                     final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey,
                             rank, count, true, location);
                     try {
                         mBarService.onNotificationClick(notificationKey, nv);
-                    } catch (RemoteException e) {
+                    } catch (RemoteException exception) {
                         /* ignore */
                     }
                 }
@@ -311,9 +312,9 @@
             Log.wtf(TAG, "mEntryManager was null!", new Throwable());
             return true;
         }
-        return isLockscreenPublicMode(mCurrentUserId)
-                && getEntryManager().getNotificationData().getVisibilityOverride(key) ==
-                        Notification.VISIBILITY_SECRET;
+        NotificationEntry visibleEntry = getEntryManager().getActiveNotificationUnfiltered(key);
+        return isLockscreenPublicMode(mCurrentUserId) && visibleEntry != null
+                && visibleEntry.getRanking().getVisibilityOverride() == VISIBILITY_SECRET;
     }
 
     public boolean shouldShowOnKeyguard(NotificationEntry entry) {
@@ -326,8 +327,7 @@
                 && hideSilentNotificationsOnLockscreen()) {
             exceedsPriorityThreshold = entry.getBucket() != BUCKET_SILENT;
         } else {
-            exceedsPriorityThreshold =
-                    !getEntryManager().getNotificationData().isAmbient(entry.getKey());
+            exceedsPriorityThreshold = !entry.getRanking().isAmbient();
         }
         return mShowLockscreenNotifications && exceedsPriorityThreshold;
     }
@@ -467,8 +467,9 @@
             Log.wtf(TAG, "mEntryManager was null!", new Throwable());
             return true;
         }
-        return getEntryManager().getNotificationData().getVisibilityOverride(key) ==
-                Notification.VISIBILITY_PRIVATE;
+        NotificationEntry entry = getEntryManager().getActiveNotificationUnfiltered(key);
+        return entry != null
+                && entry.getRanking().getVisibilityOverride() == Notification.VISIBILITY_PRIVATE;
     }
 
     private void updateCurrentProfilesCache() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index d668665..a98f826 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -102,8 +102,7 @@
     }
 
 
-    // Late binding
-    private NotificationEntryManager mEntryManager;
+    private final NotificationEntryManager mEntryManager;
 
     // Late binding, also @Nullable due to being in com.android.systemui.statusbar.phone package
     @Nullable
@@ -258,8 +257,9 @@
         if (mMediaNotificationKey == null) {
             return null;
         }
-        synchronized (mEntryManager.getNotificationData()) {
-            NotificationEntry entry = mEntryManager.getNotificationData().get(mMediaNotificationKey);
+        synchronized (mEntryManager) {
+            NotificationEntry entry = mEntryManager
+                    .getActiveNotificationUnfiltered(mMediaNotificationKey);
             if (entry == null || entry.expandedIcon == null) {
                 return null;
             }
@@ -281,8 +281,9 @@
     public void findAndUpdateMediaNotifications() {
         boolean metaDataChanged = false;
 
-        synchronized (mEntryManager.getNotificationData()) {
-            Set<NotificationEntry> allNotifications = mEntryManager.getAllNotifs();
+        synchronized (mEntryManager) {
+            Set<NotificationEntry> allNotifications =
+                    mEntryManager.getPendingAndActiveNotifications();
 
             // Promote the media notification with a controller in 'playing' state, if any.
             NotificationEntry mediaNotification = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 35f06f9..e10d27b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -184,8 +184,9 @@
                 ViewGroup actionGroup = (ViewGroup) parent;
                 buttonIndex = actionGroup.indexOfChild(view);
             }
-            final int count = mEntryManager.getNotificationData().getActiveNotifications().size();
-            final int rank = mEntryManager.getNotificationData().getRank(key);
+            final int count = mEntryManager.getActiveNotificationsCount();
+            final int rank = mEntryManager
+                    .getActiveNotificationUnfiltered(key).getRanking().getRank();
 
             // Notification may be updated before this function is executed, and thus play safe
             // here and verify that the action object is still the one that where the click happens.
@@ -202,7 +203,7 @@
             }
             NotificationVisibility.NotificationLocation location =
                     NotificationLogger.getNotificationLocation(
-                            mEntryManager.getNotificationData().get(key));
+                            mEntryManager.getActiveNotificationUnfiltered(key));
             final NotificationVisibility nv =
                     NotificationVisibility.obtain(key, rank, count, true, location);
             try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 20a3e35..ef733a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -140,8 +140,7 @@
         Assert.isMainThread();
         beginUpdate();
 
-        ArrayList<NotificationEntry> activeNotifications = mEntryManager.getNotificationData()
-                .getActiveNotifications();
+        List<NotificationEntry> activeNotifications = mEntryManager.getVisibleNotifications();
         ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
         final int N = activeNotifications.size();
         for (int i = 0; i < N; i++) {
@@ -339,7 +338,7 @@
                 }
                 for (ExpandableNotificationRow remove : toRemove) {
                     parent.removeChildNotification(remove);
-                    if (mEntryManager.getNotificationData().get(
+                    if (mEntryManager.getActiveNotificationUnfiltered(
                             remove.getStatusBarNotification().getKey()) == null) {
                         // We only want to add an animation if the view is completely removed
                         // otherwise it's just a transfer
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index 7bdb21d..40f8e39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -73,8 +73,8 @@
     public void smartActionClicked(
             NotificationEntry entry, int actionIndex, Notification.Action action,
             boolean generatedByAssistant) {
-        final int count = mEntryManager.getNotificationData().getActiveNotifications().size();
-        final int rank = mEntryManager.getNotificationData().getRank(entry.getKey());
+        final int count = mEntryManager.getActiveNotificationsCount();
+        final int rank = entry.getRanking().getRank();
         NotificationVisibility.NotificationLocation location =
                 NotificationLogger.getNotificationLocation(entry);
         final NotificationVisibility nv = NotificationVisibility.obtain(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
index 314dc04..015c323 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
@@ -37,14 +37,14 @@
  */
 @Singleton
 class BypassHeadsUpNotifier @Inject constructor(
-        private val context: Context,
-        private val bypassController: KeyguardBypassController,
-        private val statusBarStateController: StatusBarStateController,
-        private val headsUpManager: HeadsUpManagerPhone,
-        private val notificationLockscreenUserManager: NotificationLockscreenUserManager,
-        private val mediaManager: NotificationMediaManager,
-        tunerService: TunerService) : StatusBarStateController.StateListener,
-        NotificationMediaManager.MediaListener {
+    private val context: Context,
+    private val bypassController: KeyguardBypassController,
+    private val statusBarStateController: StatusBarStateController,
+    private val headsUpManager: HeadsUpManagerPhone,
+    private val notificationLockscreenUserManager: NotificationLockscreenUserManager,
+    private val mediaManager: NotificationMediaManager,
+    tunerService: TunerService
+) : StatusBarStateController.StateListener, NotificationMediaManager.MediaListener {
 
     private lateinit var entryManager: NotificationEntryManager
     private var currentMediaEntry: NotificationEntry? = null
@@ -77,7 +77,8 @@
 
     override fun onMetadataOrStateChanged(metadata: MediaMetadata?, state: Int) {
         val previous = currentMediaEntry
-        var newEntry = entryManager.notificationData.get(mediaManager.mediaNotificationKey)
+        var newEntry = entryManager
+                .getActiveNotificationUnfiltered(mediaManager.mediaNotificationKey)
         if (!NotificationMediaManager.isPlayingState(state)) {
             newEntry = null
         }
@@ -101,7 +102,7 @@
      */
     private fun canAutoHeadsUp(entry: NotificationEntry): Boolean {
         if (!isAutoHeadsUpAllowed()) {
-            return false;
+            return false
         }
         if (entry.isSensitive) {
             // filter sensitive notifications
@@ -111,7 +112,7 @@
             // filter notifications invisible on Keyguard
             return false
         }
-        if (!entryManager.notificationData.activeNotifications.contains(entry)) {
+        if (entryManager.getActiveNotificationUnfiltered(entry.key) != null) {
             // filter notifications not the active list currently
             return false
         }
@@ -125,7 +126,7 @@
     /**
      * @return {@code true} if autoHeadsUp is possible right now.
      */
-    private fun isAutoHeadsUpAllowed() : Boolean {
+    private fun isAutoHeadsUpAllowed(): Boolean {
         if (!enabled) {
             return false
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
index df78fa3..0694920 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
@@ -99,7 +99,8 @@
     /**
      * Called whenever notification ranking changes, in response to
      * {@link NotificationListenerService#onNotificationRankingUpdate}. This is called after
-     * NotificationData has processed the update and notifications have been re-sorted and filtered.
+     * NotificationEntryManager has processed the update and notifications have been re-sorted
+     * and filtered.
      *
      * @param rankingMap provides access to ranking information on currently active notifications
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 13d90ff..7a58097 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -18,10 +18,12 @@
 import static android.service.notification.NotificationListenerService.REASON_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_ERROR;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Notification;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.StatusBarNotification;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -36,9 +38,8 @@
 import com.android.systemui.statusbar.NotificationRemoveInterceptor;
 import com.android.systemui.statusbar.NotificationUiAdjustment;
 import com.android.systemui.statusbar.NotificationUpdateHandler;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
-import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
 import com.android.systemui.statusbar.notification.collection.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.logging.NotifEvent;
 import com.android.systemui.statusbar.notification.logging.NotifLog;
@@ -46,12 +47,15 @@
 import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
 import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.util.Assert;
 import com.android.systemui.util.leak.LeakDetector;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -62,9 +66,29 @@
 import javax.inject.Singleton;
 
 /**
- * NotificationEntryManager is responsible for the adding, removing, and updating of notifications.
- * It also handles tasks such as their inflation and their interaction with other
- * Notification.*Manager objects.
+ * NotificationEntryManager is responsible for the adding, removing, and updating of
+ * {@link NotificationEntry}s. It also handles tasks such as their inflation and their interaction
+ * with other Notification.*Manager objects.
+ *
+ * We track notification entries through this lifecycle:
+ *      1. Pending
+ *      2. Active
+ *      3. Sorted / filtered (visible)
+ *
+ * Every entry spends some amount of time in the pending state, while it is being inflated. Once
+ * inflated, an entry moves into the active state, where it _could_ potentially be shown to the
+ * user. After an entry makes its way into the active state, we sort and filter the entire set to
+ * repopulate the visible set.
+ *
+ * There are a few different things that other classes may be interested in, and most of them
+ * involve the current set of notifications. Here's a brief overview of things you may want to know:
+ * @see #getVisibleNotifications() for the visible set
+ * @see #getActiveNotificationUnfiltered(String) to check if a key exists
+ * @see #getPendingNotificationsIterator() for an iterator over the pending notifications
+ * @see #getPendingOrActiveNotif(String) to find a notification exists for that key in any list
+ * @see #getPendingAndActiveNotifications() to get the entire set of Notifications that we're
+ * aware of
+ * @see #getActiveNotificationsForCurrentUser() to see every notification that the current user owns
  */
 @Singleton
 public class NotificationEntryManager implements
@@ -78,12 +102,23 @@
     /**
      * Used when a notification is removed and it doesn't have a reason that maps to one of the
      * reasons defined in NotificationListenerService
-     * (e.g. {@link NotificationListenerService.REASON_CANCEL})
+     * (e.g. {@link NotificationListenerService#REASON_CANCEL})
      */
     public static final int UNDEFINED_DISMISS_REASON = 0;
 
+    /** Pending notifications are ones awaiting inflation */
     @VisibleForTesting
     protected final HashMap<String, NotificationEntry> mPendingNotifications = new HashMap<>();
+    /**
+     * Active notifications have been inflated / prepared and could become visible, but may get
+     * filtered out if for instance they are not for the current user
+     */
+    private final ArrayMap<String, NotificationEntry> mActiveNotifications = new ArrayMap<>();
+    @VisibleForTesting
+    /** This is the list of "active notifications for this user in this context" */
+    protected final ArrayList<NotificationEntry> mSortedAndFiltered = new ArrayList<>();
+    private final List<NotificationEntry> mReadOnlyNotifications =
+            Collections.unmodifiableList(mSortedAndFiltered);
 
     private final Map<NotificationEntry, NotificationLifetimeExtender> mRetainedNotifications =
             new ArrayMap<>();
@@ -92,10 +127,12 @@
     private NotificationRemoteInputManager mRemoteInputManager;
     private NotificationRowBinder mNotificationRowBinder;
 
+    private final KeyguardEnvironment mKeyguardEnvironment;
+    private final NotificationGroupManager mGroupManager;
+    private final NotificationRankingManager mRankingManager;
+
     private NotificationPresenter mPresenter;
-    private NotificationListenerService.RankingMap mLatestRankingMap;
-    @VisibleForTesting
-    protected NotificationData mNotificationData;
+    private RankingMap mLatestRankingMap;
     private NotifLog mNotifLog;
 
     @VisibleForTesting
@@ -129,10 +166,14 @@
 
     @Inject
     public NotificationEntryManager(
-            NotificationData notificationData,
-            NotifLog notifLog) {
-        mNotificationData = notificationData;
+            NotifLog notifLog,
+            NotificationGroupManager groupManager,
+            NotificationRankingManager rankingManager,
+            KeyguardEnvironment keyguardEnvironment) {
         mNotifLog = notifLog;
+        mGroupManager = groupManager;
+        mRankingManager = rankingManager;
+        mKeyguardEnvironment = keyguardEnvironment;
     }
 
     /** Adds a {@link NotificationEntryListener}. */
@@ -171,7 +212,6 @@
             NotificationListContainer listContainer,
             HeadsUpManager headsUpManager) {
         mPresenter = presenter;
-        mNotificationData.setHeadsUpManager(headsUpManager);
     }
 
     /** Adds multiple {@link NotificationLifetimeExtender}s. */
@@ -188,10 +228,6 @@
                 UNDEFINED_DISMISS_REASON));
     }
 
-    public NotificationData getNotificationData() {
-        return mNotificationData;
-    }
-
     @Override
     public void onReorderingAllowed() {
         updateNotifications("reordering is now allowed");
@@ -212,10 +248,17 @@
     }
 
     private NotificationVisibility obtainVisibility(String key) {
-        final int rank = mNotificationData.getRank(key);
-        final int count = mNotificationData.getActiveNotifications().size();
+        NotificationEntry e = mActiveNotifications.get(key);
+        final int rank;
+        if (e != null) {
+            rank = e.getRanking().getRank();
+        } else {
+            rank = 0;
+        }
+
+        final int count = mActiveNotifications.size();
         NotificationVisibility.NotificationLocation location =
-                NotificationLogger.getNotificationLocation(getNotificationData().get(key));
+                NotificationLogger.getNotificationLocation(getActiveNotificationUnfiltered(key));
         return NotificationVisibility.obtain(key, rank, count, true, location);
     }
 
@@ -227,7 +270,7 @@
             mNotifLog.log(NotifEvent.INFLATION_ABORTED, entry.getSbn(), null,
                     "PendingNotification aborted. " + reason);
         }
-        NotificationEntry addedEntry = mNotificationData.get(key);
+        NotificationEntry addedEntry = getActiveNotificationUnfiltered(key);
         if (addedEntry != null) {
             addedEntry.abortTask();
             mNotifLog.log(NotifEvent.INFLATION_ABORTED, addedEntry.getSbn(),
@@ -258,13 +301,13 @@
         // If there was an async task started after the removal, we don't want to add it back to
         // the list, otherwise we might get leaks.
         if (!entry.isRowRemoved()) {
-            boolean isNew = mNotificationData.get(entry.getKey()) == null;
+            boolean isNew = getActiveNotificationUnfiltered(entry.getKey()) == null;
             if (isNew) {
                 for (NotificationEntryListener listener : mNotificationEntryListeners) {
                     mNotifLog.log(NotifEvent.INFLATED, entry);
                     listener.onEntryInflated(entry, inflatedFlags);
                 }
-                mNotificationData.add(entry);
+                addActiveNotification(entry);
                 updateNotifications("onAsyncInflationFinished");
                 for (NotificationEntryListener listener : mNotificationEntryListeners) {
                     listener.onNotificationAdded(entry);
@@ -278,8 +321,34 @@
         }
     }
 
+    /**
+     * Equivalent to the old NotificationData#add
+     * @param entry - an entry which is prepared for display
+     */
+    private void addActiveNotification(NotificationEntry entry) {
+        Assert.isMainThread();
+
+        mActiveNotifications.put(entry.getKey(), entry);
+        mGroupManager.onEntryAdded(entry);
+        updateRankingAndSort(mRankingManager.getRankingMap(), "addEntryInternalInternal");
+    }
+
+    /**
+     * Available so that tests can directly manipulate the list of active notifications easily
+     *
+     * @param entry the entry to add directly to the visible notification map
+     */
+    @VisibleForTesting
+    public void addActiveNotificationForTest(NotificationEntry entry) {
+        mActiveNotifications.put(entry.getKey(), entry);
+        mGroupManager.onEntryAdded(entry);
+
+        reapplyFilterAndSort("addVisibleNotification");
+    }
+
+
     @Override
-    public void removeNotification(String key, NotificationListenerService.RankingMap ranking,
+    public void removeNotification(String key, RankingMap ranking,
             int reason) {
         removeNotificationInternal(key, ranking, obtainVisibility(key), false /* forceRemove */,
                 false /* removedByUser */, reason);
@@ -287,7 +356,7 @@
 
     private void removeNotificationInternal(
             String key,
-            @Nullable NotificationListenerService.RankingMap ranking,
+            @Nullable RankingMap ranking,
             @Nullable NotificationVisibility visibility,
             boolean forceRemove,
             boolean removedByUser,
@@ -300,7 +369,7 @@
             return;
         }
 
-        final NotificationEntry entry = mNotificationData.get(key);
+        final NotificationEntry entry = getActiveNotificationUnfiltered(key);
         boolean lifetimeExtended = false;
 
         // Notification was canceled before it got inflated
@@ -355,8 +424,7 @@
 
                 // Let's remove the children if this was a summary
                 handleGroupSummaryRemoved(key);
-
-                mNotificationData.remove(key, ranking);
+                removeVisibleNotification(key);
                 updateNotifications("removeNotificationInternal");
                 Dependency.get(LeakDetector.class).trackGarbage(entry);
                 removedByUser |= entryDismissed;
@@ -381,7 +449,7 @@
      *
      */
     private void handleGroupSummaryRemoved(String key) {
-        NotificationEntry entry = mNotificationData.get(key);
+        NotificationEntry entry = getActiveNotificationUnfiltered(key);
         if (entry != null && entry.rowExists() && entry.isSummaryWithChildren()) {
             if (entry.getSbn().getOverrideGroupKey() != null && !entry.isRowDismissed()) {
                 // We don't want to remove children for autobundled notifications as they are not
@@ -413,13 +481,14 @@
     }
 
     private void addNotificationInternal(StatusBarNotification notification,
-            NotificationListenerService.RankingMap rankingMap) throws InflationException {
+            RankingMap rankingMap) throws InflationException {
         String key = notification.getKey();
         if (DEBUG) {
             Log.d(TAG, "addNotification key=" + key);
         }
 
-        mNotificationData.updateRanking(rankingMap, "addNotificationInternal");
+        updateRankingAndSort(rankingMap, "addNotificationInternal");
+
         Ranking ranking = new Ranking();
         rankingMap.getRanking(key, ranking);
 
@@ -439,8 +508,7 @@
     }
 
     @Override
-    public void addNotification(StatusBarNotification notification,
-            NotificationListenerService.RankingMap ranking) {
+    public void addNotification(StatusBarNotification notification, RankingMap ranking) {
         try {
             addNotificationInternal(notification, ranking);
         } catch (InflationException e) {
@@ -449,12 +517,12 @@
     }
 
     private void updateNotificationInternal(StatusBarNotification notification,
-            NotificationListenerService.RankingMap ranking) throws InflationException {
+            RankingMap ranking) throws InflationException {
         if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
 
         final String key = notification.getKey();
         abortExistingInflation(key, "updateNotification");
-        NotificationEntry entry = mNotificationData.get(key);
+        NotificationEntry entry = getActiveNotificationUnfiltered(key);
         if (entry == null) {
             return;
         }
@@ -463,7 +531,11 @@
         // to keep its lifetime extended.
         cancelLifetimeExtension(entry);
 
-        mNotificationData.update(entry, ranking, notification, "updateNotificationInternal");
+        updateRankingAndSort(ranking, "updateNotificationInternal");
+        StatusBarNotification oldSbn = entry.getSbn();
+        entry.setSbn(notification);
+        mGroupManager.onEntryUpdated(entry, oldSbn);
+
         mNotifLog.log(NotifEvent.NOTIF_UPDATED, entry.getSbn(), entry.getRanking());
         for (NotificationEntryListener listener : mNotificationEntryListeners) {
             listener.onPreEntryUpdated(entry);
@@ -486,8 +558,7 @@
     }
 
     @Override
-    public void updateNotification(StatusBarNotification notification,
-            NotificationListenerService.RankingMap ranking) {
+    public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
         try {
             updateNotificationInternal(notification, ranking);
         } catch (InflationException e) {
@@ -500,16 +571,16 @@
      * @param reason why the notifications are updating
      */
     public void updateNotifications(String reason) {
-        mNotificationData.filterAndSort(reason);
+        reapplyFilterAndSort(reason);
         if (mPresenter != null) {
             mPresenter.updateNotificationViews();
         }
     }
 
     @Override
-    public void updateNotificationRanking(NotificationListenerService.RankingMap rankingMap) {
+    public void updateNotificationRanking(RankingMap rankingMap) {
         List<NotificationEntry> entries = new ArrayList<>();
-        entries.addAll(mNotificationData.getActiveNotifications());
+        entries.addAll(getVisibleNotifications());
         entries.addAll(mPendingNotifications.values());
 
         // Has a copy of the current UI adjustments.
@@ -523,7 +594,7 @@
         }
 
         // Populate notification entries from the new rankings.
-        mNotificationData.updateRanking(rankingMap, "updateNotificationRanking");
+        updateRankingAndSort(rankingMap, "updateNotificationRanking");
         updateRankingOfPendingNotifications(rankingMap);
 
         // By comparing the old and new UI adjustments, reinflate the view accordingly.
@@ -542,8 +613,7 @@
         }
     }
 
-    private void updateRankingOfPendingNotifications(
-            @Nullable NotificationListenerService.RankingMap rankingMap) {
+    private void updateRankingOfPendingNotifications(@Nullable RankingMap rankingMap) {
         if (rankingMap == null) {
             return;
         }
@@ -565,23 +635,35 @@
     }
 
     /**
-     * @return all notification we're currently aware of (both pending and visible notifications)
+     * @return all notifications we're currently aware of (both pending and active notifications)
      */
-    public Set<NotificationEntry> getAllNotifs() {
+    public Set<NotificationEntry> getPendingAndActiveNotifications() {
         Set<NotificationEntry> allNotifs = new HashSet<>(mPendingNotifications.values());
-        allNotifs.addAll(mNotificationData.getActiveNotifications());
+        allNotifs.addAll(mSortedAndFiltered);
         return allNotifs;
     }
 
     /**
+     * Use this method to retrieve a notification entry that has been prepared for presentation.
+     * Note that the notification may be filtered out and never shown to the user.
+     *
+     * @see #getVisibleNotifications() for the currently sorted and filtered list
+     *
+     * @return a {@link NotificationEntry} if it has been prepared, else null
+     */
+    public NotificationEntry getActiveNotificationUnfiltered(String key) {
+        return mActiveNotifications.get(key);
+    }
+
+    /**
      * Gets the pending or visible notification entry with the given key. Returns null if
      * notification doesn't exist.
      */
-    public NotificationEntry getPendingOrCurrentNotif(String key) {
+    public NotificationEntry getPendingOrActiveNotif(String key) {
         if (mPendingNotifications.containsKey(key)) {
             return mPendingNotifications.get(key);
         } else {
-            return mNotificationData.get(key);
+            return mActiveNotifications.get(key);
         }
     }
 
@@ -608,4 +690,136 @@
         }
         return mNotificationRowBinder;
     }
+
+    /*
+     * -----
+     * Annexed from NotificationData below:
+     * Some of these methods may be redundant but require some reworking to remove. For now
+     * we'll try to keep the behavior the same and can simplify these interfaces in another pass
+     */
+
+    /** Internalization of NotificationData#remove */
+    private void removeVisibleNotification(String key) {
+        // no need to synchronize if we're on the main thread dawg
+        Assert.isMainThread();
+
+        NotificationEntry removed = mActiveNotifications.remove(key);
+
+        if (removed == null) return;
+        mGroupManager.onEntryRemoved(removed);
+    }
+
+    /** @return list of active notifications filtered for the current user */
+    public List<NotificationEntry> getActiveNotificationsForCurrentUser() {
+        Assert.isMainThread();
+        ArrayList<NotificationEntry> filtered = new ArrayList<>();
+
+        final int len = mActiveNotifications.size();
+        for (int i = 0; i < len; i++) {
+            NotificationEntry entry = mActiveNotifications.valueAt(i);
+            final StatusBarNotification sbn = entry.getSbn();
+            if (!mKeyguardEnvironment.isNotificationForCurrentProfiles(sbn)) {
+                continue;
+            }
+            filtered.add(entry);
+        }
+
+        return filtered;
+    }
+
+    //TODO: Get rid of this in favor of NotificationUpdateHandler#updateNotificationRanking
+    /**
+     * @param rankingMap the {@link RankingMap} to apply to the current notification list
+     * @param reason the reason for calling this method, for {@link NotifLog}
+     */
+    public void updateRanking(RankingMap rankingMap, String reason) {
+        updateRankingAndSort(rankingMap, reason);
+    }
+
+    /** Resorts / filters the current notification set with the current RankingMap */
+    public void reapplyFilterAndSort(String reason) {
+        updateRankingAndSort(mRankingManager.getRankingMap(), reason);
+    }
+
+    /** Calls to NotificationRankingManager and updates mSortedAndFiltered */
+    private void updateRankingAndSort(@NonNull RankingMap rankingMap, String reason) {
+        mSortedAndFiltered.clear();
+        mSortedAndFiltered.addAll(mRankingManager.updateRanking(
+                rankingMap, mActiveNotifications.values(), reason));
+    }
+
+    /** dump the current active notification list. Called from StatusBar */
+    public void dump(PrintWriter pw, String indent) {
+        pw.println("NotificationEntryManager");
+        int filteredLen = mSortedAndFiltered.size();
+        pw.print(indent);
+        pw.println("active notifications: " + filteredLen);
+        int active;
+        for (active = 0; active < filteredLen; active++) {
+            NotificationEntry e = mSortedAndFiltered.get(active);
+            dumpEntry(pw, indent, active, e);
+        }
+        synchronized (mActiveNotifications) {
+            int totalLen = mActiveNotifications.size();
+            pw.print(indent);
+            pw.println("inactive notifications: " + (totalLen - active));
+            int inactiveCount = 0;
+            for (int i = 0; i < totalLen; i++) {
+                NotificationEntry entry = mActiveNotifications.valueAt(i);
+                if (!mSortedAndFiltered.contains(entry)) {
+                    dumpEntry(pw, indent, inactiveCount, entry);
+                    inactiveCount++;
+                }
+            }
+        }
+    }
+
+    private void dumpEntry(PrintWriter pw, String indent, int i, NotificationEntry e) {
+        pw.print(indent);
+        pw.println("  [" + i + "] key=" + e.getKey() + " icon=" + e.icon);
+        StatusBarNotification n = e.getSbn();
+        pw.print(indent);
+        pw.println("      pkg=" + n.getPackageName() + " id=" + n.getId() + " importance="
+                + e.getRanking().getImportance());
+        pw.print(indent);
+        pw.println("      notification=" + n.getNotification());
+    }
+
+    /**
+     * This is the answer to the question "what notifications should the user be seeing right now?"
+     * These are sorted and filtered, and directly inform the notification shade what to show
+     *
+     * @return A read-only list of the currently active notifications
+     */
+    public List<NotificationEntry> getVisibleNotifications() {
+        return mReadOnlyNotifications;
+    }
+
+    /** @return A count of the active notifications */
+    public int getActiveNotificationsCount() {
+        return mReadOnlyNotifications.size();
+    }
+
+    /**
+     * @return {@code true} if there is at least one notification that should be visible right now
+     */
+    public boolean hasActiveNotifications() {
+        return mReadOnlyNotifications.size() != 0;
+    }
+
+    /*
+     * End annexation
+     * -----
+     */
+
+
+    /**
+     * Provides access to keyguard state and user settings dependent data.
+     */
+    public interface KeyguardEnvironment {
+        /** true if the device is provisioned (should always be true in practice) */
+        boolean isDeviceProvisioned();
+        /** true if the notification is for the current profiles */
+        boolean isNotificationForCurrentProfiles(StatusBarNotification sbn);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
index b116409..e5f44bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
@@ -28,7 +28,6 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.ForegroundServiceController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ShadeController;
@@ -44,7 +43,7 @@
     private final NotificationGroupManager mGroupManager = Dependency.get(
             NotificationGroupManager.class);
 
-    private NotificationData.KeyguardEnvironment mEnvironment;
+    private NotificationEntryManager.KeyguardEnvironment mEnvironment;
     private ShadeController mShadeController;
     private ForegroundServiceController mFsc;
     private NotificationLockscreenUserManager mUserManager;
@@ -52,9 +51,9 @@
     @Inject
     public NotificationFilter() {}
 
-    private NotificationData.KeyguardEnvironment getEnvironment() {
+    private NotificationEntryManager.KeyguardEnvironment getEnvironment() {
         if (mEnvironment == null) {
-            mEnvironment = Dependency.get(NotificationData.KeyguardEnvironment.class);
+            mEnvironment = Dependency.get(NotificationEntryManager.KeyguardEnvironment.class);
         }
         return mEnvironment;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
deleted file mode 100644
index a0229d1..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
+++ /dev/null
@@ -1,500 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.collection;
-
-import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING;
-import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE;
-import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT;
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.Person;
-import android.service.notification.NotificationListenerService.Ranking;
-import android.service.notification.NotificationListenerService.RankingMap;
-import android.service.notification.SnoozeCriterion;
-import android.service.notification.StatusBarNotification;
-import android.util.ArrayMap;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dependency;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
-import com.android.systemui.statusbar.notification.logging.NotifEvent;
-import com.android.systemui.statusbar.notification.logging.NotifLog;
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Objects;
-
-import javax.inject.Inject;
-
-/**
- * The list of currently displaying notifications.
- */
-public class NotificationData {
-    private static final String TAG = "NotificationData";
-
-    private final NotificationFilter mNotificationFilter = Dependency.get(NotificationFilter.class);
-
-    /**
-     * These dependencies are late init-ed
-     */
-    private KeyguardEnvironment mEnvironment;
-    private NotificationMediaManager mMediaManager;
-
-    private HeadsUpManager mHeadsUpManager;
-
-    private final ArrayMap<String, NotificationEntry> mEntries = new ArrayMap<>();
-    private final ArrayList<NotificationEntry> mSortedAndFiltered = new ArrayList<>();
-
-    private final NotificationGroupManager mGroupManager =
-            Dependency.get(NotificationGroupManager.class);
-
-    private RankingMap mRankingMap;
-    private final Ranking mTmpRanking = new Ranking();
-    private final boolean mUsePeopleFiltering;
-    private final NotifLog mNotifLog;
-    private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
-
-    @Inject
-    public NotificationData(
-            NotificationSectionsFeatureManager sectionsFeatureManager,
-            NotifLog notifLog,
-            PeopleNotificationIdentifier peopleNotificationIdentifier) {
-        mUsePeopleFiltering = sectionsFeatureManager.isFilteringEnabled();
-        mNotifLog = notifLog;
-        mPeopleNotificationIdentifier = peopleNotificationIdentifier;
-    }
-
-    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
-        mHeadsUpManager = headsUpManager;
-    }
-
-    @VisibleForTesting
-    protected final Comparator<NotificationEntry> mRankingComparator =
-            new Comparator<NotificationEntry>() {
-        @Override
-        public int compare(NotificationEntry a, NotificationEntry b) {
-            final StatusBarNotification na = a.getSbn();
-            final StatusBarNotification nb = b.getSbn();
-            int aRank = getRank(a.getKey());
-            int bRank = getRank(b.getKey());
-
-            boolean aPeople = isPeopleNotification(a);
-            boolean bPeople = isPeopleNotification(b);
-
-            boolean aMedia = isImportantMedia(a);
-            boolean bMedia = isImportantMedia(b);
-
-            boolean aSystemMax = isSystemMax(a);
-            boolean bSystemMax = isSystemMax(b);
-
-            boolean aHeadsUp = a.isRowHeadsUp();
-            boolean bHeadsUp = b.isRowHeadsUp();
-
-            if (mUsePeopleFiltering && aPeople != bPeople) {
-                return aPeople ? -1 : 1;
-            } else if (aHeadsUp != bHeadsUp) {
-                return aHeadsUp ? -1 : 1;
-            } else if (aHeadsUp) {
-                // Provide consistent ranking with headsUpManager
-                return mHeadsUpManager.compare(a, b);
-            } else if (aMedia != bMedia) {
-                // Upsort current media notification.
-                return aMedia ? -1 : 1;
-            } else if (aSystemMax != bSystemMax) {
-                // Upsort PRIORITY_MAX system notifications
-                return aSystemMax ? -1 : 1;
-            } else if (a.isHighPriority() != b.isHighPriority()) {
-                return -1 * Boolean.compare(a.isHighPriority(), b.isHighPriority());
-            } else if (aRank != bRank) {
-                return aRank - bRank;
-            } else {
-                return Long.compare(nb.getNotification().when, na.getNotification().when);
-            }
-        }
-    };
-
-    private KeyguardEnvironment getEnvironment() {
-        if (mEnvironment == null) {
-            mEnvironment = Dependency.get(KeyguardEnvironment.class);
-        }
-        return mEnvironment;
-    }
-
-    private NotificationMediaManager getMediaManager() {
-        if (mMediaManager == null) {
-            mMediaManager = Dependency.get(NotificationMediaManager.class);
-        }
-        return mMediaManager;
-    }
-
-    /**
-     * Returns the sorted list of active notifications (depending on {@link KeyguardEnvironment}
-     *
-     * <p>
-     * This call doesn't update the list of active notifications. Call {@link #filterAndSort()}
-     * when the environment changes.
-     * <p>
-     * Don't hold on to or modify the returned list.
-     */
-    public ArrayList<NotificationEntry> getActiveNotifications() {
-        return mSortedAndFiltered;
-    }
-
-    public ArrayList<NotificationEntry> getNotificationsForCurrentUser() {
-        synchronized (mEntries) {
-            final int len = mEntries.size();
-            ArrayList<NotificationEntry> filteredForUser = new ArrayList<>(len);
-
-            for (int i = 0; i < len; i++) {
-                NotificationEntry entry = mEntries.valueAt(i);
-                final StatusBarNotification sbn = entry.getSbn();
-                if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
-                    continue;
-                }
-                filteredForUser.add(entry);
-            }
-            return filteredForUser;
-        }
-    }
-
-    public NotificationEntry get(String key) {
-        return mEntries.get(key);
-    }
-
-    public void add(NotificationEntry entry) {
-        synchronized (mEntries) {
-            mEntries.put(entry.getSbn().getKey(), entry);
-        }
-        mGroupManager.onEntryAdded(entry);
-
-        updateRankingAndSort(mRankingMap, "addEntry=" + entry.getSbn());
-    }
-
-    public NotificationEntry remove(String key, RankingMap ranking) {
-        NotificationEntry removed;
-        synchronized (mEntries) {
-            removed = mEntries.remove(key);
-        }
-        if (removed == null) return null;
-        mGroupManager.onEntryRemoved(removed);
-        updateRankingAndSort(ranking, "removeEntry=" + removed.getSbn());
-        return removed;
-    }
-
-    /** Updates the given notification entry with the provided ranking. */
-    public void update(
-            NotificationEntry entry,
-            RankingMap ranking,
-            StatusBarNotification notification,
-            String reason) {
-        updateRanking(ranking, reason);
-        final StatusBarNotification oldNotification = entry.getSbn();
-        entry.setSbn(notification);
-        mGroupManager.onEntryUpdated(entry, oldNotification);
-    }
-
-    /**
-     * Update ranking and trigger a re-sort
-     */
-    public void updateRanking(RankingMap ranking, String reason) {
-        updateRankingAndSort(ranking, reason);
-    }
-
-    /**
-     * Returns true if this notification should be displayed in the high-priority notifications
-     * section
-     */
-    public boolean isHighPriority(StatusBarNotification statusBarNotification) {
-        if (mRankingMap != null) {
-            getRanking(statusBarNotification.getKey(), mTmpRanking);
-            if (mTmpRanking.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT
-                    || hasHighPriorityCharacteristics(
-                            mTmpRanking.getChannel(), statusBarNotification)) {
-                return true;
-            }
-            if (mGroupManager.isSummaryOfGroup(statusBarNotification)) {
-                final ArrayList<NotificationEntry> logicalChildren =
-                        mGroupManager.getLogicalChildren(statusBarNotification);
-                for (NotificationEntry child : logicalChildren) {
-                    if (isHighPriority(child.getSbn())) {
-                        return true;
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    private boolean hasHighPriorityCharacteristics(NotificationChannel channel,
-            StatusBarNotification statusBarNotification) {
-
-        if (isImportantOngoing(statusBarNotification.getNotification())
-                || statusBarNotification.getNotification().hasMediaSession()
-                || hasPerson(statusBarNotification.getNotification())
-                || hasStyle(statusBarNotification.getNotification(),
-                Notification.MessagingStyle.class)) {
-            // Users who have long pressed and demoted to silent should not see the notification
-            // in the top section
-            if (channel != null && channel.hasUserSetImportance()) {
-                return false;
-            }
-            return true;
-        }
-
-        return false;
-    }
-
-    private boolean isImportantOngoing(Notification notification) {
-        return notification.isForegroundService()
-                && mTmpRanking.getImportance() >= NotificationManager.IMPORTANCE_LOW;
-    }
-
-    private boolean hasStyle(Notification notification, Class targetStyle) {
-        Class<? extends Notification.Style> style = notification.getNotificationStyle();
-        return targetStyle.equals(style);
-    }
-
-    private boolean hasPerson(Notification notification) {
-        // TODO: cache favorite and recent contacts to check contact affinity
-        ArrayList<Person> people = notification.extras != null
-                ? notification.extras.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST)
-                : new ArrayList<>();
-        return people != null && !people.isEmpty();
-    }
-
-    public boolean isAmbient(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.isAmbient();
-        }
-        return false;
-    }
-
-    public int getVisibilityOverride(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.getVisibilityOverride();
-        }
-        return Ranking.VISIBILITY_NO_OVERRIDE;
-    }
-
-    public List<SnoozeCriterion> getSnoozeCriteria(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.getSnoozeCriteria();
-        }
-        return null;
-    }
-
-    public NotificationChannel getChannel(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.getChannel();
-        }
-        return null;
-    }
-
-    public int getRank(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.getRank();
-        }
-        return 0;
-    }
-
-    private boolean isImportantMedia(NotificationEntry e) {
-        int importance = e.getRanking().getImportance();
-        boolean media = e.getKey().equals(getMediaManager().getMediaNotificationKey())
-                && importance > NotificationManager.IMPORTANCE_MIN;
-
-        return media;
-    }
-
-    private boolean isSystemMax(NotificationEntry e) {
-        int importance = e.getRanking().getImportance();
-        boolean sys = importance  >= NotificationManager.IMPORTANCE_HIGH
-                && isSystemNotification(e.getSbn());
-
-        return sys;
-    }
-
-    public boolean shouldHide(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.isSuspended();
-        }
-        return false;
-    }
-
-    private void updateRankingAndSort(RankingMap rankingMap, String reason) {
-        if (rankingMap != null) {
-            mRankingMap = rankingMap;
-            synchronized (mEntries) {
-                final int len = mEntries.size();
-                for (int i = 0; i < len; i++) {
-                    NotificationEntry entry = mEntries.valueAt(i);
-                    Ranking newRanking = new Ranking();
-                    if (!getRanking(entry.getKey(), newRanking)) {
-                        continue;
-                    }
-                    entry.setRanking(newRanking);
-
-                    final StatusBarNotification oldSbn = entry.getSbn().cloneLight();
-                    final String overrideGroupKey = newRanking.getOverrideGroupKey();
-                    if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) {
-                        entry.getSbn().setOverrideGroupKey(overrideGroupKey);
-                        mGroupManager.onEntryUpdated(entry, oldSbn);
-                    }
-                    entry.setIsHighPriority(isHighPriority(entry.getSbn()));
-                }
-            }
-        }
-        filterAndSort(reason);
-    }
-
-    /**
-     * Get the ranking from the current ranking map.
-     *
-     * @param key the key to look up
-     * @param outRanking the ranking to populate
-     *
-     * @return {@code true} if the ranking was properly obtained.
-     */
-    @VisibleForTesting
-    protected boolean getRanking(String key, Ranking outRanking) {
-        return mRankingMap.getRanking(key, outRanking);
-    }
-
-    // TODO: This should not be public. Instead the Environment should notify this class when
-    // anything changed, and this class should call back the UI so it updates itself.
-    /**
-     * Filters and sorts the list of notification entries
-     */
-    public void filterAndSort(String reason) {
-        mNotifLog.log(NotifEvent.FILTER_AND_SORT, reason);
-        mSortedAndFiltered.clear();
-
-        synchronized (mEntries) {
-            final int len = mEntries.size();
-            for (int i = 0; i < len; i++) {
-                NotificationEntry entry = mEntries.valueAt(i);
-
-                if (mNotificationFilter.shouldFilterOut(entry)) {
-                    continue;
-                }
-
-                mSortedAndFiltered.add(entry);
-            }
-        }
-
-        Collections.sort(mSortedAndFiltered, mRankingComparator);
-
-        int bucket = BUCKET_PEOPLE;
-        for (NotificationEntry e : mSortedAndFiltered) {
-            assignBucketForEntry(e);
-            if (e.getBucket() < bucket) {
-                android.util.Log.wtf(TAG, "Detected non-contiguous bucket!");
-            }
-            bucket = e.getBucket();
-        }
-    }
-
-    private void assignBucketForEntry(NotificationEntry e) {
-        boolean isHeadsUp = e.isRowHeadsUp();
-        boolean isMedia = isImportantMedia(e);
-        boolean isSystemMax = isSystemMax(e);
-
-        setBucket(e, isHeadsUp, isMedia, isSystemMax);
-    }
-
-    private void setBucket(
-            NotificationEntry e,
-            boolean isHeadsUp,
-            boolean isMedia,
-            boolean isSystemMax) {
-        if (mUsePeopleFiltering && isPeopleNotification(e)) {
-            e.setBucket(BUCKET_PEOPLE);
-        } else if (isHeadsUp || isMedia || isSystemMax || e.isHighPriority()) {
-            e.setBucket(BUCKET_ALERTING);
-        } else {
-            e.setBucket(BUCKET_SILENT);
-        }
-    }
-
-    private boolean isPeopleNotification(NotificationEntry e) {
-        return mPeopleNotificationIdentifier.isPeopleNotification(e.getSbn());
-    }
-
-    public void dump(PrintWriter pw, String indent) {
-        int filteredLen = mSortedAndFiltered.size();
-        pw.print(indent);
-        pw.println("active notifications: " + filteredLen);
-        int active;
-        for (active = 0; active < filteredLen; active++) {
-            NotificationEntry e = mSortedAndFiltered.get(active);
-            dumpEntry(pw, indent, active, e);
-        }
-        synchronized (mEntries) {
-            int totalLen = mEntries.size();
-            pw.print(indent);
-            pw.println("inactive notifications: " + (totalLen - active));
-            int inactiveCount = 0;
-            for (int i = 0; i < totalLen; i++) {
-                NotificationEntry entry = mEntries.valueAt(i);
-                if (!mSortedAndFiltered.contains(entry)) {
-                    dumpEntry(pw, indent, inactiveCount, entry);
-                    inactiveCount++;
-                }
-            }
-        }
-    }
-
-    private void dumpEntry(PrintWriter pw, String indent, int i, NotificationEntry e) {
-        getRanking(e.getKey(), mTmpRanking);
-        pw.print(indent);
-        pw.println("  [" + i + "] key=" + e.getKey() + " icon=" + e.icon);
-        StatusBarNotification n = e.getSbn();
-        pw.print(indent);
-        pw.println("      pkg=" + n.getPackageName() + " id=" + n.getId() + " importance="
-                + mTmpRanking.getImportance());
-        pw.print(indent);
-        pw.println("      notification=" + n.getNotification());
-    }
-
-    private static boolean isSystemNotification(StatusBarNotification sbn) {
-        String sbnPackage = sbn.getPackageName();
-        return "android".equals(sbnPackage) || "com.android.systemui".equals(sbnPackage);
-    }
-
-    /**
-     * Provides access to keyguard state and user settings dependent data.
-     */
-    public interface KeyguardEnvironment {
-        boolean isDeviceProvisioned();
-        boolean isNotificationForCurrentProfiles(StatusBarNotification sbn);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
new file mode 100644
index 0000000..8bce528
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import android.app.Notification
+import android.app.NotificationManager.IMPORTANCE_DEFAULT
+import android.app.NotificationManager.IMPORTANCE_HIGH
+import android.app.NotificationManager.IMPORTANCE_LOW
+import android.app.NotificationManager.IMPORTANCE_MIN
+import android.app.Person
+import android.service.notification.NotificationListenerService.Ranking
+import android.service.notification.NotificationListenerService.RankingMap
+import android.service.notification.StatusBarNotification
+import com.android.internal.annotations.VisibleForTesting
+
+import com.android.systemui.statusbar.NotificationMediaManager
+import com.android.systemui.statusbar.notification.NotificationFilter
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
+import com.android.systemui.statusbar.notification.logging.NotifEvent
+import com.android.systemui.statusbar.notification.logging.NotifLog
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT
+import com.android.systemui.statusbar.phone.NotificationGroupManager
+import com.android.systemui.statusbar.policy.HeadsUpManager
+
+import java.util.Objects
+import java.util.ArrayList
+
+import javax.inject.Inject
+
+import kotlin.Comparator
+
+import dagger.Lazy
+
+private const val TAG = "NotifRankingManager"
+
+/**
+ * NotificationRankingManager is responsible for holding on to the most recent [RankingMap], and
+ * updating SystemUI's set of [NotificationEntry]s with their own ranking. It also sorts and filters
+ * a set of entries (but retains none of them). We also set buckets on the entries here since
+ * bucketing is tied closely to sorting.
+ *
+ * For the curious: this class is one iteration closer to null of what used to be called
+ * NotificationData.java.
+ */
+open class NotificationRankingManager @Inject constructor(
+    private val mediaManagerLazy: Lazy<NotificationMediaManager>,
+    private val groupManager: NotificationGroupManager,
+    private val headsUpManager: HeadsUpManager,
+    private val notifFilter: NotificationFilter,
+    private val notifLog: NotifLog,
+    sectionsFeatureManager: NotificationSectionsFeatureManager
+) {
+
+    var rankingMap: RankingMap? = null
+        protected set
+    private val mediaManager by lazy {
+        mediaManagerLazy.get()
+    }
+    private val usePeopleFiltering: Boolean = sectionsFeatureManager.isFilteringEnabled()
+    private val rankingComparator: Comparator<NotificationEntry> = Comparator { a, b ->
+        val na = a.sbn
+        val nb = b.sbn
+        val aRank = a.ranking.rank
+        val bRank = b.ranking.rank
+
+        val aMedia = isImportantMedia(a)
+        val bMedia = isImportantMedia(b)
+
+        val aSystemMax = a.isSystemMax()
+        val bSystemMax = b.isSystemMax()
+
+        val aHeadsUp = a.isRowHeadsUp
+        val bHeadsUp = b.isRowHeadsUp
+
+        if (usePeopleFiltering && a.isPeopleNotification() != b.isPeopleNotification()) {
+            if (a.isPeopleNotification()) -1 else 1
+        } else if (aHeadsUp != bHeadsUp) {
+            if (aHeadsUp) -1 else 1
+        } else if (aHeadsUp) {
+            // Provide consistent ranking with headsUpManager
+            headsUpManager.compare(a, b)
+        } else if (aMedia != bMedia) {
+            // Upsort current media notification.
+            if (aMedia) -1 else 1
+        } else if (aSystemMax != bSystemMax) {
+            // Upsort PRIORITY_MAX system notifications
+            if (aSystemMax) -1 else 1
+        } else if (a.isHighPriority != b.isHighPriority) {
+            -1 * java.lang.Boolean.compare(a.isHighPriority, b.isHighPriority)
+        } else if (aRank != bRank) {
+            aRank - bRank
+        } else {
+            nb.notification.`when`.compareTo(na.notification.`when`)
+        }
+    }
+
+    private fun isImportantMedia(entry: NotificationEntry): Boolean {
+        val importance = entry.ranking.importance
+        return entry.key == mediaManager.mediaNotificationKey && importance > IMPORTANCE_MIN
+    }
+
+    @VisibleForTesting
+    protected fun isHighPriority(entry: NotificationEntry): Boolean {
+        if (entry.importance >= IMPORTANCE_DEFAULT ||
+                hasHighPriorityCharacteristics(entry)) {
+            return true
+        }
+
+        if (groupManager.isSummaryOfGroup(entry.sbn)) {
+            val logicalChildren = groupManager.getLogicalChildren(entry.sbn)
+            for (child in logicalChildren) {
+                if (isHighPriority(child)) {
+                    return true
+                }
+            }
+        }
+
+        return false
+    }
+
+    private fun hasHighPriorityCharacteristics(entry: NotificationEntry): Boolean {
+        val c = entry.channel
+        val n = entry.sbn.notification
+
+        if (((n.isForegroundService && entry.ranking.importance >= IMPORTANCE_LOW) ||
+            n.hasMediaSession() ||
+            n.hasPerson() ||
+            n.hasStyle(Notification.MessagingStyle::class.java))) {
+            // Users who have long pressed and demoted to silent should not see the notification
+            // in the top section
+            if (c != null && c.hasUserSetImportance()) {
+                return false
+            }
+
+            return true
+        }
+
+        return false
+    }
+
+    fun updateRanking(
+        newRankingMap: RankingMap?,
+        entries: Collection<NotificationEntry>,
+        reason: String
+    ): List<NotificationEntry> {
+        val eSeq = entries.asSequence()
+
+        // TODO: may not be ideal to guard on null here, but this code is implementing exactly what
+        // NotificationData used to do
+        if (newRankingMap != null) {
+            rankingMap = newRankingMap
+            updateRankingForEntries(eSeq)
+        }
+
+        val filtered: Sequence<NotificationEntry>
+        synchronized(this) {
+            filtered = filterAndSortLocked(eSeq, reason)
+        }
+
+        return filtered.toList()
+    }
+
+    /** Uses the [rankingComparator] to sort notifications which aren't filtered */
+    private fun filterAndSortLocked(
+        entries: Sequence<NotificationEntry>,
+        reason: String
+    ): Sequence<NotificationEntry> {
+        notifLog.log(NotifEvent.FILTER_AND_SORT, reason)
+
+        return entries.filter { !notifFilter.shouldFilterOut(it) }
+                .sortedWith(rankingComparator)
+                .map {
+                    assignBucketForEntry(it)
+                    it
+                }
+    }
+
+    private fun assignBucketForEntry(entry: NotificationEntry) {
+        val isHeadsUp = entry.isRowHeadsUp
+        val isMedia = isImportantMedia(entry)
+        val isSystemMax = entry.isSystemMax()
+        setBucket(entry, isHeadsUp, isMedia, isSystemMax)
+    }
+
+    private fun setBucket(
+        entry: NotificationEntry,
+        isHeadsUp: Boolean,
+        isMedia: Boolean,
+        isSystemMax: Boolean
+    ) {
+        if (usePeopleFiltering && entry.hasAssociatedPeople()) {
+            entry.bucket = BUCKET_PEOPLE
+        } else if (isHeadsUp || isMedia || isSystemMax || entry.isHighPriority) {
+            entry.bucket = BUCKET_ALERTING
+        } else {
+            entry.bucket = BUCKET_SILENT
+        }
+    }
+
+    private fun updateRankingForEntries(entries: Sequence<NotificationEntry>) {
+        rankingMap?.let { rankingMap ->
+            synchronized(entries) {
+                entries.forEach { entry ->
+                    val newRanking = Ranking()
+                    if (!rankingMap.getRanking(entry.key, newRanking)) {
+                        return@forEach
+                    }
+                    entry.ranking = newRanking
+
+                    val oldSbn = entry.sbn.cloneLight()
+                    val newOverrideGroupKey = newRanking.overrideGroupKey
+                    if (!Objects.equals(oldSbn.overrideGroupKey, newOverrideGroupKey)) {
+                        entry.sbn.overrideGroupKey = newOverrideGroupKey
+                        // TODO: notify group manager here?
+                        groupManager.onEntryUpdated(entry, oldSbn)
+                    }
+                    entry.setIsHighPriority(isHighPriority(entry))
+                }
+            }
+        }
+    }
+}
+
+// Convenience functions
+private fun NotificationEntry.isSystemMax(): Boolean {
+    return importance >= IMPORTANCE_HIGH && sbn.isSystemNotification()
+}
+
+private fun StatusBarNotification.isSystemNotification(): Boolean {
+    return "android" == packageName || "com.android.systemui" == packageName
+}
+
+private fun Notification.hasPerson(): Boolean {
+    val people: ArrayList<Person> =
+            (extras?.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST)) ?: ArrayList()
+    return people.isNotEmpty()
+}
+
+private fun Notification.hasStyle(targetStyleClass: Class<*>): Boolean {
+    return targetStyleClass == notificationStyle
+}
+
+private fun NotificationEntry.isPeopleNotification(): Boolean =
+        sbn.notification.hasStyle(Notification.MessagingStyle::class.java)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
index 52fd079..1c0a9d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
@@ -32,7 +32,6 @@
 import com.android.internal.util.NotificationMessagingUtil;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.UiOffloadThread;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationPresenter;
@@ -61,7 +60,6 @@
             Dependency.get(NotificationGroupManager.class);
     private final NotificationGutsManager mGutsManager =
             Dependency.get(NotificationGutsManager.class);
-    private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
     private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
             Dependency.get(NotificationInterruptionStateProvider.class);
 
@@ -81,16 +79,20 @@
     private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
     private BindRowCallback mBindRowCallback;
     private NotificationClicker mNotificationClicker;
-    private final NotificationLogger mNotificationLogger = Dependency.get(NotificationLogger.class);
+    private final NotificationLogger mNotificationLogger;
 
-    public NotificationRowBinderImpl(Context context, boolean allowLongPress,
+    public NotificationRowBinderImpl(
+            Context context,
+            boolean allowLongPress,
             KeyguardBypassController keyguardBypassController,
-            StatusBarStateController statusBarStateController) {
+            StatusBarStateController statusBarStateController,
+            NotificationLogger logger) {
         mContext = context;
         mMessagingUtil = new NotificationMessagingUtil(context);
         mAllowLongPress = allowLongPress;
         mKeyguardBypassController = keyguardBypassController;
         mStatusBarStateController = statusBarStateController;
+        mNotificationLogger = logger;
     }
 
     private NotificationRemoteInputManager getRemoteInputManager() {
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 90c5502..77ccf19 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
@@ -43,9 +43,9 @@
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 
 import javax.inject.Inject;
@@ -113,7 +113,7 @@
         public void run() {
             mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis();
 
-            // 1. Loop over mNotificationData entries:
+            // 1. Loop over active entries:
             //   A. Keep list of visible notifications.
             //   B. Keep list of previously hidden, now visible notifications.
             // 2. Compute no-longer visible notifications by removing currently
@@ -121,8 +121,7 @@
             //    notifications.
             // 3. Report newly visible and no-longer visible notifications.
             // 4. Keep currently visible notifications for next report.
-            ArrayList<NotificationEntry> activeNotifications = mEntryManager
-                    .getNotificationData().getActiveNotifications();
+            List<NotificationEntry> activeNotifications = mEntryManager.getVisibleNotifications();
             int N = activeNotifications.size();
             for (int i = 0; i < N; i++) {
                 NotificationEntry entry = activeNotifications.get(i);
@@ -403,7 +402,7 @@
      */
     public void onExpansionChanged(String key, boolean isUserAction, boolean isExpanded) {
         NotificationVisibility.NotificationLocation location =
-                getNotificationLocation(mEntryManager.getNotificationData().get(key));
+                getNotificationLocation(mEntryManager.getActiveNotificationUnfiltered(key));
         mExpansionStateLogger.onExpansionChanged(key, isUserAction, isExpanded, location);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 0d8e30e..462fa59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -696,8 +696,7 @@
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void updateFooter() {
         boolean showDismissView = mClearAllEnabled && hasActiveClearableNotifications(ROWS_ALL);
-        boolean showFooterView = (showDismissView ||
-                mEntryManager.getNotificationData().getActiveNotifications().size() != 0)
+        boolean showFooterView = (showDismissView || mEntryManager.hasActiveNotifications())
                 && mStatusBarState != StatusBarState.KEYGUARD
                 && !mRemoteInputManager.getController().isRemoteInputActive();
 
@@ -5787,11 +5786,6 @@
   }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public boolean hasActiveNotifications() {
-        return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
-    }
-
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void updateSpeedBumpIndex() {
         int speedBumpIndex = 0;
         int currentIndex = 0;
@@ -6400,7 +6394,7 @@
         @Override
         public boolean onDraggedDown(View startingChild, int dragLengthY) {
             if (mStatusBarState == StatusBarState.KEYGUARD
-                    && hasActiveNotifications()) {
+                    && mEntryManager.hasActiveNotifications()) {
                 mLockscreenGestureLogger.write(
                         MetricsEvent.ACTION_LS_SHADE,
                         (int) (dragLengthY / mDisplayMetrics.density),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
index 2c931ae..e763496 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
@@ -22,7 +22,7 @@
 
 import com.android.systemui.Dependency;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
 import javax.inject.Inject;
@@ -42,12 +42,12 @@
     public KeyguardEnvironmentImpl() {
     }
 
-    @Override  // NotificationData.KeyguardEnvironment
+    @Override  // NotificationEntryManager.KeyguardEnvironment
     public boolean isDeviceProvisioned() {
         return mDeviceProvisionedController.isDeviceProvisioned();
     }
 
-    @Override  // NotificationData.KeyguardEnvironment
+    @Override  // NotificationEntryManager.KeyguardEnvironment
     public boolean isNotificationForCurrentProfiles(StatusBarNotification n) {
         final int notificationUserId = n.getUserId();
         if (DEBUG && MULTIUSER_DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
index 93887a6..5703f06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
@@ -97,7 +97,7 @@
     }
 
     private boolean hasActiveNotifications() {
-        return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
+        return mEntryManager.hasActiveNotifications();
     }
 
     @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 1e10b6f..3554b54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -15,7 +15,6 @@
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.util.ContrastColorUtil;
 import com.android.settingslib.Utils;
-import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.plugins.DarkIconDispatcher;
@@ -26,7 +25,6 @@
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -48,7 +46,6 @@
     private static final long AOD_ICONS_APPEAR_DURATION = 200;
 
     private final ContrastColorUtil mContrastColorUtil;
-    private final NotificationEntryManager mEntryManager;
     private final Runnable mUpdateStatusBarIcons = this::updateStatusBarIcons;
     private final StatusBarStateController mStatusBarStateController;
     private final NotificationMediaManager mMediaManager;
@@ -91,7 +88,6 @@
         mStatusBar = statusBar;
         mContrastColorUtil = ContrastColorUtil.getInstance(context);
         mContext = context;
-        mEntryManager = Dependency.get(NotificationEntryManager.class);
         mStatusBarStateController = statusBarStateController;
         mStatusBarStateController.addCallback(this);
         mMediaManager = notificationMediaManager;
@@ -247,7 +243,7 @@
         if (hideCenteredIcon && isCenteredNotificationIcon && !entry.isRowHeadsUp()) {
             return false;
         }
-        if (mEntryManager.getNotificationData().isAmbient(entry.getKey()) && !showAmbient) {
+        if (entry.getRanking().isAmbient() && !showAmbient) {
             return false;
         }
         if (hideCurrentMedia && entry.getKey().equals(mMediaManager.getMediaNotificationKey())) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 6839fb4..8ebf574 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -3562,8 +3562,7 @@
 
     private void updateShowEmptyShadeView() {
         boolean showEmptyShadeView =
-                mBarState != StatusBarState.KEYGUARD &&
-                        mEntryManager.getNotificationData().getActiveNotifications().size() == 0;
+                mBarState != StatusBarState.KEYGUARD && !mEntryManager.hasActiveNotifications();
         showEmptyShadeView(showEmptyShadeView);
     }
 
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 0e1985d..adea8c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1228,7 +1228,8 @@
                         mContext,
                         mAllowNotificationLongPress,
                         mKeyguardBypassController,
-                        mStatusBarStateController);
+                        mStatusBarStateController,
+                        mNotificationLogger);
 
         mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel,
                 mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController,
@@ -2494,8 +2495,8 @@
         }
 
         if (DUMPTRUCK) {
-            synchronized (mEntryManager.getNotificationData()) {
-                mEntryManager.getNotificationData().dump(pw, "  ");
+            synchronized (mEntryManager) {
+                mEntryManager.dump(pw, "  ");
             }
 
             if (false) {
@@ -2753,11 +2754,7 @@
     };
 
     public void resetUserExpandedStates() {
-        ArrayList<NotificationEntry> activeNotifications = mEntryManager.getNotificationData()
-                .getActiveNotifications();
-        final int notificationCount = activeNotifications.size();
-        for (int i = 0; i < notificationCount; i++) {
-            NotificationEntry entry = activeNotifications.get(i);
+        for (NotificationEntry entry : mEntryManager.getVisibleNotifications()) {
             entry.resetUserExpansion();
         }
     }
@@ -2857,8 +2854,7 @@
         try {
             // consider the transition from peek to expanded to be a panel open,
             // but not one that clears notification effects.
-            int notificationLoad = mEntryManager.getNotificationData()
-                    .getActiveNotifications().size();
+            int notificationLoad = mEntryManager.getActiveNotificationsCount();
             mBarService.onPanelRevealed(false, notificationLoad);
         } catch (RemoteException ex) {
             // Won't fail unless the world has ended.
@@ -2876,8 +2872,7 @@
                     !mPresenter.isPresenterFullyCollapsed() &&
                             (mState == StatusBarState.SHADE
                                     || mState == StatusBarState.SHADE_LOCKED);
-            int notificationLoad = mEntryManager.getNotificationData().getActiveNotifications()
-                    .size();
+            int notificationLoad = mEntryManager.getActiveNotificationsCount();
             if (pinnedHeadsUp && mPresenter.isPresenterFullyCollapsed()) {
                 notificationLoad = 1;
             }
@@ -3862,10 +3857,6 @@
         mScreenPinningRequest.showPrompt(taskId, allowCancel);
     }
 
-    public boolean hasActiveNotifications() {
-        return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
-    }
-
     @Override
     public void appTransitionCancelled(int displayId) {
         if (displayId == 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 64a45e1..b340813 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -40,6 +40,7 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.view.RemoteAnimationAdapter;
+import android.view.View;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.statusbar.IStatusBarService;
@@ -75,7 +76,7 @@
  */
 public class StatusBarNotificationActivityStarter implements NotificationActivityStarter {
 
-    private static final String TAG = "NotificationClickHandler";
+    private static final String TAG = "NotifActivityStarter";
     protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final AssistManager mAssistManager;
@@ -197,8 +198,6 @@
             return;
         }
 
-        final String notificationKey = sbn.getKey();
-
         boolean isActivityIntent = intent != null && intent.isActivity() && !isBubble;
         final boolean afterKeyguardGone = isActivityIntent
                 && mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(),
@@ -209,7 +208,7 @@
                 mLockscreenUserManager.getCurrentUserId());
         ActivityStarter.OnDismissAction postKeyguardAction =
                 () -> handleNotificationClickAfterKeyguardDismissed(
-                        sbn, row, controller, intent, notificationKey,
+                        sbn, row, controller, intent,
                         isActivityIntent, wasOccluded, showOverLockscreen);
         if (showOverLockscreen) {
             mIsCollapsingToShowActivityOverLockscreen = true;
@@ -225,12 +224,11 @@
             ExpandableNotificationRow row,
             RemoteInputController controller,
             PendingIntent intent,
-            String notificationKey,
             boolean isActivityIntent,
             boolean wasOccluded,
             boolean showOverLockscreen) {
         // TODO: Some of this code may be able to move to NotificationEntryManager.
-        if (mHeadsUpManager != null && mHeadsUpManager.isAlerting(notificationKey)) {
+        if (mHeadsUpManager != null && mHeadsUpManager.isAlerting(sbn.getKey())) {
             // Release the HUN notification to the shade.
 
             if (mPresenter.isPresenterFullyCollapsed()) {
@@ -252,7 +250,7 @@
         }
         final StatusBarNotification parentToCancelFinal = parentToCancel;
         final Runnable runnable = () -> handleNotificationClickAfterPanelCollapsed(
-                sbn, row, controller, intent, notificationKey,
+                sbn, row, controller, intent,
                 isActivityIntent, wasOccluded, parentToCancelFinal);
 
         if (showOverLockscreen) {
@@ -273,10 +271,10 @@
             ExpandableNotificationRow row,
             RemoteInputController controller,
             PendingIntent intent,
-            String notificationKey,
             boolean isActivityIntent,
             boolean wasOccluded,
             StatusBarNotification parentToCancelFinal) {
+        String notificationKey = sbn.getKey();
         try {
             // The intent we are sending is for the application, which
             // won't have permission to immediately start an activity after
@@ -310,7 +308,7 @@
         if (!TextUtils.isEmpty(entry.remoteInputText)) {
             remoteInputText = entry.remoteInputText;
         }
-        if (!TextUtils.isEmpty(remoteInputText) && !controller.isSpinning(entry.getKey())) {
+        if (!TextUtils.isEmpty(remoteInputText) && !controller.isSpinning(notificationKey)) {
             fillInIntent = new Intent().putExtra(Notification.EXTRA_REMOTE_INPUT_DRAFT,
                     remoteInputText.toString());
         }
@@ -326,12 +324,10 @@
             collapseOnMainThread();
         }
 
-        final int count =
-                mEntryManager.getNotificationData().getActiveNotifications().size();
-        final int rank = mEntryManager.getNotificationData().getRank(notificationKey);
+        final int count = mEntryManager.getActiveNotificationsCount();
+        final int rank = entry.getRanking().getRank();
         NotificationVisibility.NotificationLocation location =
-                NotificationLogger.getNotificationLocation(
-                        mEntryManager.getNotificationData().get(notificationKey));
+                NotificationLogger.getNotificationLocation(entry);
         final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey,
                 rank, count, true, location);
         try {
@@ -363,7 +359,7 @@
     }
 
     private void startNotificationIntent(PendingIntent intent, Intent fillInIntent,
-            ExpandableNotificationRow row, boolean wasOccluded, boolean isActivityIntent) {
+            View row, boolean wasOccluded, boolean isActivityIntent) {
         RemoteAnimationAdapter adapter = mActivityLaunchAnimator.getLaunchAnimation(row,
                 wasOccluded);
         try {
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 38ff862..30e26e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -75,7 +75,7 @@
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
-import java.util.ArrayList;
+import java.util.List;
 
 public class StatusBarNotificationPresenter implements NotificationPresenter,
         ConfigurationController.ConfigurationListener,
@@ -252,8 +252,8 @@
     }
 
     private void updateNotificationOnUiModeChanged() {
-        ArrayList<NotificationEntry> userNotifications
-                = mEntryManager.getNotificationData().getNotificationsForCurrentUser();
+        List<NotificationEntry> userNotifications =
+                mEntryManager.getActiveNotificationsForCurrentUser();
         for (int i = 0; i < userNotifications.size(); i++) {
             NotificationEntry entry = userNotifications.get(i);
             ExpandableNotificationRow row = entry.getRow();
@@ -264,8 +264,8 @@
     }
 
     private void updateNotificationsOnDensityOrFontScaleChanged() {
-        ArrayList<NotificationEntry> userNotifications =
-                mEntryManager.getNotificationData().getNotificationsForCurrentUser();
+        List<NotificationEntry> userNotifications =
+                mEntryManager.getActiveNotificationsForCurrentUser();
         for (int i = 0; i < userNotifications.size(); i++) {
             NotificationEntry entry = userNotifications.get(i);
             entry.onDensityOrFontScaleChanged();
@@ -326,7 +326,7 @@
     }
 
     public boolean hasActiveNotifications() {
-        return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
+        return mEntryManager.hasActiveNotifications();
     }
 
     public boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn) {
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java
index 49ada1a..a60ca62 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java
@@ -158,7 +158,7 @@
 
         String demoTime = "1010"; // 10:10, a classic choice of horologists
         try {
-            String[] versionParts = android.os.Build.VERSION.RELEASE_OR_CODENAME.split("\\.");
+            String[] versionParts = android.os.Build.VERSION.RELEASE.split("\\.");
             int majorVersion = Integer.valueOf(versionParts[0]);
             demoTime = String.format("%02d00", majorVersion % 24);
         } catch (IllegalArgumentException ex) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index e63b6d66..02a3766 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -102,7 +102,7 @@
     public void testAppOps_appOpAddedToForegroundNotif() {
         // GIVEN a notification associated with a foreground service
         NotificationEntry entry = addFgEntry();
-        when(mEntryManager.getPendingOrCurrentNotif(entry.getKey())).thenReturn(entry);
+        when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(entry);
 
         // WHEN we are notified of a new app op for this notification
         mFsc.onAppOpChanged(
@@ -123,7 +123,7 @@
         // GIVEN a foreground service associated notification that already has the correct app op
         NotificationEntry entry = addFgEntry();
         entry.mActiveAppOps.add(AppOpsManager.OP_CAMERA);
-        when(mEntryManager.getPendingOrCurrentNotif(entry.getKey())).thenReturn(entry);
+        when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(entry);
 
         // WHEN we are notified of the same app op for this notification
         mFsc.onAppOpChanged(
@@ -143,7 +143,7 @@
     public void testAppOps_appOpNotAddedToUnrelatedNotif() {
         // GIVEN no notification entries correspond to the newly updated appOp
         NotificationEntry entry = addFgEntry();
-        when(mEntryManager.getPendingOrCurrentNotif(entry.getKey())).thenReturn(null);
+        when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(null);
 
         // WHEN a new app op is detected
         mFsc.onAppOpChanged(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java
index fbb8e0c..f832b7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java
@@ -65,7 +65,7 @@
 
     @Mock private AssistUtils mMockAssistUtils;
     @Mock private Handler mMockHandler;
-    @Mock private PhenotypeHelper mMockPhenotypeHelper;
+    @Mock private DeviceConfigHelper mMockDeviceConfigHelper;
     @Mock private AssistHandleOffBehavior mMockOffBehavior;
     @Mock private AssistHandleLikeHomeBehavior mMockLikeHomeBehavior;
     @Mock private AssistHandleReminderExpBehavior mMockReminderExpBehavior;
@@ -97,7 +97,7 @@
                         mMockAssistUtils,
                         mMockHandler,
                         () -> mMockAssistHandleViewController,
-                        mMockPhenotypeHelper,
+                        mMockDeviceConfigHelper,
                         behaviorMap,
                         mMockNavigationModeController,
                         mMockDumpController);
@@ -216,7 +216,7 @@
     public void showAndGo_doesNothingIfRecentlyHidden() {
         // Arrange
         when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME);
-        when(mMockPhenotypeHelper.getLong(
+        when(mMockDeviceConfigHelper.getLong(
                 eq(SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS),
                 anyLong())).thenReturn(10000L);
         mAssistHandleBehaviorController.showAndGo();
@@ -297,7 +297,7 @@
     public void showAndGoDelayed_doesNothingIfRecentlyHidden() {
         // Arrange
         when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME);
-        when(mMockPhenotypeHelper.getLong(
+        when(mMockDeviceConfigHelper.getLong(
                 eq(SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS),
                 anyLong())).thenReturn(10000L);
         mAssistHandleBehaviorController.showAndGo();
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 a7c0204..8c9f759 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -72,7 +72,6 @@
 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.NotificationData;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.phone.DozeParameters;
@@ -141,8 +140,6 @@
     private ExpandableNotificationRow mNonBubbleNotifRow;
 
     @Mock
-    private NotificationData mNotificationData;
-    @Mock
     private BubbleController.BubbleStateChangeListener mBubbleStateChangeListener;
     @Mock
     private BubbleController.BubbleExpandListener mBubbleExpandListener;
@@ -183,10 +180,9 @@
         mNonBubbleNotifRow = mNotificationTestHelper.createRow();
 
         // Return non-null notification data from the NEM
-        when(mNotificationEntryManager.getNotificationData()).thenReturn(mNotificationData);
-        when(mNotificationData.get(mRow.getEntry().getKey())).thenReturn(mRow.getEntry());
-        when(mNotificationData.getChannel(mRow.getEntry().getKey())).thenReturn(
-                mRow.getEntry().getChannel());
+        when(mNotificationEntryManager
+                .getActiveNotificationUnfiltered(mRow.getEntry().getKey())).thenReturn(
+                mRow.getEntry());
 
         mZenModeConfig.suppressedVisualEffects = 0;
         when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
index 86869bd..2f53a01 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -37,7 +37,6 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -54,7 +53,6 @@
 
     @Mock private NotificationPresenter mPresenter;
     @Mock private NotificationListenerService.RankingMap mRanking;
-    @Mock private NotificationData mNotificationData;
 
     // Dependency mocks:
     @Mock private NotificationEntryManager mEntryManager;
@@ -74,8 +72,6 @@
                 new Handler(TestableLooper.get(this).getLooper()));
         mContext.addMockSystemService(NotificationManager.class, mNotificationManager);
 
-        when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
-
         mListener = new NotificationListener(mContext);
         mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
                 new Notification(), UserHandle.CURRENT, null, 0);
@@ -90,7 +86,7 @@
 
     @Test
     public void testNotificationUpdateCallsUpdateNotification() {
-        when(mNotificationData.get(mSbn.getKey()))
+        when(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()))
                 .thenReturn(
                         new NotificationEntryBuilder()
                                 .setSbn(mSbn)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 548f7a8..d54e24ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar;
 
+import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.content.Intent.ACTION_USER_SWITCHED;
 import static android.provider.Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL;
 
@@ -24,7 +25,6 @@
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
-import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -49,7 +49,6 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -72,7 +71,6 @@
 
     // Dependency mocks:
     @Mock private NotificationEntryManager mEntryManager;
-    @Mock private NotificationData mNotificationData;
     @Mock private DeviceProvisionedController mDeviceProvisionedController;
     @Mock private StatusBarKeyguardViewManager mKeyguardViewManager;
     @Mock private BroadcastDispatcher mBroadcastDispatcher;
@@ -93,7 +91,6 @@
 
         when(mUserManager.getProfiles(mCurrentUserId)).thenReturn(Lists.newArrayList(
                 new UserInfo(mCurrentUserId, "", 0), new UserInfo(mCurrentUserId + 1, "", 0)));
-        when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
         mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
                 Handler.createAsync(Looper.myLooper()));
 
@@ -170,9 +167,10 @@
                 NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
         Settings.Secure.putInt(mContext.getContentResolver(),
                 Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1);
-        when(mNotificationData.isHighPriority(any())).thenReturn(false);
 
-        NotificationEntry entry = new NotificationEntryBuilder().build();
+        NotificationEntry entry = new NotificationEntryBuilder()
+                .setImportance(IMPORTANCE_LOW)
+                .build();
         entry.setBucket(BUCKET_SILENT);
 
         assertTrue(mLockscreenUserManager.shouldShowOnKeyguard(entry));
@@ -186,10 +184,12 @@
                 NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
         Settings.Secure.putInt(mContext.getContentResolver(),
                 Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
-        when(mNotificationData.isHighPriority(any())).thenReturn(false);
 
-        NotificationEntry entry = new NotificationEntryBuilder().build();
+        NotificationEntry entry = new NotificationEntryBuilder()
+                .setImportance(IMPORTANCE_LOW)
+                .build();
         entry.setBucket(BUCKET_SILENT);
+        entry.setIsHighPriority(true);
         assertFalse(mLockscreenUserManager.shouldShowOnKeyguard(entry));
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index 90bd0e9..88546b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -26,7 +26,6 @@
 
 import android.annotation.Nullable;
 import android.app.ActivityManager;
-import android.app.Instrumentation;
 import android.app.Notification;
 import android.app.Notification.BubbleMetadata;
 import android.app.NotificationChannel;
@@ -40,8 +39,6 @@
 import android.view.LayoutInflater;
 import android.widget.RemoteViews;
 
-import androidx.test.InstrumentationRegistry;
-
 import com.android.systemui.TestableDependency;
 import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.bubbles.BubblesTestActivity;
@@ -72,7 +69,6 @@
     private static final String GROUP_KEY = "gruKey";
 
     private final Context mContext;
-    private final Instrumentation mInstrumentation;
     private int mId;
     private final NotificationGroupManager mGroupManager;
     private ExpandableNotificationRow mRow;
@@ -83,7 +79,7 @@
         dependency.injectMockDependency(NotificationMediaManager.class);
         dependency.injectMockDependency(BubbleController.class);
         dependency.injectMockDependency(StatusBarWindowController.class);
-        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        dependency.injectMockDependency(SmartReplyController.class);
         StatusBarStateController stateController = mock(StatusBarStateController.class);
         mGroupManager = new NotificationGroupManager(stateController);
         mHeadsUpManager = new HeadsUpManagerPhone(mContext, stateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 18649bf..99c94ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -46,7 +46,6 @@
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -73,7 +72,6 @@
 @TestableLooper.RunWithLooper
 public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
     @Mock private NotificationPresenter mPresenter;
-    @Mock private NotificationData mNotificationData;
     @Spy private FakeListContainer mListContainer = new FakeListContainer();
 
     // Dependency mocks:
@@ -105,8 +103,6 @@
 
         mHelper = new NotificationTestHelper(mContext, mDependency);
 
-        when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
-
         mViewHierarchyManager = new NotificationViewHierarchyManager(mContext,
                 mHandler, mLockscreenUserManager, mGroupManager, mVisualStabilityManager,
                 mock(StatusBarStateControllerImpl.class), mEntryManager,
@@ -139,7 +135,7 @@
         mListContainer.addContainerView(entry0.getRow());
         mListContainer.addContainerView(entry1.getRow());
         mListContainer.addContainerView(entry2.getRow());
-        when(mNotificationData.getActiveNotifications()).thenReturn(
+        when(mEntryManager.getVisibleNotifications()).thenReturn(
                 Lists.newArrayList(entry0, entry1, entry2));
 
         // Set up group manager to report that they should be bundled now.
@@ -168,7 +164,7 @@
 
         // Set up the prior state to look like one top level notification.
         mListContainer.addContainerView(entry0.getRow());
-        when(mNotificationData.getActiveNotifications()).thenReturn(
+        when(mEntryManager.getVisibleNotifications()).thenReturn(
                 Lists.newArrayList(entry0, entry1, entry2));
 
         // Set up group manager to report that they should not be bundled now.
@@ -197,7 +193,7 @@
 
         // Set up the prior state to look like a top level notification.
         mListContainer.addContainerView(entry0.getRow());
-        when(mNotificationData.getActiveNotifications()).thenReturn(
+        when(mEntryManager.getVisibleNotifications()).thenReturn(
                 Lists.newArrayList(entry0, entry1));
 
         // Set up group manager to report a suppressed summary now.
@@ -219,7 +215,7 @@
     public void testUpdateNotificationViews_appOps() throws Exception {
         NotificationEntry entry0 = createEntry();
         entry0.setRow(spy(entry0.getRow()));
-        when(mNotificationData.getActiveNotifications()).thenReturn(
+        when(mEntryManager.getVisibleNotifications()).thenReturn(
                 Lists.newArrayList(entry0));
         mListContainer.addContainerView(entry0.getRow());
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 86ef6e8..a98945f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.notification;
 
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.service.notification.NotificationListenerService.REASON_CANCEL;
 
 import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
@@ -28,7 +29,6 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
@@ -40,14 +40,15 @@
 
 import android.app.ActivityManager;
 import android.app.Notification;
-import android.app.NotificationManager;
+import android.app.NotificationChannel;
 import android.app.PendingIntent;
 import android.content.Intent;
 import android.graphics.drawable.Icon;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.UserHandle;
-import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.StatusBarNotification;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -73,18 +74,18 @@
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationRemoveInterceptor;
+import com.android.systemui.statusbar.RankingBuilder;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
-import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
 import com.android.systemui.statusbar.notification.collection.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
 import com.android.systemui.statusbar.notification.logging.NotifLog;
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.row.RowInflaterTask;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -93,6 +94,7 @@
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.util.Assert;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -104,13 +106,14 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper()
 public class NotificationEntryManagerTest extends SysuiTestCase {
     private static final String TEST_PACKAGE_NAME = "test";
     private static final int TEST_UID = 0;
@@ -123,7 +126,7 @@
     @Mock private NotificationRemoveInterceptor mRemoveInterceptor;
     @Mock private NotificationRowBinderImpl.BindRowCallback mBindCallback;
     @Mock private HeadsUpManager mHeadsUpManager;
-    @Mock private NotificationListenerService.RankingMap mRankingMap;
+    @Mock private RankingMap mRankingMap;
     @Mock private RemoteInputController mRemoteInputController;
 
     // Dependency mocks:
@@ -139,6 +142,7 @@
     @Mock private SmartReplyController mSmartReplyController;
     @Mock private RowInflaterTask mAsyncInflationTask;
     @Mock private NotificationRowBinder mMockedRowBinder;
+    @Mock private NotifLog mNotifLog;
 
     private int mId;
     private NotificationEntry mEntry;
@@ -146,43 +150,9 @@
     private TestableNotificationEntryManager mEntryManager;
     private CountDownLatch mCountDownLatch;
 
-    private class TestableNotificationEntryManager extends NotificationEntryManager {
-        private final CountDownLatch mCountDownLatch;
-
-        TestableNotificationEntryManager() {
-            super(
-                    new NotificationData(
-                            mock(NotificationSectionsFeatureManager.class),
-                            mock(NotifLog.class),
-                            mock(PeopleNotificationIdentifier.class)),
-                    mock(NotifLog.class));
-            mCountDownLatch = new CountDownLatch(1);
-        }
-
-        public void setNotificationData(NotificationData data) {
-            mNotificationData = data;
-        }
-
-        @Override
-        public void onAsyncInflationFinished(NotificationEntry entry,
-                @InflationFlag int inflatedFlags) {
-            super.onAsyncInflationFinished(entry, inflatedFlags);
-
-            mCountDownLatch.countDown();
-        }
-
-        public CountDownLatch getCountDownLatch() {
-            return mCountDownLatch;
-        }
-
-        public ArrayList<NotificationLifetimeExtender> getLifetimeExtenders() {
-            return mNotificationLifetimeExtenders;
-        }
-    }
-
     private void setUserSentiment(String key, int sentiment) {
         doAnswer(invocationOnMock -> {
-            NotificationListenerService.Ranking ranking = (NotificationListenerService.Ranking)
+            Ranking ranking = (Ranking)
                     invocationOnMock.getArguments()[1];
             ranking.populate(
                     key,
@@ -190,16 +160,16 @@
                     false,
                     0,
                     0,
-                    NotificationManager.IMPORTANCE_DEFAULT,
+                    IMPORTANCE_DEFAULT,
                     null, null,
                     null, null, null, true, sentiment, false, -1, false, null, null, false, false);
             return true;
-        }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
+        }).when(mRankingMap).getRanking(eq(key), any(Ranking.class));
     }
 
     private void setSmartActions(String key, ArrayList<Notification.Action> smartActions) {
         doAnswer(invocationOnMock -> {
-            NotificationListenerService.Ranking ranking = (NotificationListenerService.Ranking)
+            Ranking ranking = (Ranking)
                     invocationOnMock.getArguments()[1];
             ranking.populate(
                     key,
@@ -207,13 +177,13 @@
                     false,
                     0,
                     0,
-                    NotificationManager.IMPORTANCE_DEFAULT,
+                    IMPORTANCE_DEFAULT,
                     null, null,
                     null, null, null, true,
-                    NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL, false, -1,
+                    Ranking.USER_SENTIMENT_NEUTRAL, false, -1,
                     false, smartActions, null, false, false);
             return true;
-        }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
+        }).when(mRankingMap).getRanking(eq(key), any(Ranking.class));
     }
 
     @Before
@@ -249,7 +219,18 @@
 
         mEntry.expandedIcon = mock(StatusBarIconView.class);
 
-        mEntryManager = new TestableNotificationEntryManager();
+        mEntryManager = new TestableNotificationEntryManager(
+                mNotifLog,
+                mGroupManager,
+                new NotificationRankingManager(
+                        () -> mock(NotificationMediaManager.class),
+                        mGroupManager,
+                        mHeadsUpManager,
+                        mock(NotificationFilter.class),
+                        mNotifLog,
+                        mock(NotificationSectionsFeatureManager.class)),
+                mEnvironment
+        );
         Dependency.get(InitController.class).executePostInitTasks();
         mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mHeadsUpManager);
         mEntryManager.addNotificationEntryListener(mEntryListener);
@@ -258,19 +239,19 @@
         NotificationRowBinderImpl notificationRowBinder =
                 new NotificationRowBinderImpl(mContext, true, /* allowLongPress */
                         mock(KeyguardBypassController.class),
-                        mock(StatusBarStateController.class));
+                        mock(StatusBarStateController.class),
+                        mock(NotificationLogger.class));
         notificationRowBinder.setUpWithPresenter(
                 mPresenter, mListContainer, mHeadsUpManager, mEntryManager, mBindCallback);
         notificationRowBinder.setNotificationClicker(mock(NotificationClicker.class));
         mEntryManager.setRowBinder(notificationRowBinder);
 
         setUserSentiment(
-                mEntry.getKey(), NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
+                mEntry.getKey(), Ranking.USER_SENTIMENT_NEUTRAL);
     }
 
     @Test
     public void testAddNotification() throws Exception {
-        com.android.systemui.util.Assert.isNotMainThread();
         TestableLooper.get(this).processAllMessages();
 
         doAnswer(invocation -> {
@@ -299,21 +280,20 @@
         verify(mEntryListener).onNotificationAdded(entry);
         verify(mPresenter).updateNotificationViews();
 
-        assertEquals(mEntryManager.getNotificationData().get(mSbn.getKey()), entry);
+        assertEquals(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()), entry);
         assertNotNull(entry.getRow());
         assertEquals(mEntry.getUserSentiment(),
-                NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
+                Ranking.USER_SENTIMENT_NEUTRAL);
     }
 
     @Test
     public void testUpdateNotification() throws Exception {
-        com.android.systemui.util.Assert.isNotMainThread();
         TestableLooper.get(this).processAllMessages();
 
-        mEntryManager.getNotificationData().add(mEntry);
+        mEntryManager.addActiveNotificationForTest(mEntry);
 
         setUserSentiment(
-                mEntry.getKey(), NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
+                mEntry.getKey(), Ranking.USER_SENTIMENT_NEGATIVE);
 
         mEntryManager.updateNotification(mSbn, mRankingMap);
         TestableLooper.get(this).processMessages(1);
@@ -327,19 +307,15 @@
         verify(mEntryListener).onPostEntryUpdated(mEntry);
 
         assertNotNull(mEntry.getRow());
-        assertEquals(mEntry.getUserSentiment(),
-                NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
+        assertEquals(Ranking.USER_SENTIMENT_NEGATIVE,
+                mEntry.getUserSentiment());
     }
 
     @Test
     public void testUpdateNotification_prePostEntryOrder() throws Exception {
-        com.android.systemui.util.Assert.isNotMainThread();
         TestableLooper.get(this).processAllMessages();
 
-        NotificationData notifData = mock(NotificationData.class);
-        when(notifData.get(mEntry.getKey())).thenReturn(mEntry);
-
-        mEntryManager.setNotificationData(notifData);
+        mEntryManager.addActiveNotificationForTest(mEntry);
 
         mEntryManager.updateNotification(mSbn, mRankingMap);
         TestableLooper.get(this).processMessages(1);
@@ -349,9 +325,8 @@
         verify(mEntryListener, never()).onInflationError(any(), any());
 
         // Ensure that update callbacks happen in correct order
-        InOrder order = inOrder(mEntryListener, notifData, mPresenter, mEntryListener);
+        InOrder order = inOrder(mEntryListener, mPresenter, mEntryListener);
         order.verify(mEntryListener).onPreEntryUpdated(mEntry);
-        order.verify(notifData).filterAndSort(anyString());
         order.verify(mPresenter).updateNotificationViews();
         order.verify(mEntryListener).onPostEntryUpdated(mEntry);
 
@@ -359,11 +334,12 @@
     }
 
     @Test
-    public void testRemoveNotification() throws Exception {
-        com.android.systemui.util.Assert.isNotMainThread();
+    public void testRemoveNotification() {
+        // Row inflation happens off thread, so pretend that this test looper is main
+        Assert.sMainLooper = TestableLooper.get(this).getLooper();
 
         mEntry.setRow(mRow);
-        mEntryManager.getNotificationData().add(mEntry);
+        mEntryManager.addActiveNotificationForTest(mEntry);
 
         mEntryManager.removeNotification(mSbn.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
 
@@ -374,12 +350,11 @@
                 eq(mEntry), any(), eq(false) /* removedByUser */);
         verify(mRow).setRemoved();
 
-        assertNull(mEntryManager.getNotificationData().get(mSbn.getKey()));
+        assertNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
     }
 
     @Test
     public void testRemoveNotification_onEntryRemoveNotFiredIfEntryDoesntExist() {
-        com.android.systemui.util.Assert.isNotMainThread();
 
         mEntryManager.removeNotification("not_a_real_key", mRankingMap, UNDEFINED_DISMISS_REASON);
 
@@ -388,8 +363,7 @@
     }
 
     @Test
-    public void testRemoveNotification_whilePending() throws InterruptedException {
-        com.android.systemui.util.Assert.isNotMainThread();
+    public void testRemoveNotification_whilePending() {
 
         mEntryManager.setRowBinder(mMockedRowBinder);
 
@@ -408,7 +382,7 @@
 
         mEntry.setRow(mRow);
         mEntry.setInflationTask(mAsyncInflationTask);
-        mEntryManager.getNotificationData().add(mEntry);
+        mEntryManager.addActiveNotificationForTest(mEntry);
         setSmartActions(mEntry.getKey(), new ArrayList<>(Arrays.asList(createAction())));
 
         mEntryManager.updateNotificationRanking(mRankingMap);
@@ -424,7 +398,7 @@
         when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
 
         mEntry.setRow(mRow);
-        mEntryManager.getNotificationData().add(mEntry);
+        mEntryManager.addActiveNotificationForTest(mEntry);
         setSmartActions(mEntry.getKey(), null);
 
         mEntryManager.updateNotificationRanking(mRankingMap);
@@ -438,7 +412,7 @@
         when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
 
         mEntry.setRow(null);
-        mEntryManager.getNotificationData().add(mEntry);
+        mEntryManager.addActiveNotificationForTest(mEntry);
         setSmartActions(mEntry.getKey(), new ArrayList<>(Arrays.asList(createAction())));
 
         mEntryManager.updateNotificationRanking(mRankingMap);
@@ -466,7 +440,7 @@
     public void testLifetimeExtenders_ifNotificationIsRetainedItIsntRemoved() {
         // GIVEN an entry manager with a notification
         mEntryManager.setRowBinder(mMockedRowBinder);
-        mEntryManager.getNotificationData().add(mEntry);
+        mEntryManager.addActiveNotificationForTest(mEntry);
 
         // GIVEN a lifetime extender that always tries to extend lifetime
         NotificationLifetimeExtender extender = mock(NotificationLifetimeExtender.class);
@@ -479,15 +453,18 @@
         // THEN the extender is asked to manage the lifetime
         verify(extender).setShouldManageLifetime(mEntry, true);
         // THEN the notification is retained
-        assertNotNull(mEntryManager.getNotificationData().get(mSbn.getKey()));
+        assertNotNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
         verify(mEntryListener, never()).onEntryRemoved(eq(mEntry), any(), eq(false));
     }
 
     @Test
     public void testLifetimeExtenders_whenRetentionEndsNotificationIsRemoved() {
+        // Row inflation happens off thread, so pretend that this test looper is main
+        Assert.sMainLooper = TestableLooper.get(this).getLooper();
+
         // GIVEN an entry manager with a notification whose life has been extended
         mEntryManager.setRowBinder(mMockedRowBinder);
-        mEntryManager.getNotificationData().add(mEntry);
+        mEntryManager.addActiveNotificationForTest(mEntry);
         final FakeNotificationLifetimeExtender extender = new FakeNotificationLifetimeExtender();
         mEntryManager.addNotificationLifetimeExtender(extender);
         mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
@@ -498,7 +475,7 @@
         extender.getCallback().onSafeToRemove(mEntry.getKey());
 
         // THEN the notification is removed
-        assertNull(mEntryManager.getNotificationData().get(mSbn.getKey()));
+        assertNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
         verify(mEntryListener).onEntryRemoved(eq(mEntry), any(), eq(false));
     }
 
@@ -506,7 +483,7 @@
     public void testLifetimeExtenders_whenNotificationUpdatedRetainersAreCanceled() {
         // GIVEN an entry manager with a notification whose life has been extended
         mEntryManager.setRowBinder(mMockedRowBinder);
-        mEntryManager.getNotificationData().add(mEntry);
+        mEntryManager.addActiveNotificationForTest(mEntry);
         NotificationLifetimeExtender extender = mock(NotificationLifetimeExtender.class);
         when(extender.shouldExtendLifetime(mEntry)).thenReturn(true);
         mEntryManager.addNotificationLifetimeExtender(extender);
@@ -523,7 +500,7 @@
     public void testLifetimeExtenders_whenNewExtenderTakesPrecedenceOldExtenderIsCanceled() {
         // GIVEN an entry manager with a notification
         mEntryManager.setRowBinder(mMockedRowBinder);
-        mEntryManager.getNotificationData().add(mEntry);
+        mEntryManager.addActiveNotificationForTest(mEntry);
 
         // GIVEN two lifetime extenders, the first which never extends and the second which
         // always extends
@@ -554,15 +531,15 @@
      */
     @Test
     public void testPerformRemoveNotification_removedEntry() {
-        mEntryManager.getNotificationData().remove(mSbn.getKey(), null /* ranking */);
+        mEntryManager.removeNotification(mSbn.getKey(), null, 0);
         mEntryManager.performRemoveNotification(mSbn, REASON_CANCEL);
     }
 
     @Test
-    public void testRemoveInterceptor_interceptsDontGetRemoved() {
+    public void testRemoveInterceptor_interceptsDontGetRemoved() throws InterruptedException {
         // GIVEN an entry manager with a notification
         mEntryManager.setRowBinder(mMockedRowBinder);
-        mEntryManager.getNotificationData().add(mEntry);
+        mEntryManager.addActiveNotificationForTest(mEntry);
 
         // GIVEN interceptor that intercepts that entry
         when(mRemoveInterceptor.onNotificationRemoveRequested(eq(mEntry.getKey()), anyInt()))
@@ -572,16 +549,19 @@
         mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
 
         // THEN the interceptor intercepts & the entry is not removed & no listeners are called
-        assertNotNull(mEntryManager.getNotificationData().get(mEntry.getKey()));
+        assertNotNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
         verify(mEntryListener, never()).onEntryRemoved(eq(mEntry),
                 any(NotificationVisibility.class), anyBoolean());
     }
 
     @Test
     public void testRemoveInterceptor_notInterceptedGetsRemoved() {
+        // Row inflation happens off thread, so pretend that this test looper is main
+        Assert.sMainLooper = TestableLooper.get(this).getLooper();
+
         // GIVEN an entry manager with a notification
         mEntryManager.setRowBinder(mMockedRowBinder);
-        mEntryManager.getNotificationData().add(mEntry);
+        mEntryManager.addActiveNotificationForTest(mEntry);
 
         // GIVEN interceptor that doesn't intercept
         when(mRemoveInterceptor.onNotificationRemoveRequested(eq(mEntry.getKey()), anyInt()))
@@ -591,7 +571,7 @@
         mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
 
         // THEN the interceptor intercepts & the entry is not removed & no listeners are called
-        assertNull(mEntryManager.getNotificationData().get(mEntry.getKey()));
+        assertNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
         verify(mEntryListener, atLeastOnce()).onEntryRemoved(eq(mEntry),
                 any(NotificationVisibility.class), anyBoolean());
     }
@@ -612,6 +592,63 @@
                 .build();
     }
 
+    /* Tests annexed from NotificationDataTest go here */
+
+    @Test
+    public void testChannelIsSetWhenAdded() {
+        NotificationChannel nc = new NotificationChannel(
+                "testId",
+                "testName",
+                IMPORTANCE_DEFAULT);
+
+        Ranking r = new RankingBuilder()
+                .setKey(mEntry.getKey())
+                .setChannel(nc)
+                .build();
+
+        RankingMap rm = new RankingMap(new Ranking[] { r });
+
+        // GIVEN: a notification is added, and the ranking updated
+        mEntryManager.addActiveNotificationForTest(mEntry);
+        mEntryManager.updateRanking(rm, "testReason");
+
+        // THEN the notification entry better have a channel on it
+        assertEquals(
+                "Channel must be set when adding a notification",
+                nc.getName(),
+                mEntry.getChannel().getName());
+    }
+
+    @Test
+    public void testGetNotificationsForCurrentUser_shouldFilterNonCurrentUserNotifications() {
+        Assert.sMainLooper = TestableLooper.get(this).getLooper();
+        Notification.Builder n = new Notification.Builder(mContext, "")
+                .setSmallIcon(R.drawable.ic_person)
+                .setContentTitle("Title")
+                .setContentText("Text");
+
+        NotificationEntry e2 = new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE_NAME)
+                .setOpPkg(TEST_PACKAGE_NAME)
+                .setUid(TEST_UID)
+                .setId(mId++)
+                .setNotification(n.build())
+                .setUser(new UserHandle(ActivityManager.getCurrentUser()))
+                .build();
+
+        mEntryManager.addActiveNotificationForTest(mEntry);
+        mEntryManager.addActiveNotificationForTest(e2);
+
+        when(mEnvironment.isNotificationForCurrentProfiles(mEntry.getSbn())).thenReturn(false);
+        when(mEnvironment.isNotificationForCurrentProfiles(e2.getSbn())).thenReturn(true);
+
+        List<NotificationEntry> result = mEntryManager.getActiveNotificationsForCurrentUser();
+        assertEquals(result.size(), 1);
+        junit.framework.Assert.assertEquals(result.get(0), e2);
+    }
+
+    /* End annex */
+
     private Notification.Action createAction() {
         return new Notification.Action.Builder(
                 Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
index d85f275..68730d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
@@ -44,7 +44,7 @@
 import com.android.systemui.statusbar.NotificationEntryBuilder;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationTestHelper;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -71,7 +71,7 @@
     @Mock
     ForegroundServiceController mFsc;
     @Mock
-    NotificationData.KeyguardEnvironment mEnvironment;
+    KeyguardEnvironment mEnvironment;
     private final IPackageManager mMockPackageManager = mock(IPackageManager.class);
 
     private NotificationFilter mNotificationFilter;
@@ -96,7 +96,7 @@
                 new NotificationGroupManager(mock(StatusBarStateController.class)));
         mDependency.injectMockDependency(ShadeController.class);
         mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
-        mDependency.injectTestDependency(NotificationData.KeyguardEnvironment.class, mEnvironment);
+        mDependency.injectTestDependency(KeyguardEnvironment.class, mEnvironment);
         when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
         when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
         mRow = new NotificationTestHelper(getContext(), mDependency).createRow();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
index cc56949..133d52b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
@@ -17,9 +17,7 @@
 package com.android.systemui.statusbar.notification;
 
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
 import android.app.Notification;
@@ -34,14 +32,10 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationEntryBuilder;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.logging.NotifLog;
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
-import com.android.systemui.util.DeviceConfigProxyFake;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -67,13 +61,6 @@
     private NotificationEntryListener mEntryListener;
     private DeviceProvisionedListener mProvisionedListener;
 
-    // TODO: Remove this once EntryManager no longer needs to be mocked
-    private NotificationData mNotificationData =
-            new NotificationData(
-                    new NotificationSectionsFeatureManager(new DeviceConfigProxyFake(), mContext),
-                    mock(NotifLog.class),
-                    mock(PeopleNotificationIdentifier.class));
-
     private int mNextNotifId = 0;
 
     @Before
@@ -81,8 +68,6 @@
         MockitoAnnotations.initMocks(this);
         mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
 
-        when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
-
         mController = new NotificationListController(
                 mEntryManager,
                 mListContainer,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
new file mode 100644
index 0000000..34beefe
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification
+
+import com.android.systemui.statusbar.NotificationPresenter
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationRankingManager
+import com.android.systemui.statusbar.notification.logging.NotifLog
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
+import com.android.systemui.statusbar.phone.NotificationGroupManager
+
+import java.util.concurrent.CountDownLatch
+
+/**
+ * Enable some test capabilities for NEM without making everything public on the base class
+ */
+class TestableNotificationEntryManager(
+    log: NotifLog,
+    gm: NotificationGroupManager,
+    rm: NotificationRankingManager,
+    ke: KeyguardEnvironment
+) : NotificationEntryManager(log, gm, rm, ke) {
+
+    public var countDownLatch: CountDownLatch = CountDownLatch(1)
+
+    override fun onAsyncInflationFinished(entry: NotificationEntry?, inflatedFlags: Int) {
+        super.onAsyncInflationFinished(entry, inflatedFlags)
+        countDownLatch.countDown()
+    }
+
+    fun setUpForTest(
+        presenter: NotificationPresenter?,
+        listContainer: NotificationListContainer?,
+        headsUpManager: HeadsUpManagerPhone?
+    ) {
+        super.setUpWithPresenter(presenter, listContainer, headsUpManager)
+    }
+
+    fun setActiveNotificationList(activeList: List<NotificationEntry>) {
+        mSortedAndFiltered.clear()
+        mSortedAndFiltered.addAll(activeList)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
deleted file mode 100644
index 1a469d8..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
+++ /dev/null
@@ -1,697 +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.statusbar.notification.collection;
-
-import static android.app.Notification.CATEGORY_ALARM;
-import static android.app.Notification.CATEGORY_CALL;
-import static android.app.Notification.CATEGORY_EVENT;
-import static android.app.Notification.CATEGORY_MESSAGE;
-import static android.app.Notification.CATEGORY_REMINDER;
-import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.app.NotificationManager.IMPORTANCE_LOW;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
-
-import static com.android.systemui.statusbar.NotificationEntryHelper.modifySbn;
-import static com.android.systemui.statusbar.notification.collection.NotificationDataTest.TestableNotificationData.OVERRIDE_CHANNEL;
-import static com.android.systemui.statusbar.notification.collection.NotificationDataTest.TestableNotificationData.OVERRIDE_IMPORTANCE;
-import static com.android.systemui.statusbar.notification.collection.NotificationDataTest.TestableNotificationData.OVERRIDE_RANK;
-import static com.android.systemui.statusbar.notification.collection.NotificationDataTest.TestableNotificationData.OVERRIDE_VIS_EFFECTS;
-import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING;
-import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT;
-
-import static junit.framework.Assert.assertEquals;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.Manifest;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.Person;
-import android.content.Intent;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.graphics.drawable.Icon;
-import android.media.session.MediaSession;
-import android.os.Bundle;
-import android.service.notification.NotificationListenerService;
-import android.service.notification.NotificationListenerService.Ranking;
-import android.service.notification.SnoozeCriterion;
-import android.service.notification.StatusBarNotification;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.Dependency;
-import com.android.systemui.ForegroundServiceController;
-import com.android.systemui.InitController;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.NotificationEntryBuilder;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationTestHelper;
-import com.android.systemui.statusbar.RankingBuilder;
-import com.android.systemui.statusbar.SbnBuilder;
-import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
-import com.android.systemui.statusbar.notification.logging.NotifLog;
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.ShadeController;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-public class NotificationDataTest extends SysuiTestCase {
-
-    private static final int UID_NORMAL = 123;
-    private static final int UID_ALLOW_DURING_SETUP = 456;
-    private static final NotificationChannel NOTIFICATION_CHANNEL =
-            new NotificationChannel("id", "name", NotificationChannel.USER_LOCKED_IMPORTANCE);
-
-    private NotificationEntry mEntry;
-
-    @Mock
-    ForegroundServiceController mFsc;
-    @Mock
-    NotificationData.KeyguardEnvironment mEnvironment;
-
-    private final IPackageManager mMockPackageManager = mock(IPackageManager.class);
-    private TestableNotificationData mNotificationData;
-    private ExpandableNotificationRow mRow;
-
-    @Before
-    public void setUp() throws Exception {
-        com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
-        MockitoAnnotations.initMocks(this);
-
-        mEntry = new NotificationEntryBuilder()
-                .setUid(UID_NORMAL)
-                .build();
-
-        when(mMockPackageManager.checkUidPermission(
-                eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
-                eq(UID_NORMAL)))
-                .thenReturn(PackageManager.PERMISSION_DENIED);
-        when(mMockPackageManager.checkUidPermission(
-                eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
-                eq(UID_ALLOW_DURING_SETUP)))
-                .thenReturn(PackageManager.PERMISSION_GRANTED);
-
-        mDependency.injectTestDependency(ForegroundServiceController.class, mFsc);
-        mDependency.injectTestDependency(NotificationGroupManager.class,
-                new NotificationGroupManager(mock(StatusBarStateController.class)));
-        mDependency.injectMockDependency(ShadeController.class);
-        mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
-        mDependency.injectTestDependency(KeyguardEnvironment.class, mEnvironment);
-        when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
-        when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
-        mNotificationData = new TestableNotificationData(
-                mock(NotificationSectionsFeatureManager.class));
-        mNotificationData.updateRanking(mock(NotificationListenerService.RankingMap.class), "");
-        mRow = new NotificationTestHelper(getContext(), mDependency).createRow();
-        Dependency.get(InitController.class).executePostInitTasks();
-    }
-
-    @Test
-    public void testChannelSetWhenAdded() {
-        Bundle override = new Bundle();
-        override.putParcelable(OVERRIDE_CHANNEL, NOTIFICATION_CHANNEL);
-        mNotificationData.rankingOverrides.put(mRow.getEntry().getKey(), override);
-        mNotificationData.add(mRow.getEntry());
-        assertEquals(NOTIFICATION_CHANNEL, mRow.getEntry().getChannel());
-    }
-
-    @Test
-    public void testGetNotificationsForCurrentUser_shouldFilterNonCurrentUserNotifications()
-            throws Exception {
-        mNotificationData.add(mRow.getEntry());
-        ExpandableNotificationRow row2 = new NotificationTestHelper(getContext(), mDependency)
-                .createRow();
-        mNotificationData.add(row2.getEntry());
-
-        when(mEnvironment.isNotificationForCurrentProfiles(
-                mRow.getEntry().getSbn())).thenReturn(false);
-        when(mEnvironment.isNotificationForCurrentProfiles(
-                row2.getEntry().getSbn())).thenReturn(true);
-        ArrayList<NotificationEntry> result =
-                mNotificationData.getNotificationsForCurrentUser();
-
-        assertEquals(result.size(), 1);
-        junit.framework.Assert.assertEquals(result.get(0), row2.getEntry());
-    }
-
-    @Test
-    public void testIsExemptFromDndVisualSuppression_foreground() {
-        initStatusBarNotification(false);
-
-        mEntry.getSbn().getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
-        mEntry.setRow(mRow);
-        mNotificationData.add(mEntry);
-        Bundle override = new Bundle();
-        override.putInt(OVERRIDE_VIS_EFFECTS, 255);
-        mNotificationData.rankingOverrides.put(mEntry.getKey(), override);
-
-        assertTrue(mEntry.isExemptFromDndVisualSuppression());
-        assertFalse(mEntry.shouldSuppressAmbient());
-    }
-
-    @Test
-    public void testIsExemptFromDndVisualSuppression_media() {
-        initStatusBarNotification(false);
-        Notification n = mEntry.getSbn().getNotification();
-        Notification.Builder nb = Notification.Builder.recoverBuilder(mContext, n);
-        nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
-        n = nb.build();
-        modifySbn(mEntry)
-                .setNotification(n)
-                .build();
-        mEntry.setRow(mRow);
-        mNotificationData.add(mEntry);
-        Bundle override = new Bundle();
-        override.putInt(OVERRIDE_VIS_EFFECTS, 255);
-        mNotificationData.rankingOverrides.put(mEntry.getKey(), override);
-
-        assertTrue(mEntry.isExemptFromDndVisualSuppression());
-        assertFalse(mEntry.shouldSuppressAmbient());
-    }
-
-    @Test
-    public void testIsExemptFromDndVisualSuppression_system() {
-        initStatusBarNotification(false);
-        mEntry.setRow(mRow);
-        mEntry.mIsSystemNotification = true;
-        mNotificationData.add(mEntry);
-        Bundle override = new Bundle();
-        override.putInt(OVERRIDE_VIS_EFFECTS, 255);
-        mNotificationData.rankingOverrides.put(mEntry.getKey(), override);
-
-        assertTrue(mEntry.isExemptFromDndVisualSuppression());
-        assertFalse(mEntry.shouldSuppressAmbient());
-    }
-
-    @Test
-    public void testIsNotExemptFromDndVisualSuppression_hiddenCategories() {
-        initStatusBarNotification(false);
-        NotificationEntry entry = new NotificationEntryBuilder()
-                .setUid(UID_NORMAL)
-                .build();
-        entry.setRow(mRow);
-        entry.mIsSystemNotification = true;
-        Bundle override = new Bundle();
-        override.putInt(OVERRIDE_VIS_EFFECTS, NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT);
-        mNotificationData.rankingOverrides.put(entry.getKey(), override);
-        mNotificationData.add(entry);
-
-        modifySbn(entry)
-                .setNotification(
-                        new Notification.Builder(mContext, "").setCategory(CATEGORY_CALL).build())
-                .build();
-        assertFalse(entry.isExemptFromDndVisualSuppression());
-        assertTrue(entry.shouldSuppressAmbient());
-
-        modifySbn(entry)
-                .setNotification(
-                        new Notification.Builder(mContext, "")
-                                .setCategory(CATEGORY_REMINDER)
-                                .build())
-                .build();
-        assertFalse(entry.isExemptFromDndVisualSuppression());
-
-        modifySbn(entry)
-                .setNotification(
-                        new Notification.Builder(mContext, "").setCategory(CATEGORY_ALARM).build())
-                .build();
-        assertFalse(entry.isExemptFromDndVisualSuppression());
-
-        modifySbn(entry)
-                .setNotification(
-                        new Notification.Builder(mContext, "").setCategory(CATEGORY_EVENT).build())
-                .build();
-        assertFalse(entry.isExemptFromDndVisualSuppression());
-
-        modifySbn(entry)
-                .setNotification(
-                        new Notification.Builder(mContext, "")
-                                .setCategory(CATEGORY_MESSAGE)
-                                .build())
-                .build();
-        assertFalse(entry.isExemptFromDndVisualSuppression());
-    }
-
-    @Test
-    public void testCreateNotificationDataEntry_RankingUpdate() {
-        StatusBarNotification sbn = new SbnBuilder().build();
-        sbn.getNotification().actions =
-                new Notification.Action[] { createContextualAction("appGeneratedAction") };
-
-        ArrayList<Notification.Action> systemGeneratedSmartActions =
-                createActions("systemGeneratedAction");
-
-        SnoozeCriterion snoozeCriterion = new SnoozeCriterion("id", "explanation", "confirmation");
-        ArrayList<SnoozeCriterion> snoozeCriterions = new ArrayList<>();
-        snoozeCriterions.add(snoozeCriterion);
-
-        Ranking ranking = new RankingBuilder()
-                .setKey(sbn.getKey())
-                .setSmartActions(systemGeneratedSmartActions)
-                .setChannel(NOTIFICATION_CHANNEL)
-                .setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
-                .setSnoozeCriteria(snoozeCriterions)
-                .build();
-
-        NotificationEntry entry =
-                new NotificationEntry(sbn, ranking);
-
-        assertEquals(systemGeneratedSmartActions, entry.getSmartActions());
-        assertEquals(NOTIFICATION_CHANNEL, entry.getChannel());
-        assertEquals(Ranking.USER_SENTIMENT_NEGATIVE, entry.getUserSentiment());
-        assertEquals(snoozeCriterions, entry.getSnoozeCriteria());
-    }
-
-    @Test
-    public void notificationDataEntry_testIsLastMessageFromReply() {
-        Person.Builder person = new Person.Builder()
-                .setName("name")
-                .setKey("abc")
-                .setUri("uri")
-                .setBot(true);
-
-        // EXTRA_MESSAGING_PERSON is the same Person as the sender in last message in EXTRA_MESSAGES
-        Bundle bundle = new Bundle();
-        bundle.putParcelable(Notification.EXTRA_MESSAGING_PERSON, person.build());
-        Bundle[] messagesBundle = new Bundle[]{ new Notification.MessagingStyle.Message(
-                "text", 0, person.build()).toBundle() };
-        bundle.putParcelableArray(Notification.EXTRA_MESSAGES, messagesBundle);
-
-        Notification notification = new Notification.Builder(mContext, "test")
-                .addExtras(bundle)
-                .build();
-
-        NotificationEntry entry = new NotificationEntryBuilder()
-                .setPkg("pkg")
-                .setOpPkg("pkg")
-                .setTag("tag")
-                .setNotification(notification)
-                .setUser(mContext.getUser())
-                .setOverrideGroupKey("")
-                .build();
-        entry.setHasSentReply();
-
-        assertTrue(entry.isLastMessageFromReply());
-    }
-
-    @Test
-    public void personHighPriority() {
-        Person person = new Person.Builder()
-                .setName("name")
-                .setKey("abc")
-                .setUri("uri")
-                .setBot(true)
-                .build();
-
-        Notification notification = new Notification.Builder(mContext, "test")
-                .addPerson(person)
-                .build();
-
-        StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
-                notification, mContext.getUser(), "", 0);
-
-        assertTrue(mNotificationData.isHighPriority(sbn));
-    }
-
-    @Test
-    public void messagingStyleHighPriority() {
-
-        Notification notification = new Notification.Builder(mContext, "test")
-                .setStyle(new Notification.MessagingStyle(""))
-                .build();
-
-        StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
-                notification, mContext.getUser(), "", 0);
-
-        assertTrue(mNotificationData.isHighPriority(sbn));
-    }
-
-    @Test
-    public void minForegroundNotHighPriority() {
-        Notification notification = mock(Notification.class);
-        when(notification.isForegroundService()).thenReturn(true);
-
-        StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
-                notification, mContext.getUser(), "", 0);
-
-        Bundle override = new Bundle();
-        override.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_MIN);
-        mNotificationData.rankingOverrides.put(sbn.getKey(), override);
-
-        assertFalse(mNotificationData.isHighPriority(sbn));
-    }
-
-    @Test
-    public void lowForegroundHighPriority() {
-        Notification notification = mock(Notification.class);
-        when(notification.isForegroundService()).thenReturn(true);
-
-        StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
-                notification, mContext.getUser(), "", 0);
-
-        Bundle override = new Bundle();
-        override.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_LOW);
-        mNotificationData.rankingOverrides.put(sbn.getKey(), override);
-
-        assertTrue(mNotificationData.isHighPriority(sbn));
-    }
-
-    @Test
-    public void userChangeTrumpsHighPriorityCharacteristics() {
-        Person person = new Person.Builder()
-                .setName("name")
-                .setKey("abc")
-                .setUri("uri")
-                .setBot(true)
-                .build();
-
-        Notification notification = new Notification.Builder(mContext, "test")
-                .addPerson(person)
-                .setStyle(new Notification.MessagingStyle(""))
-                .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
-                .build();
-
-        StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
-                notification, mContext.getUser(), "", 0);
-
-        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
-        channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
-
-        Bundle override = new Bundle();
-        override.putParcelable(OVERRIDE_CHANNEL, channel);
-        mNotificationData.rankingOverrides.put(sbn.getKey(), override);
-
-        assertFalse(mNotificationData.isHighPriority(sbn));
-    }
-
-    @Test
-    public void testSort_highPriorityTrumpsNMSRank() {
-        // NMS rank says A and then B. But A is not high priority and B is, so B should sort in
-        // front
-        Notification aN = new Notification.Builder(mContext, "test")
-                .setStyle(new Notification.MessagingStyle(""))
-                .build();
-        NotificationEntry a = new NotificationEntryBuilder()
-                .setPkg("pkg")
-                .setOpPkg("pkg")
-                .setTag("tag")
-                .setNotification(aN)
-                .setUser(mContext.getUser())
-                .setOverrideGroupKey("")
-                .build();
-        a.setRow(mock(ExpandableNotificationRow.class));
-        a.setIsHighPriority(false);
-
-        Bundle override = new Bundle();
-        override.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_LOW);
-        override.putInt(OVERRIDE_RANK, 1);
-        mNotificationData.rankingOverrides.put(a.getKey(), override);
-
-        Notification bN = new Notification.Builder(mContext, "test")
-                .setStyle(new Notification.MessagingStyle(""))
-                .build();
-        NotificationEntry b = new NotificationEntryBuilder()
-                .setPkg("pkg2")
-                .setOpPkg("pkg2")
-                .setTag("tag")
-                .setNotification(bN)
-                .setUser(mContext.getUser())
-                .setOverrideGroupKey("")
-                .build();
-        b.setIsHighPriority(true);
-        b.setRow(mock(ExpandableNotificationRow.class));
-
-        Bundle bOverride = new Bundle();
-        bOverride.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_LOW);
-        bOverride.putInt(OVERRIDE_RANK, 2);
-        mNotificationData.rankingOverrides.put(b.getKey(), bOverride);
-
-        assertEquals(1, mNotificationData.mRankingComparator.compare(a, b));
-    }
-
-    @Test
-    public void testSort_samePriorityUsesNMSRank() {
-        // NMS rank says A and then B, and they are the same priority so use that rank
-        Notification aN = new Notification.Builder(mContext, "test")
-                .setStyle(new Notification.MessagingStyle(""))
-                .build();
-        NotificationEntry a = new NotificationEntryBuilder()
-                .setPkg("pkg")
-                .setOpPkg("pkg")
-                .setTag("tag")
-                .setNotification(aN)
-                .setUser(mContext.getUser())
-                .setOverrideGroupKey("")
-                .build();
-        a.setRow(mock(ExpandableNotificationRow.class));
-        a.setIsHighPriority(false);
-
-        Bundle override = new Bundle();
-        override.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_LOW);
-        override.putInt(OVERRIDE_RANK, 1);
-        mNotificationData.rankingOverrides.put(a.getKey(), override);
-
-        Notification bN = new Notification.Builder(mContext, "test")
-                .setStyle(new Notification.MessagingStyle(""))
-                .build();
-        NotificationEntry b = new NotificationEntryBuilder()
-                .setPkg("pkg2")
-                .setOpPkg("pkg2")
-                .setTag("tag")
-                .setNotification(bN)
-                .setUser(mContext.getUser())
-                .setOverrideGroupKey("")
-                .build();
-        b.setRow(mock(ExpandableNotificationRow.class));
-        b.setIsHighPriority(false);
-
-        Bundle bOverride = new Bundle();
-        bOverride.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_LOW);
-        bOverride.putInt(OVERRIDE_RANK, 2);
-        mNotificationData.rankingOverrides.put(b.getKey(), bOverride);
-
-        assertEquals(-1, mNotificationData.mRankingComparator.compare(a, b));
-    }
-
-    @Test
-    public void testSort_properlySetsAlertingBucket() {
-        Notification notification = new Notification.Builder(mContext, "test")
-                .build();
-        NotificationEntry entry = new NotificationEntryBuilder()
-                .setPkg("pkg")
-                .setOpPkg("pkg")
-                .setTag("tag")
-                .setNotification(notification)
-                .setUser(mContext.getUser())
-                .setOverrideGroupKey("")
-                .build();
-
-        Bundle override = new Bundle();
-        override.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_DEFAULT);
-        mNotificationData.rankingOverrides.put(entry.getKey(), override);
-
-        entry.setRow(mRow);
-        mNotificationData.add(entry);
-
-        assertEquals(entry.getBucket(), BUCKET_ALERTING);
-    }
-
-    @Test
-    public void testSort_properlySetsSilentBucket() {
-        Notification notification = new Notification.Builder(mContext, "test")
-                .build();
-
-        NotificationEntry entry = new NotificationEntryBuilder()
-                .setPkg("pkg")
-                .setOpPkg("pkg")
-                .setTag("tag")
-                .setNotification(notification)
-                .setUser(mContext.getUser())
-                .setOverrideGroupKey("")
-                .build();
-
-        Bundle override = new Bundle();
-        override.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_LOW);
-        mNotificationData.rankingOverrides.put(entry.getKey(), override);
-
-        entry.setRow(mRow);
-        mNotificationData.add(entry);
-
-        assertEquals(entry.getBucket(), BUCKET_SILENT);
-    }
-
-    private void initStatusBarNotification(boolean allowDuringSetup) {
-        Bundle bundle = new Bundle();
-        bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup);
-        Notification notification = new Notification.Builder(mContext, "test")
-                .addExtras(bundle)
-                .build();
-        modifySbn(mEntry)
-                .setNotification(notification)
-                .build();
-    }
-
-    public static class TestableNotificationData extends NotificationData {
-        public TestableNotificationData(NotificationSectionsFeatureManager sectionsFeatureManager) {
-            super(
-                    sectionsFeatureManager,
-                    mock(NotifLog.class),
-                    mock(PeopleNotificationIdentifier.class));
-        }
-
-        public static final String OVERRIDE_RANK = "r";
-        public static final String OVERRIDE_DND = "dnd";
-        public static final String OVERRIDE_VIS_OVERRIDE = "vo";
-        public static final String OVERRIDE_VIS_EFFECTS = "ve";
-        public static final String OVERRIDE_IMPORTANCE = "i";
-        public static final String OVERRIDE_IMP_EXP = "ie";
-        public static final String OVERRIDE_GROUP = "g";
-        public static final String OVERRIDE_CHANNEL = "c";
-        public static final String OVERRIDE_PEOPLE = "p";
-        public static final String OVERRIDE_SNOOZE_CRITERIA = "sc";
-        public static final String OVERRIDE_BADGE = "b";
-        public static final String OVERRIDE_USER_SENTIMENT = "us";
-        public static final String OVERRIDE_HIDDEN = "h";
-        public static final String OVERRIDE_LAST_ALERTED = "la";
-        public static final String OVERRIDE_NOISY = "n";
-        public static final String OVERRIDE_SMART_ACTIONS = "sa";
-        public static final String OVERRIDE_SMART_REPLIES = "sr";
-        public static final String OVERRIDE_BUBBLE = "cb";
-        public static final String OVERRIDE_VISUALLY_INTERRUPTIVE = "vi";
-
-        public Map<String, Bundle> rankingOverrides = new HashMap<>();
-
-        @Override
-        protected boolean getRanking(String key, Ranking outRanking) {
-            super.getRanking(key, outRanking);
-
-            ArrayList<String> currentAdditionalPeople = new ArrayList<>();
-            if (outRanking.getAdditionalPeople() != null) {
-                currentAdditionalPeople.addAll(outRanking.getAdditionalPeople());
-            }
-
-            ArrayList<SnoozeCriterion> currentSnooze = new ArrayList<>();
-            if (outRanking.getSnoozeCriteria() != null) {
-                currentSnooze.addAll(outRanking.getSnoozeCriteria());
-            }
-
-            ArrayList<Notification.Action> currentActions = new ArrayList<>();
-            if (outRanking.getSmartActions() != null) {
-                currentActions.addAll(outRanking.getSmartActions());
-            }
-
-            ArrayList<CharSequence> currentReplies = new ArrayList<>();
-            if (outRanking.getSmartReplies() != null) {
-                currentReplies.addAll(outRanking.getSmartReplies());
-            }
-
-            if (rankingOverrides.get(key) != null) {
-                Bundle overrides = rankingOverrides.get(key);
-                outRanking.populate(key,
-                        overrides.getInt(OVERRIDE_RANK, outRanking.getRank()),
-                        overrides.getBoolean(OVERRIDE_DND, outRanking.matchesInterruptionFilter()),
-                        overrides.getInt(OVERRIDE_VIS_OVERRIDE, outRanking.getVisibilityOverride()),
-                        overrides.getInt(OVERRIDE_VIS_EFFECTS,
-                                outRanking.getSuppressedVisualEffects()),
-                        overrides.getInt(OVERRIDE_IMPORTANCE, outRanking.getImportance()),
-                        overrides.getCharSequence(OVERRIDE_IMP_EXP,
-                                outRanking.getImportanceExplanation()),
-                        overrides.getString(OVERRIDE_GROUP, outRanking.getOverrideGroupKey()),
-                        overrides.containsKey(OVERRIDE_CHANNEL)
-                                ? (NotificationChannel) overrides.getParcelable(OVERRIDE_CHANNEL)
-                                : outRanking.getChannel(),
-                        overrides.containsKey(OVERRIDE_PEOPLE)
-                                ? overrides.getStringArrayList(OVERRIDE_PEOPLE)
-                                : currentAdditionalPeople,
-                        overrides.containsKey(OVERRIDE_SNOOZE_CRITERIA)
-                                ? overrides.getParcelableArrayList(OVERRIDE_SNOOZE_CRITERIA)
-                                : currentSnooze,
-                        overrides.getBoolean(OVERRIDE_BADGE, outRanking.canShowBadge()),
-                        overrides.getInt(OVERRIDE_USER_SENTIMENT, outRanking.getUserSentiment()),
-                        overrides.getBoolean(OVERRIDE_HIDDEN, outRanking.isSuspended()),
-                        overrides.getLong(OVERRIDE_LAST_ALERTED,
-                                outRanking.getLastAudiblyAlertedMillis()),
-                        overrides.getBoolean(OVERRIDE_NOISY, outRanking.isNoisy()),
-                        overrides.containsKey(OVERRIDE_SMART_ACTIONS)
-                                ? overrides.getParcelableArrayList(OVERRIDE_SMART_ACTIONS)
-                                : currentActions,
-                        overrides.containsKey(OVERRIDE_SMART_REPLIES)
-                                ? overrides.getCharSequenceArrayList(OVERRIDE_SMART_REPLIES)
-                                : currentReplies,
-                        overrides.getBoolean(OVERRIDE_BUBBLE, outRanking.canBubble()),
-                        overrides.getBoolean(OVERRIDE_VISUALLY_INTERRUPTIVE,
-                                outRanking.visuallyInterruptive()));
-            } else {
-                outRanking.populate(
-                        new RankingBuilder()
-                                .setKey(key)
-                                .build());
-            }
-            return true;
-        }
-    }
-
-    private Notification.Action createContextualAction(String title) {
-        return new Notification.Action.Builder(
-                Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
-                title,
-                PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0))
-                        .setContextual(true)
-                        .build();
-    }
-
-    private Notification.Action createAction(String title) {
-        return new Notification.Action.Builder(
-                Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
-                title,
-                PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0)).build();
-    }
-
-    private ArrayList<Notification.Action> createActions(String... titles) {
-        ArrayList<Notification.Action> actions = new ArrayList<>();
-        for (String title : titles) {
-            actions.add(createAction(title));
-        }
-        return actions;
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
new file mode 100644
index 0000000..536aeb4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection;
+
+import static android.app.Notification.CATEGORY_ALARM;
+import static android.app.Notification.CATEGORY_CALL;
+import static android.app.Notification.CATEGORY_EVENT;
+import static android.app.Notification.CATEGORY_MESSAGE;
+import static android.app.Notification.CATEGORY_REMINDER;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
+
+import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
+import static com.android.systemui.statusbar.NotificationEntryHelper.modifySbn;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.PendingIntent;
+import android.app.Person;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.media.session.MediaSession;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.SnoozeCriterion;
+import android.service.notification.StatusBarNotification;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
+import com.android.systemui.statusbar.RankingBuilder;
+import com.android.systemui.statusbar.SbnBuilder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class NotificationEntryTest extends SysuiTestCase {
+    private static final String TEST_PACKAGE_NAME = "test";
+    private static final int TEST_UID = 0;
+    private static final int UID_NORMAL = 123;
+    private static final NotificationChannel NOTIFICATION_CHANNEL =
+            new NotificationChannel("id", "name", NotificationChannel.USER_LOCKED_IMPORTANCE);
+
+    private int mId;
+
+    private NotificationEntry mEntry;
+
+    @Before
+    public void setup() {
+        Notification.Builder n = new Notification.Builder(mContext, "")
+                .setSmallIcon(R.drawable.ic_person)
+                .setContentTitle("Title")
+                .setContentText("Text");
+
+        mEntry = new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE_NAME)
+                .setOpPkg(TEST_PACKAGE_NAME)
+                .setUid(TEST_UID)
+                .setId(mId++)
+                .setNotification(n.build())
+                .setUser(new UserHandle(ActivityManager.getCurrentUser()))
+                .build();
+    }
+
+    @Test
+    public void testIsExemptFromDndVisualSuppression_foreground() {
+        mEntry.getSbn().getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
+
+        assertTrue(mEntry.isExemptFromDndVisualSuppression());
+        assertFalse(mEntry.shouldSuppressAmbient());
+    }
+
+    @Test
+    public void testIsExemptFromDndVisualSuppression_media() {
+        Notification.Builder n = new Notification.Builder(mContext, "")
+                .setStyle(new Notification.MediaStyle()
+                        .setMediaSession(mock(MediaSession.Token.class)))
+                .setSmallIcon(R.drawable.ic_person)
+                .setContentTitle("Title")
+                .setContentText("Text");
+        NotificationEntry e1 = new NotificationEntryBuilder()
+                .setNotification(n.build())
+                .build();
+
+        assertTrue(e1.isExemptFromDndVisualSuppression());
+        assertFalse(e1.shouldSuppressAmbient());
+    }
+
+    @Test
+    public void testIsExemptFromDndVisualSuppression_system() {
+        mEntry.mIsSystemNotification = true;
+
+        assertTrue(mEntry.isExemptFromDndVisualSuppression());
+        assertFalse(mEntry.shouldSuppressAmbient());
+    }
+
+    @Test
+    public void testIsNotExemptFromDndVisualSuppression_hiddenCategories() {
+        NotificationEntry entry = new NotificationEntryBuilder()
+                .setUid(UID_NORMAL)
+                .build();
+        entry.mIsSystemNotification = true;
+        modifyRanking(entry).setSuppressedVisualEffects(SUPPRESSED_EFFECT_AMBIENT).build();
+
+        modifySbn(entry)
+                .setNotification(
+                        new Notification.Builder(mContext, "").setCategory(CATEGORY_CALL).build())
+                .build();
+        assertFalse(entry.isExemptFromDndVisualSuppression());
+        assertTrue(entry.shouldSuppressAmbient());
+
+        modifySbn(entry)
+                .setNotification(
+                        new Notification.Builder(mContext, "")
+                                .setCategory(CATEGORY_REMINDER)
+                                .build())
+                .build();
+        assertFalse(entry.isExemptFromDndVisualSuppression());
+
+        modifySbn(entry)
+                .setNotification(
+                        new Notification.Builder(mContext, "").setCategory(CATEGORY_ALARM).build())
+                .build();
+        assertFalse(entry.isExemptFromDndVisualSuppression());
+
+        modifySbn(entry)
+                .setNotification(
+                        new Notification.Builder(mContext, "").setCategory(CATEGORY_EVENT).build())
+                .build();
+        assertFalse(entry.isExemptFromDndVisualSuppression());
+
+        modifySbn(entry)
+                .setNotification(
+                        new Notification.Builder(mContext, "")
+                                .setCategory(CATEGORY_MESSAGE)
+                                .build())
+                .build();
+        assertFalse(entry.isExemptFromDndVisualSuppression());
+    }
+
+    @Test
+    public void testCreateNotificationDataEntry_RankingUpdate() {
+        StatusBarNotification sbn = new SbnBuilder().build();
+        sbn.getNotification().actions =
+                new Notification.Action[]{createContextualAction("appGeneratedAction")};
+
+        ArrayList<Notification.Action> systemGeneratedSmartActions =
+                createActions("systemGeneratedAction");
+
+        SnoozeCriterion snoozeCriterion = new SnoozeCriterion("id", "explanation", "confirmation");
+        ArrayList<SnoozeCriterion> snoozeCriterions = new ArrayList<>();
+        snoozeCriterions.add(snoozeCriterion);
+
+        Ranking ranking = new RankingBuilder()
+                .setKey(sbn.getKey())
+                .setSmartActions(systemGeneratedSmartActions)
+                .setChannel(NOTIFICATION_CHANNEL)
+                .setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
+                .setSnoozeCriteria(snoozeCriterions)
+                .build();
+
+        NotificationEntry entry =
+                new NotificationEntry(sbn, ranking);
+
+        assertEquals(systemGeneratedSmartActions, entry.getSmartActions());
+        assertEquals(NOTIFICATION_CHANNEL, entry.getChannel());
+        assertEquals(Ranking.USER_SENTIMENT_NEGATIVE, entry.getUserSentiment());
+        assertEquals(snoozeCriterions, entry.getSnoozeCriteria());
+    }
+
+    @Test
+    public void notificationDataEntry_testIsLastMessageFromReply() {
+        Person.Builder person = new Person.Builder()
+                .setName("name")
+                .setKey("abc")
+                .setUri("uri")
+                .setBot(true);
+
+        // EXTRA_MESSAGING_PERSON is the same Person as the sender in last message in EXTRA_MESSAGES
+        Bundle bundle = new Bundle();
+        bundle.putParcelable(Notification.EXTRA_MESSAGING_PERSON, person.build());
+        Bundle[] messagesBundle = new Bundle[]{new Notification.MessagingStyle.Message(
+                "text", 0, person.build()).toBundle()};
+        bundle.putParcelableArray(Notification.EXTRA_MESSAGES, messagesBundle);
+
+        Notification notification = new Notification.Builder(mContext, "test")
+                .addExtras(bundle)
+                .build();
+
+        NotificationEntry entry = new NotificationEntryBuilder()
+                .setPkg("pkg")
+                .setOpPkg("pkg")
+                .setTag("tag")
+                .setNotification(notification)
+                .setUser(mContext.getUser())
+                .setOverrideGroupKey("")
+                .build();
+        entry.setHasSentReply();
+
+        assertTrue(entry.isLastMessageFromReply());
+    }
+
+    private Notification.Action createContextualAction(String title) {
+        return new Notification.Action.Builder(
+                Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
+                title,
+                PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0))
+                .setContextual(true)
+                .build();
+    }
+
+    private Notification.Action createAction(String title) {
+        return new Notification.Action.Builder(
+                Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
+                title,
+                PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0)).build();
+    }
+
+    private ArrayList<Notification.Action> createActions(String... titles) {
+        ArrayList<Notification.Action> actions = new ArrayList<>();
+        for (String title : titles) {
+            actions.add(createAction(title));
+        }
+        return actions;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
new file mode 100644
index 0000000..01b2f89
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager.IMPORTANCE_DEFAULT
+import android.app.NotificationManager.IMPORTANCE_LOW
+import android.app.Person
+import android.service.notification.NotificationListenerService.RankingMap
+import android.service.notification.StatusBarNotification
+import android.testing.AndroidTestingRunner
+
+import org.junit.runner.RunWith
+
+import androidx.test.filters.SmallTest
+
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.NotificationEntryBuilder
+import com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking
+import com.android.systemui.statusbar.NotificationMediaManager
+import com.android.systemui.statusbar.notification.NotificationFilter
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
+import com.android.systemui.statusbar.notification.logging.NotifLog
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT
+import com.android.systemui.statusbar.phone.NotificationGroupManager
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import dagger.Lazy
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
+
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.mock
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class NotificationRankingManagerTest
+    : SysuiTestCase() {
+
+    private var lazyMedia: Lazy<NotificationMediaManager> = Lazy {
+        mock<NotificationMediaManager>(NotificationMediaManager::class.java)
+    }
+
+    private val rankingManager = TestableNotificationRankingManager(
+            lazyMedia,
+            mock<NotificationGroupManager>(NotificationGroupManager::class.java),
+            mock<HeadsUpManager>(HeadsUpManager::class.java),
+            mock<NotificationFilter>(NotificationFilter::class.java),
+            mock<NotifLog>(NotifLog::class.java),
+            mock<NotificationSectionsFeatureManager>(NotificationSectionsFeatureManager::class.java)
+    )
+
+    @Before
+    fun setup() {
+    }
+
+    @Test
+    fun testPeopleNotification_isHighPriority() {
+        val person = Person.Builder()
+                .setName("name")
+                .setKey("abc")
+                .setUri("uri")
+                .setBot(true)
+                .build()
+
+        val notification = Notification.Builder(mContext, "test")
+                .addPerson(person)
+                .build()
+
+        val sbn = StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
+                notification, mContext.user, "", 0)
+
+        val e = NotificationEntryBuilder()
+                .setNotification(notification)
+                .setSbn(sbn)
+                .build()
+
+        assertTrue(rankingManager.isHighPriority2(e))
+    }
+
+    @Test
+    fun messagingStyleHighPriority() {
+
+        val notif = Notification.Builder(mContext, "test")
+                .setStyle(Notification.MessagingStyle(""))
+                .build()
+
+        val sbn = StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
+                notif, mContext.getUser(), "", 0)
+
+        val e = NotificationEntryBuilder()
+                .setNotification(notif)
+                .setSbn(sbn)
+                .build()
+
+        assertTrue(rankingManager.isHighPriority2(e))
+    }
+
+    @Test
+    fun lowForegroundHighPriority() {
+        val notification = mock(Notification::class.java)
+        `when`<Boolean>(notification.isForegroundService).thenReturn(true)
+
+        val sbn = StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
+                notification, mContext.user, "", 0)
+
+        val e = NotificationEntryBuilder()
+                .setNotification(notification)
+                .setSbn(sbn)
+                .build()
+
+        modifyRanking(e)
+                .setImportance(IMPORTANCE_LOW)
+                .build()
+
+        assertTrue(rankingManager.isHighPriority2(e))
+    }
+
+    @Test
+    fun userChangeTrumpsHighPriorityCharacteristics() {
+        val person = Person.Builder()
+                .setName("name")
+                .setKey("abc")
+                .setUri("uri")
+                .setBot(true)
+                .build()
+
+        val notification = Notification.Builder(mContext, "test")
+                .addPerson(person)
+                .setStyle(Notification.MessagingStyle(""))
+                .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
+                .build()
+
+        val sbn = StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
+                notification, mContext.user, "", 0)
+
+        val channel = NotificationChannel("a", "a", IMPORTANCE_LOW)
+        channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE)
+
+        val e = NotificationEntryBuilder()
+                .setSbn(sbn)
+                .setChannel(channel)
+                .build()
+
+        assertFalse(rankingManager.isHighPriority2(e))
+    }
+
+    @Test
+    fun testSort_highPriorityTrumpsNMSRank() {
+        // NMS rank says A and then B. But A is not high priority and B is, so B should sort in
+        // front
+        val aN = Notification.Builder(mContext, "test")
+                .setStyle(Notification.MessagingStyle(""))
+                .build()
+        val a = NotificationEntryBuilder()
+                .setPkg("pkg")
+                .setOpPkg("pkg")
+                .setTag("tag")
+                .setNotification(aN)
+                .setUser(mContext.getUser())
+                .setOverrideGroupKey("")
+                .build()
+
+        a.setIsHighPriority(false)
+
+        modifyRanking(a)
+                .setImportance(IMPORTANCE_LOW)
+                .setRank(1)
+                .build()
+
+        val bN = Notification.Builder(mContext, "test")
+                .setStyle(Notification.MessagingStyle(""))
+                .build()
+        val b = NotificationEntryBuilder()
+                .setPkg("pkg2")
+                .setOpPkg("pkg2")
+                .setTag("tag")
+                .setNotification(bN)
+                .setUser(mContext.getUser())
+                .setOverrideGroupKey("")
+                .build()
+        b.setIsHighPriority(true)
+
+        modifyRanking(b)
+                .setImportance(IMPORTANCE_LOW)
+                .setRank(2)
+                .build()
+
+        assertEquals(
+                listOf(b, a),
+                rankingManager.updateRanking(null, listOf(a, b), "test"))
+    }
+
+    @Test
+    fun testSort_samePriorityUsesNMSRank() {
+        // NMS rank says A and then B, and they are the same priority so use that rank
+        val aN = Notification.Builder(mContext, "test")
+                .setStyle(Notification.MessagingStyle(""))
+                .build()
+        val a = NotificationEntryBuilder()
+                .setPkg("pkg")
+                .setOpPkg("pkg")
+                .setTag("tag")
+                .setNotification(aN)
+                .setUser(mContext.getUser())
+                .setOverrideGroupKey("")
+                .build()
+        a.setIsHighPriority(false)
+
+        modifyRanking(a)
+                .setImportance(IMPORTANCE_LOW)
+                .setRank(1)
+                .build()
+
+        val bN = Notification.Builder(mContext, "test")
+                .setStyle(Notification.MessagingStyle(""))
+                .build()
+        val b = NotificationEntryBuilder()
+                .setPkg("pkg2")
+                .setOpPkg("pkg2")
+                .setTag("tag")
+                .setNotification(bN)
+                .setUser(mContext.getUser())
+                .setOverrideGroupKey("")
+                .build()
+        b.setIsHighPriority(false)
+
+        modifyRanking(b)
+                .setImportance(IMPORTANCE_LOW)
+                .setRank(2)
+                .build()
+
+        assertEquals(
+                listOf(a, b),
+                rankingManager.updateRanking(null, listOf(a, b), "test"))
+    }
+
+    @Test
+    fun testSort_properlySetsAlertingBucket() {
+        val notif = Notification.Builder(mContext, "test") .build()
+
+        val e = NotificationEntryBuilder()
+                .setPkg("pkg")
+                .setOpPkg("pkg")
+                .setTag("tag")
+                .setNotification(notif)
+                .setUser(mContext.user)
+                .setOverrideGroupKey("")
+                .build()
+
+        modifyRanking(e).setImportance(IMPORTANCE_DEFAULT) .build()
+
+        rankingManager.updateRanking(RankingMap(arrayOf(e.ranking)), listOf(e), "test")
+        assertEquals(e.bucket, BUCKET_ALERTING)
+    }
+
+    @Test
+    fun testSort_properlySetsSilentBucket() {
+        val notif = Notification.Builder(mContext, "test") .build()
+
+        val e = NotificationEntryBuilder()
+                .setPkg("pkg")
+                .setOpPkg("pkg")
+                .setTag("tag")
+                .setNotification(notif)
+                .setUser(mContext.user)
+                .setOverrideGroupKey("")
+                .build()
+
+        modifyRanking(e).setImportance(IMPORTANCE_LOW).build()
+
+        rankingManager.updateRanking(RankingMap(arrayOf(e.ranking)), listOf(e), "test")
+        assertEquals(e.bucket, BUCKET_SILENT)
+    }
+
+    internal class TestableNotificationRankingManager(
+        mediaManager: Lazy<NotificationMediaManager>,
+        groupManager: NotificationGroupManager,
+        headsUpManager: HeadsUpManager,
+        filter: NotificationFilter,
+        notifLog: NotifLog,
+        sectionsFeatureManager: NotificationSectionsFeatureManager
+    ) : NotificationRankingManager(
+        mediaManager,
+        groupManager,
+        headsUpManager,
+        filter,
+        notifLog,
+        sectionsFeatureManager
+    ) {
+
+        fun isHighPriority2(e: NotificationEntry): Boolean {
+            return isHighPriority(e)
+        }
+
+        fun applyTestRankingMap(r: RankingMap) {
+            rankingMap = r
+        }
+    }
+}
\ No newline at end of file
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 47c17ad..d139866 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
@@ -44,7 +44,6 @@
 import com.android.systemui.statusbar.StatusBarStateControllerImpl;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -71,7 +70,6 @@
 
     @Mock private NotificationListContainer mListContainer;
     @Mock private IStatusBarService mBarService;
-    @Mock private NotificationData mNotificationData;
     @Mock private ExpandableNotificationRow mRow;
     @Mock private NotificationLogger.ExpansionStateLogger mExpansionStateLogger;
 
@@ -91,8 +89,6 @@
         mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
         mDependency.injectTestDependency(NotificationListener.class, mListener);
 
-        when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
-
         mEntry = new NotificationEntryBuilder()
                 .setPkg(TEST_PACKAGE_NAME)
                 .setOpPkg(TEST_PACKAGE_NAME)
@@ -131,7 +127,7 @@
                 any(NotificationVisibility[].class));
 
         when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
-        when(mNotificationData.getActiveNotifications()).thenReturn(Lists.newArrayList(mEntry));
+        when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
         mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
         TestableLooper.get(this).processAllMessages();
         waitForUiOffloadThread();
@@ -153,7 +149,7 @@
     public void testStoppingNotificationLoggingReportsCurrentNotifications()
             throws Exception {
         when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
-        when(mNotificationData.getActiveNotifications()).thenReturn(Lists.newArrayList(mEntry));
+        when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
         mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
         TestableLooper.get(this).processAllMessages();
         waitForUiOffloadThread();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 6f52e4a..5b624bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -20,7 +20,6 @@
 import static junit.framework.Assert.assertNull;
 
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -56,7 +55,9 @@
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.statusbar.EmptyShadeView;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShelf;
@@ -65,9 +66,13 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationFilter;
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.TestableNotificationEntryManager;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
 import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewAdapter;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.FooterView;
@@ -80,7 +85,6 @@
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.StatusBarTest.TestableNotificationEntryManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.util.DeviceConfigProxyFake;
@@ -97,6 +101,7 @@
 import org.mockito.junit.MockitoRule;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Tests for {@link NotificationStackScrollLayout}.
@@ -117,7 +122,6 @@
     @Mock private NotificationGroupManager mGroupManager;
     @Mock private ExpandHelper mExpandHelper;
     @Mock private EmptyShadeView mEmptyShadeView;
-    @Mock private NotificationData mNotificationData;
     @Mock private NotificationRemoteInputManager mRemoteInputManager;
     @Mock private RemoteInputController mRemoteInputController;
     @Mock private NotificationIconAreaController mNotificationIconAreaController;
@@ -140,6 +144,7 @@
                 NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
 
         // Inject dependencies before initializing the layout
+        mDependency.injectMockDependency(VisualStabilityManager.class);
         mDependency.injectTestDependency(
                 NotificationBlockingHelperManager.class,
                 mBlockingHelperManager);
@@ -150,7 +155,18 @@
         mDependency.injectMockDependency(ShadeController.class);
         when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
 
-        mEntryManager = new TestableNotificationEntryManager(mNotificationData);
+        mEntryManager = new TestableNotificationEntryManager(
+                mock(NotifLog.class),
+                mock(NotificationGroupManager.class),
+                new NotificationRankingManager(
+                        () -> mock(NotificationMediaManager.class),
+                        mGroupManager,
+                        mHeadsUpManager,
+                        mock(NotificationFilter.class),
+                        mock(NotifLog.class),
+                        mock(NotificationSectionsFeatureManager.class)
+                ),
+                mock(NotificationEntryManager.KeyguardEnvironment.class));
         mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
         Dependency.get(InitController.class).executePostInitTasks();
         mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, mHeadsUpManager);
@@ -161,8 +177,8 @@
         // The actual class under test.  You may need to work with this class directly when
         // testing anonymous class members of mStackScroller, like mMenuEventListener,
         // which refer to members of NotificationStackScrollLayout. The spy
-        // holds a copy of the CUT's instances of these classes, so they still refer to the CUT's
-        // member variables, not the spy's member variables.
+        // holds a copy of the CUT's instances of these KeyguardBypassController, so they still
+        // refer to the CUT's member variables, not the spy's member variables.
         mStackScrollerInternal = new NotificationStackScrollLayout(getContext(), null,
                 true /* allowLongPress */, mNotificationRoundnessManager,
                 mock(DynamicPrivacyController.class),
@@ -293,7 +309,7 @@
     @Test
     public void testUpdateFooter_noNotifications() {
         setBarStateForTest(StatusBarState.SHADE);
-        assertEquals(0, mNotificationData.getActiveNotifications().size());
+        assertEquals(0, mEntryManager.getActiveNotificationsCount());
 
         mStackScroller.updateFooter();
         verify(mStackScroller, atLeastOnce()).updateFooterView(false, false);
@@ -303,8 +319,8 @@
     public void testUpdateFooter_remoteInput() {
         setBarStateForTest(StatusBarState.SHADE);
         ArrayList<NotificationEntry> entries = new ArrayList<>();
-        entries.add(mock(NotificationEntry.class));
-        when(mNotificationData.getActiveNotifications()).thenReturn(entries);
+        entries.add(new NotificationEntryBuilder().build());
+        addEntriesToEntryManager(entries);
 
         ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
         when(row.canViewBeDismissed()).thenReturn(true);
@@ -319,9 +335,10 @@
     @Test
     public void testUpdateFooter_oneClearableNotification() {
         setBarStateForTest(StatusBarState.SHADE);
+
         ArrayList<NotificationEntry> entries = new ArrayList<>();
-        entries.add(mock(NotificationEntry.class));
-        when(mNotificationData.getActiveNotifications()).thenReturn(entries);
+        entries.add(new NotificationEntryBuilder().build());
+        addEntriesToEntryManager(entries);
 
         ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
         when(row.canViewBeDismissed()).thenReturn(true);
@@ -335,10 +352,10 @@
     @Test
     public void testUpdateFooter_oneNonClearableNotification() {
         setBarStateForTest(StatusBarState.SHADE);
+
         ArrayList<NotificationEntry> entries = new ArrayList<>();
-        entries.add(mock(NotificationEntry.class));
-        when(mEntryManager.getNotificationData().getActiveNotifications()).thenReturn(entries);
-        assertTrue(mEntryManager.getNotificationData().getActiveNotifications().size() != 0);
+        entries.add(new NotificationEntryBuilder().build());
+        addEntriesToEntryManager(entries);
 
         mStackScroller.updateFooter();
         verify(mStackScroller).updateFooterView(true, false);
@@ -460,4 +477,14 @@
         // rather than the mock because the spy just coppied the anonymous inner /shruggie.
         mStackScroller.setStatusBarState(state);
     }
+
+    private void addEntriesToEntryManager(List<NotificationEntry> entries) {
+        for (NotificationEntry e : entries) {
+            mEntryManager.addActiveNotificationForTest(e);
+        }
+    }
+
+    private void addActiveNotificationsToManager(List<NotificationEntry> entries) {
+        mEntryManager.setActiveNotificationList(entries);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
index 64c1b51..a024454 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
@@ -40,7 +40,6 @@
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import org.junit.Before;
@@ -51,8 +50,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.ArrayList;
-
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
@@ -61,7 +58,6 @@
     private static final int LIGHTS_OUT = APPEARANCE_LOW_PROFILE_BARS;
 
     @Mock private NotificationEntryManager mEntryManager;
-    @Mock private NotificationData mNotificationData;
     @Mock private CommandQueue mCommandQueue;
     @Mock private WindowManager mWindowManager;
     @Mock private Display mDisplay;
@@ -71,7 +67,6 @@
 
     private View mLightsOutView;
     private LightsOutNotifController mLightsOutNotifController;
-    private ArrayList<NotificationEntry> mActiveNotifications = new ArrayList<>();
     private int mDisplayId;
     private NotificationEntryListener mEntryListener;
     private CommandQueue.Callbacks mCallbacks;
@@ -81,8 +76,6 @@
         MockitoAnnotations.initMocks(this);
         mDisplayId = mContext.getDisplayId();
         mLightsOutView = new View(mContext);
-        when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
-        when(mNotificationData.getActiveNotifications()).thenReturn(mActiveNotifications);
         when(mWindowManager.getDefaultDisplay()).thenReturn(mDisplay);
         when(mDisplay.getDisplayId()).thenReturn(mDisplayId);
 
@@ -136,7 +129,7 @@
     @Test
     public void testLightsOut_withNotifs_onSystemBarAppearanceChanged() {
         // GIVEN active visible notifications
-        mActiveNotifications.add(mock(NotificationEntry.class));
+        when(mEntryManager.hasActiveNotifications()).thenReturn(true);
 
         // WHEN lights out
         mCallbacks.onSystemBarAppearanceChanged(
@@ -153,7 +146,7 @@
     @Test
     public void testLightsOut_withoutNotifs_onSystemBarAppearanceChanged() {
         // GIVEN no active visible notifications
-        mActiveNotifications.clear();
+        when(mEntryManager.hasActiveNotifications()).thenReturn(false);
 
         // WHEN lights out
         mCallbacks.onSystemBarAppearanceChanged(
@@ -170,7 +163,7 @@
     @Test
     public void testLightsOn_afterLightsOut_onSystemBarAppearanceChanged() {
         // GIVEN active visible notifications
-        mActiveNotifications.add(mock(NotificationEntry.class));
+        when(mEntryManager.hasActiveNotifications()).thenReturn(true);
 
         // WHEN lights on
         mCallbacks.onSystemBarAppearanceChanged(
@@ -187,13 +180,13 @@
     @Test
     public void testEntryAdded() {
         // GIVEN no visible notifications and lights out
-        mActiveNotifications.clear();
+        when(mEntryManager.hasActiveNotifications()).thenReturn(false);
         mLightsOutNotifController.mAppearance = LIGHTS_OUT;
         mLightsOutNotifController.updateLightsOutView();
         assertIsShowingDot(false);
 
         // WHEN an active notification is added
-        mActiveNotifications.add(mock(NotificationEntry.class));
+        when(mEntryManager.hasActiveNotifications()).thenReturn(true);
         assertTrue(mLightsOutNotifController.shouldShowDot());
         mEntryListener.onNotificationAdded(mock(NotificationEntry.class));
 
@@ -204,13 +197,13 @@
     @Test
     public void testEntryRemoved() {
         // GIVEN a visible notification and lights out
-        mActiveNotifications.add(mock(NotificationEntry.class));
+        when(mEntryManager.hasActiveNotifications()).thenReturn(true);
         mLightsOutNotifController.mAppearance = LIGHTS_OUT;
         mLightsOutNotifController.updateLightsOutView();
         assertIsShowingDot(true);
 
         // WHEN all active notifications are removed
-        mActiveNotifications.clear();
+        when(mEntryManager.hasActiveNotifications()).thenReturn(false);
         assertFalse(mLightsOutNotifController.shouldShowDot());
         mEntryListener.onEntryRemoved(mock(NotificationEntry.class), null, false);
 
@@ -221,13 +214,13 @@
     @Test
     public void testEntryUpdated() {
         // GIVEN no visible notifications and lights out
-        mActiveNotifications.clear();
+        when(mEntryManager.hasActiveNotifications()).thenReturn(false);
         mLightsOutNotifController.mAppearance = LIGHTS_OUT;
         mLightsOutNotifController.updateLightsOutView();
         assertIsShowingDot(false);
 
         // WHEN an active notification is added
-        mActiveNotifications.add(mock(NotificationEntry.class));
+        when(mEntryManager.hasActiveNotifications()).thenReturn(true);
         assertTrue(mLightsOutNotifController.shouldShowDot());
         mEntryListener.onPostEntryUpdated(mock(NotificationEntry.class));
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 280cc90..c165e56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -54,11 +54,9 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
 import com.android.systemui.statusbar.notification.logging.NotifLog;
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -239,11 +237,10 @@
                     mock(ShadeController.class),
                     mock(NotificationLockscreenUserManager.class),
                     new NotificationEntryManager(
-                            new NotificationData(
-                                    mock(NotificationSectionsFeatureManager.class),
-                                    mock(NotifLog.class),
-                                    mock(PeopleNotificationIdentifier.class)),
-                            mock(NotifLog.class)),
+                            mock(NotifLog.class),
+                            mock(NotificationGroupManager.class),
+                            mock(NotificationRankingManager.class),
+                            mock(NotificationEntryManager.KeyguardEnvironment.class)),
                     mock(KeyguardStateController.class),
                     statusBarStateController,
                     mock(DozeLog.class),
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 d8a68b0..24a5d93 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
@@ -68,7 +68,6 @@
 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.NotificationData;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -117,7 +116,6 @@
     @Mock
     private Intent mContentIntentInner;
     @Mock
-    private NotificationData mNotificationData;
 
     private NotificationActivityStarter mNotificationActivityStarter;
 
@@ -134,7 +132,6 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
-        when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
 
         when(mContentIntent.isActivity()).thenReturn(true);
         when(mContentIntent.getCreatorUserHandle()).thenReturn(UserHandle.of(1));
@@ -157,7 +154,7 @@
         mActiveNotifications = new ArrayList<>();
         mActiveNotifications.add(mNotificationRow.getEntry());
         mActiveNotifications.add(mBubbleNotificationRow.getEntry());
-        when(mNotificationData.getActiveNotifications()).thenReturn(mActiveNotifications);
+        when(mEntryManager.getVisibleNotifications()).thenReturn(mActiveNotifications);
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
 
         mNotificationActivityStarter = new StatusBarNotificationActivityStarter(getContext(),
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 de87d31..1c02b60 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
@@ -52,7 +52,6 @@
 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.NotificationData;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -100,11 +99,9 @@
         mDependency.injectMockDependency(NotificationGutsManager.class);
         mDependency.injectMockDependency(StatusBarWindowController.class);
         mDependency.injectMockDependency(InitController.class);
-        NotificationData notificationData = mock(NotificationData.class);
-        when(notificationData.getNotificationsForCurrentUser()).thenReturn(new ArrayList<>());
         NotificationEntryManager entryManager =
                 mDependency.injectMockDependency(NotificationEntryManager.class);
-        when(entryManager.getNotificationData()).thenReturn(notificationData);
+        when(entryManager.getActiveNotificationsForCurrentUser()).thenReturn(new ArrayList<>());
 
         StatusBarWindowView statusBarWindowView = mock(StatusBarWindowView.class);
         when(statusBarWindowView.getResources()).thenReturn(mContext.getResources());
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 f327378..95929c3a 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
@@ -101,7 +101,6 @@
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
 import com.android.systemui.statusbar.PulseExpansionHandler;
@@ -120,12 +119,9 @@
 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.NotificationData;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.logging.NotifLog;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -177,7 +173,6 @@
     @Mock private ArrayList<NotificationEntry> mNotificationList;
     @Mock private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
     @Mock private BiometricUnlockController mBiometricUnlockController;
-    @Mock private NotificationData mNotificationData;
     @Mock private NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor;
     @Mock private VisualStabilityManager mVisualStabilityManager;
     @Mock private NotificationListener mNotificationListener;
@@ -240,6 +235,7 @@
     @Mock private ViewMediatorCallback mViewMediatorCallback;
     @Mock private DismissCallbackRegistry mDismissCallbackRegistry;
     @Mock private ScreenPinningRequest mScreenPinningRequest;
+    @Mock private NotificationEntryManager mEntryManager;
 
     @Before
     public void setup() throws Exception {
@@ -260,10 +256,8 @@
         mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
 
         mMetricsLogger = new FakeMetricsLogger();
-        TestableNotificationEntryManager entryManager = new TestableNotificationEntryManager(
-                mNotificationData);
         NotificationLogger notificationLogger = new NotificationLogger(mNotificationListener,
-                Dependency.get(UiOffloadThread.class), entryManager, mStatusBarStateController,
+                Dependency.get(UiOffloadThread.class), mEntryManager, mStatusBarStateController,
                 mExpansionStateLogger);
         notificationLogger.setVisibilityReporter(mock(Runnable.class));
 
@@ -332,7 +326,7 @@
                 ),
                 mNotificationGutsManager,
                 notificationLogger,
-                entryManager,
+                mEntryManager,
                 mNotificationInterruptionStateProvider,
                 mNotificationViewHierarchyManager,
                 mKeyguardViewMediator,
@@ -407,9 +401,6 @@
         mStatusBar.mStatusBarWindowViewController = mStatusBarWindowViewController;
         mStatusBar.startKeyguard();
         Dependency.get(InitController.class).executePostInitTasks();
-        entryManager.setUpForTest(mock(NotificationPresenter.class), mStackScroller,
-                mHeadsUpManager);
-        entryManager.addNotificationEntryListener(mEntryListener);
         notificationLogger.setUpWithContainer(mStackScroller);
     }
 
@@ -645,8 +636,7 @@
     public void testPanelOpenForHeadsUp() {
         when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
-        when(mNotificationData.getActiveNotifications()).thenReturn(mNotificationList);
-        when(mNotificationList.size()).thenReturn(5);
+        when(mEntryManager.getActiveNotificationsCount()).thenReturn(5);
         when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(true);
         mStatusBar.setBarStateForTest(StatusBarState.SHADE);
 
@@ -664,8 +654,8 @@
     @Test
     public void testPanelOpenAndClear() {
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
-        when(mNotificationData.getActiveNotifications()).thenReturn(mNotificationList);
-        when(mNotificationList.size()).thenReturn(5);
+        when(mEntryManager.getActiveNotificationsCount()).thenReturn(5);
+
         when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(false);
         mStatusBar.setBarStateForTest(StatusBarState.SHADE);
 
@@ -683,8 +673,7 @@
     @Test
     public void testPanelOpenAndNoClear() {
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
-        when(mNotificationData.getActiveNotifications()).thenReturn(mNotificationList);
-        when(mNotificationList.size()).thenReturn(5);
+        when(mEntryManager.getActiveNotificationsCount()).thenReturn(5);
         when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(false);
         mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
 
@@ -842,19 +831,6 @@
                 any(UserHandle.class));
     }
 
-    public static class TestableNotificationEntryManager extends NotificationEntryManager {
-
-        public TestableNotificationEntryManager(NotificationData notificationData) {
-            super(notificationData, mock(NotifLog.class));
-        }
-
-        public void setUpForTest(NotificationPresenter presenter,
-                NotificationListContainer listContainer,
-                HeadsUpManagerPhone headsUpManager) {
-            super.setUpWithPresenter(presenter, listContainer, headsUpManager);
-        }
-    }
-
     public static class TestableNotificationInterruptionStateProvider extends
             NotificationInterruptionStateProvider {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java
index 0bc7868a..e15ca1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java
@@ -64,7 +64,8 @@
                 .getDefaultSensor(Sensor.TYPE_PROXIMITY);
         if (proxSensor == null) {
             // No prox? Let's create a fake one!
-            proxSensor = createSensor(Sensor.TYPE_PROXIMITY, null);
+            proxSensor =
+                    createSensor(Sensor.TYPE_PROXIMITY, null, 1 /* SENSOR_FLAG_WAKE_UP_SENSOR */);
         }
 
         mSensors = new FakeGenericSensor[]{
@@ -92,18 +93,6 @@
         if (s != null) {
             return s;
         }
-        switch(type) {
-            case Sensor.TYPE_PROXIMITY:
-                try {
-                    return createSensor(Sensor.TYPE_PROXIMITY, null);
-                } catch (Exception e) {
-                    // fall through
-                }
-                break;
-            default:
-                break;
-
-        }
         // Our mock sensors aren't wakeup, and it's a pain to create them that way. Instead, just
         // return non-wakeup sensors if we can't find a wakeup sensor.
         return getDefaultSensor(type, false /* wakeup */);
@@ -208,6 +197,10 @@
     }
 
     private Sensor createSensor(int type, @Nullable String stringType) throws Exception {
+        return createSensor(type, stringType, 0 /* flags */);
+    }
+
+    private Sensor createSensor(int type, @Nullable String stringType, int flags) throws Exception {
         Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
         constr.setAccessible(true);
         Sensor sensor = constr.newInstance();
@@ -225,7 +218,7 @@
         setSensorField(sensor, "mPower", 1);
         setSensorField(sensor, "mMinDelay", 1000);
         setSensorField(sensor, "mMaxDelay", 1000000000);
-        setSensorField(sensor, "mFlags", 0);
+        setSensorField(sensor, "mFlags", flags);
         setSensorField(sensor, "mId", -1);
 
         return sensor;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 6f43529..7fdd83b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -98,6 +98,7 @@
 import com.android.internal.util.IntPair;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
 import org.xmlpull.v1.XmlPullParserException;
@@ -179,6 +180,8 @@
 
     private final AccessibilityDisplayListener mA11yDisplayListener;
 
+    private final ActivityTaskManagerInternal mActivityTaskManagerService;
+
     private final MainHandler mMainHandler;
 
     private final SystemActionPerformer mSystemActionPerformer;
@@ -260,6 +263,7 @@
                 mWindowManagerService, this, mSecurityPolicy, this);
         mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
         mSecurityPolicy.setAccessibilityWindowManager(mA11yWindowManager);
+        mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
 
         registerBroadcastReceivers();
         new AccessibilityContentObserver(mMainHandler).register(
@@ -1435,7 +1439,7 @@
                     service = new AccessibilityServiceConnection(userState, mContext, componentName,
                             installedService, sIdCounter++, mMainHandler, mLock, mSecurityPolicy,
                             this, mWindowManagerService, mSystemActionPerformer,
-                            mA11yWindowManager);
+                            mA11yWindowManager, mActivityTaskManagerService);
                 } else if (userState.mBoundServices.contains(service)) {
                     continue;
                 }
@@ -2411,7 +2415,7 @@
                     userState, mContext,
                     COMPONENT_NAME, info, sIdCounter++, mMainHandler, mLock, mSecurityPolicy,
                     AccessibilityManagerService.this, mWindowManagerService,
-                    mSystemActionPerformer, mA11yWindowManager) {
+                    mSystemActionPerformer, mA11yWindowManager, mActivityTaskManagerService) {
                 @Override
                 public boolean supportsFlagForNotImportantViews(AccessibilityServiceInfo info) {
                     return true;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index a0a755a..6cadb6d 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -35,6 +35,7 @@
 import android.util.Slog;
 import android.view.Display;
 
+import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
 import java.lang.ref.WeakReference;
@@ -59,6 +60,7 @@
     */
     final WeakReference<AccessibilityUserState> mUserStateWeakReference;
     final Intent mIntent;
+    final ActivityTaskManagerInternal mActivityTaskManagerService;
 
     private final Handler mMainHandler;
 
@@ -67,7 +69,8 @@
             AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
             Object lock, AccessibilitySecurityPolicy securityPolicy, SystemSupport systemSupport,
             WindowManagerInternal windowManagerInternal,
-            SystemActionPerformer systemActionPerfomer, AccessibilityWindowManager awm) {
+            SystemActionPerformer systemActionPerfomer, AccessibilityWindowManager awm,
+            ActivityTaskManagerInternal activityTaskManagerService) {
         super(context, componentName, accessibilityServiceInfo, id, mainHandler, lock,
                 securityPolicy, systemSupport, windowManagerInternal, systemActionPerfomer, awm);
         mUserStateWeakReference = new WeakReference<AccessibilityUserState>(userState);
@@ -75,6 +78,7 @@
         mMainHandler = mainHandler;
         mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
                 com.android.internal.R.string.accessibility_binding_label);
+        mActivityTaskManagerService = activityTaskManagerService;
         final long identity = Binder.clearCallingIdentity();
         try {
             mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, mSystemSupport.getPendingIntentActivity(
@@ -101,6 +105,9 @@
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
+        mActivityTaskManagerService.setAllowAppSwitches(mComponentName.flattenToString(),
+                mAccessibilityServiceInfo.getResolveInfo().serviceInfo.applicationInfo.uid,
+                userState.mUserId);
     }
 
     public void unbindLocked() {
@@ -109,6 +116,8 @@
         if (userState == null) return;
         userState.removeServiceLocked(this);
         mSystemSupport.getMagnificationController().resetAllIfNeeded(mId);
+        mActivityTaskManagerService.setAllowAppSwitches(mComponentName.flattenToString(), -1,
+                userState.mUserId);
         resetLocked();
     }
 
@@ -207,6 +216,11 @@
     @Override
     public void onServiceDisconnected(ComponentName componentName) {
         binderDied();
+        AccessibilityUserState userState = mUserStateWeakReference.get();
+        if (userState != null) {
+            mActivityTaskManagerService.setAllowAppSwitches(mComponentName.flattenToString(), -1,
+                    userState.mUserId);
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f27f707..9a92778 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -2915,11 +2915,14 @@
             final boolean currentMute = AudioSystem.isMicrophoneMuted();
             final long identity = Binder.clearCallingIdentity();
             AudioSystem.muteMicrophone(muted);
-            Binder.restoreCallingIdentity(identity);
-            if (muted != currentMute) {
-                mContext.sendBroadcastAsUser(
+            try {
+                if (muted != currentMute) {
+                    mContext.sendBroadcastAsUser(
                         new Intent(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED)
                                 .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
             }
         }
     }
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 4a62bc5..bc7307b 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -394,6 +394,15 @@
      *     allowed.
      */
     @Override
+    public void notifyChange(Uri[] uris, IContentObserver observer,
+            boolean observerWantsSelfNotifications, int flags, int userHandle,
+            int targetSdkVersion, String callingPackage) {
+        for (Uri uri : uris) {
+            notifyChange(uri, observer, observerWantsSelfNotifications, flags, userHandle,
+                    targetSdkVersion, callingPackage);
+        }
+    }
+
     public void notifyChange(Uri uri, IContentObserver observer,
             boolean observerWantsSelfNotifications, int flags, int userHandle,
             int targetSdkVersion, String callingPackage) {
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
index 99b1ef4..f05b2bf 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
@@ -16,8 +16,15 @@
 
 package com.android.server.notification;
 
+import android.app.AlarmManager;
 import android.app.NotificationHistory;
 import android.app.NotificationHistory.HistoricalNotification;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
 import android.os.Handler;
 import android.util.AtomicFile;
 import android.util.Slog;
@@ -33,11 +40,15 @@
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.attribute.BasicFileAttributes;
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.GregorianCalendar;
 import java.util.Iterator;
 import java.util.LinkedList;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Provides an interface to write and query for notification history data for a user from a Protocol
@@ -52,32 +63,48 @@
     private static final String TAG = "NotiHistoryDatabase";
     private static final boolean DEBUG = NotificationManagerService.DBG;
     private static final int HISTORY_RETENTION_DAYS = 2;
+    private static final int HISTORY_RETENTION_MS = 24 * 60 * 60 * 1000;
     private static final long WRITE_BUFFER_INTERVAL_MS = 1000 * 60 * 20;
 
+    private static final String ACTION_HISTORY_DELETION =
+            NotificationHistoryDatabase.class.getSimpleName() + ".CLEANUP";
+    private static final int REQUEST_CODE_DELETION = 1;
+    private static final String SCHEME_DELETION = "delete";
+    private static final String EXTRA_KEY = "key";
+
+    private final Context mContext;
+    private final AlarmManager mAlarmManager;
     private final Object mLock = new Object();
     private Handler mFileWriteHandler;
     @VisibleForTesting
     // List of files holding history information, sorted newest to oldest
     final LinkedList<AtomicFile> mHistoryFiles;
-    private final GregorianCalendar mCal;
     private final File mHistoryDir;
     private final File mVersionFile;
     // Current version of the database files schema
     private int mCurrentVersion;
     private final WriteBufferRunnable mWriteBufferRunnable;
+    private final FileAttrProvider mFileAttrProvider;
 
     // Object containing posted notifications that have not yet been written to disk
     @VisibleForTesting
     NotificationHistory mBuffer;
 
-    public NotificationHistoryDatabase(File dir) {
+    public NotificationHistoryDatabase(Context context, File dir,
+            FileAttrProvider fileAttrProvider) {
+        mContext = context;
+        mAlarmManager = context.getSystemService(AlarmManager.class);
         mCurrentVersion = DEFAULT_CURRENT_VERSION;
         mVersionFile = new File(dir, "version");
         mHistoryDir = new File(dir, "history");
         mHistoryFiles = new LinkedList<>();
-        mCal = new GregorianCalendar();
         mBuffer = new NotificationHistory();
         mWriteBufferRunnable = new WriteBufferRunnable();
+        mFileAttrProvider = fileAttrProvider;
+
+        IntentFilter deletionFilter = new IntentFilter(ACTION_HISTORY_DELETION);
+        deletionFilter.addDataScheme(SCHEME_DELETION);
+        mContext.registerReceiver(mFileCleaupReceiver, deletionFilter);
     }
 
     public void init(Handler fileWriteHandler) {
@@ -105,7 +132,8 @@
         }
 
         // Sort with newest files first
-        Arrays.sort(files, (lhs, rhs) -> Long.compare(rhs.lastModified(), lhs.lastModified()));
+        Arrays.sort(files, (lhs, rhs) -> Long.compare(mFileAttrProvider.getCreationTime(rhs),
+                mFileAttrProvider.getCreationTime(lhs)));
 
         for (File file : files) {
             mHistoryFiles.addLast(new AtomicFile(file));
@@ -197,31 +225,48 @@
     }
 
     /**
-     * Remove any files that are too old.
+     * Remove any files that are too old and schedule jobs to clean up the rest
      */
     public void prune(final int retentionDays, final long currentTimeMillis) {
         synchronized (mLock) {
-            mCal.setTimeInMillis(currentTimeMillis);
-            mCal.add(Calendar.DATE, -1 * retentionDays);
+            GregorianCalendar retentionBoundary = new GregorianCalendar();
+            retentionBoundary.setTimeInMillis(currentTimeMillis);
+            retentionBoundary.add(Calendar.DATE, -1 * retentionDays);
 
-            while (!mHistoryFiles.isEmpty()) {
-                final AtomicFile currentOldestFile = mHistoryFiles.getLast();
-                final long age = currentTimeMillis
-                        - currentOldestFile.getBaseFile().lastModified();
-                if (age > mCal.getTimeInMillis()) {
+            for (int i = mHistoryFiles.size() - 1; i >= 0; i--) {
+                final AtomicFile currentOldestFile = mHistoryFiles.get(i);
+                final long creationTime =
+                        mFileAttrProvider.getCreationTime(currentOldestFile.getBaseFile());
+                if (creationTime <= retentionBoundary.getTimeInMillis()) {
                     if (DEBUG) {
                         Slog.d(TAG, "Removed " + currentOldestFile.getBaseFile().getName());
                     }
                     currentOldestFile.delete();
                     mHistoryFiles.removeLast();
                 } else {
-                    // all remaining files are newer than the cut off
-                    return;
+                    // all remaining files are newer than the cut off; schedule jobs to delete
+                    final long deletionTime = creationTime + (retentionDays * HISTORY_RETENTION_MS);
+                    scheduleDeletion(currentOldestFile.getBaseFile(), deletionTime);
                 }
             }
         }
     }
 
+    void scheduleDeletion(File file, long deletionTime) {
+        if (DEBUG) {
+            Slog.d(TAG, "Scheduling deletion for " + file.getName() + " at " + deletionTime);
+        }
+        final PendingIntent pi = PendingIntent.getBroadcast(mContext,
+                REQUEST_CODE_DELETION,
+                new Intent(ACTION_HISTORY_DELETION)
+                        .setData(new Uri.Builder().scheme(SCHEME_DELETION)
+                                .appendPath(file.getAbsolutePath()).build())
+                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+                        .putExtra(EXTRA_KEY, file.getAbsolutePath()),
+                PendingIntent.FLAG_UPDATE_CURRENT);
+        mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, deletionTime, pi);
+    }
+
     private void writeLocked(AtomicFile file, NotificationHistory notifications)
             throws IOException {
         FileOutputStream fos = file.startWrite();
@@ -245,6 +290,25 @@
         }
     }
 
+    private final BroadcastReceiver mFileCleaupReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action == null) {
+                return;
+            }
+            if (ACTION_HISTORY_DELETION.equals(action)) {
+                try {
+                    final String filePath = intent.getStringExtra(EXTRA_KEY);
+                    AtomicFile fileToDelete = new AtomicFile(new File(filePath));
+                    fileToDelete.delete();
+                } catch (Exception e) {
+                    Slog.e(TAG, "Failed to delete notification history file", e);
+                }
+            }
+        }
+    };
+
     private final class WriteBufferRunnable implements Runnable {
         @Override
         public void run() {
@@ -277,10 +341,7 @@
                 // Remove packageName entries from pending history
                 mBuffer.removeNotificationsFromWrite(mPkg);
 
-                // Remove packageName entries from files on disk, and rewrite them to disk
-                // Since we sort by modified date, we have to update the files oldest to newest to
-                // maintain the original ordering
-                Iterator<AtomicFile> historyFileItr = mHistoryFiles.descendingIterator();
+                Iterator<AtomicFile> historyFileItr = mHistoryFiles.iterator();
                 while (historyFileItr.hasNext()) {
                     final AtomicFile af = historyFileItr.next();
                     try {
@@ -297,4 +358,24 @@
             }
         }
     }
+
+    public static final class NotificationHistoryFileAttrProvider implements
+            NotificationHistoryDatabase.FileAttrProvider {
+        final static String TAG = "NotifHistoryFileDate";
+
+        public long getCreationTime(File file) {
+            try {
+                BasicFileAttributes attr = Files.readAttributes(FileSystems.getDefault().getPath(
+                        file.getAbsolutePath()), BasicFileAttributes.class);
+                return attr.creationTime().to(TimeUnit.MILLISECONDS);
+            } catch (Exception e) {
+                Slog.w(TAG, "Cannot read creation data for file; using file name");
+                return Long.valueOf(file.getName());
+            }
+        }
+    }
+
+    interface FileAttrProvider {
+        long getCreationTime(File file);
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 631df0ff..38649a7 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2213,8 +2213,10 @@
         final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED);
 
         // Send broadcast to default launcher only if it's a new install
+        // TODO(b/144270665): Secure the usage of this broadcast.
         final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING);
-        if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()) {
+        if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()
+                && (params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
             mPm.sendSessionCommitBroadcast(generateInfo(), userId);
         }
 
diff --git a/services/core/java/com/android/server/policy/LegacyGlobalActions.java b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
index 3aafd5e..c779ebf 100644
--- a/services/core/java/com/android/server/policy/LegacyGlobalActions.java
+++ b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
@@ -403,7 +403,7 @@
         public String getStatus() {
             return mContext.getString(
                     com.android.internal.R.string.bugreport_status,
-                    Build.VERSION.RELEASE_OR_CODENAME,
+                    Build.VERSION.RELEASE,
                     Build.ID);
         }
     }
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 9b9f93f..3c4e3f6 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -29,8 +29,10 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -169,6 +171,23 @@
         } catch (RemoteException doesNotHappen) {
             Slog.wtf(LOG_TAG, "Cannot set up app-ops listener");
         }
+
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        intentFilter.addDataScheme("package");
+
+        getContext().registerReceiverAsUser(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                UserHandle user =
+                        UserHandle.getUserHandleForUid(intent.getIntExtra(Intent.EXTRA_UID, -1));
+                new PermissionControllerManager(
+                        getUserContext(getContext(), user), FgThread.getHandler())
+                        .updateUserSensitive();
+            }
+        }, UserHandle.ALL, intentFilter, null, null);
+
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 0a861ad..73034b0 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -548,7 +548,7 @@
     private static boolean hasActivityToBeDrawn(Task t) {
         for (int i = t.getChildCount() - 1; i >= 0; --i) {
             final ActivityRecord r = t.getChildAt(i);
-            if (r.mVisibleRequested && !r.mDrawn && !r.finishing) {
+            if (r.visible && !r.mDrawn && !r.finishing) {
                 return true;
             }
         }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 2162bde..3d41608 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -121,7 +121,6 @@
 import static com.android.server.am.ActivityRecordProto.STATE;
 import static com.android.server.am.ActivityRecordProto.TRANSLUCENT;
 import static com.android.server.am.ActivityRecordProto.VISIBLE;
-import static com.android.server.am.ActivityRecordProto.VISIBLE_REQUESTED;
 import static com.android.server.am.EventLogTags.AM_RELAUNCH_ACTIVITY;
 import static com.android.server.am.EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
@@ -173,10 +172,12 @@
 import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutLocked;
 import static com.android.server.wm.AppWindowTokenProto.ALL_DRAWN;
 import static com.android.server.wm.AppWindowTokenProto.APP_STOPPED;
-import static com.android.server.wm.AppWindowTokenProto.CLIENT_VISIBLE;
+import static com.android.server.wm.AppWindowTokenProto.CLIENT_HIDDEN;
 import static com.android.server.wm.AppWindowTokenProto.DEFER_HIDING_CLIENT;
 import static com.android.server.wm.AppWindowTokenProto.FILLS_PARENT;
 import static com.android.server.wm.AppWindowTokenProto.FROZEN_BOUNDS;
+import static com.android.server.wm.AppWindowTokenProto.HIDDEN_REQUESTED;
+import static com.android.server.wm.AppWindowTokenProto.HIDDEN_SET_FROM_TRANSFERRED_STARTING_WINDOW;
 import static com.android.server.wm.AppWindowTokenProto.IS_ANIMATING;
 import static com.android.server.wm.AppWindowTokenProto.IS_WAITING_FOR_TRANSITION_START;
 import static com.android.server.wm.AppWindowTokenProto.LAST_ALL_DRAWN;
@@ -191,7 +192,6 @@
 import static com.android.server.wm.AppWindowTokenProto.STARTING_MOVED;
 import static com.android.server.wm.AppWindowTokenProto.STARTING_WINDOW;
 import static com.android.server.wm.AppWindowTokenProto.THUMBNAIL;
-import static com.android.server.wm.AppWindowTokenProto.VISIBLE_SET_FROM_TRANSFERRED_STARTING_WINDOW;
 import static com.android.server.wm.AppWindowTokenProto.WINDOW_TOKEN;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
@@ -462,16 +462,16 @@
     private boolean keysPaused;     // has key dispatching been paused for it?
     int launchMode;         // the launch mode activity attribute.
     int lockTaskLaunchMode; // the lockTaskMode manifest attribute, subject to override
-    private boolean mVisible;        // Should this token's windows be visible?
+    boolean visible;        // does this activity's window need to be shown?
     boolean visibleIgnoringKeyguard; // is this activity visible, ignoring the fact that Keyguard
                                      // might hide this activity?
-    // True if the visible state of this token was forced to true due to a transferred starting
+    // True if the hidden state of this token was forced to false due to a transferred starting
     // window.
-    private boolean mVisibleSetFromTransferredStartingWindow;
-    // TODO: figure out how to consolidate with the same variable in ActivityRecord.
+    private boolean mHiddenSetFromTransferredStartingWindow;
+    // TODO: figureout how to consolidate with the same variable in ActivityRecord.
     private boolean mDeferHidingClient; // If true we told WM to defer reporting to the client
                                         // process that it is hidden.
-    private boolean mLastDeferHidingClient; // If true we will defer setting mClientVisible to false
+    private boolean mLastDeferHidingClient; // If true we will defer setting mClientHidden to true
                                            // and reporting to the client that it is hidden.
     boolean sleeping;       // have we told the activity to sleep?
     boolean nowVisible;     // is this activity's window visible?
@@ -536,8 +536,8 @@
 
     private Task mLastParent;
 
-    // Have we told the window clients to show themselves?
-    private boolean mClientVisible;
+    // Have we told the window clients to hide themselves?
+    private boolean mClientHidden;
 
     boolean firstWindowDrawn;
     // Last drawn state we reported to the app token.
@@ -622,11 +622,11 @@
     // case do not clear allDrawn until the animation completes.
     boolean deferClearAllDrawn;
 
-    // Is this window's surface needed?  This is almost like visible, except
-    // it will sometimes be true a little earlier: when the activity record has
+    // Is this window's surface needed?  This is almost like hidden, except
+    // it will sometimes be true a little earlier: when the token has
     // been shown, but is still waiting for its app transition to execute
     // before making its windows shown.
-    boolean mVisibleRequested;
+    boolean hiddenRequested;
 
     // Last visibility state we reported to the app token.
     boolean reportedVisible;
@@ -836,6 +836,7 @@
                 pw.print(" finishing="); pw.println(finishing);
         pw.print(prefix); pw.print("keysPaused="); pw.print(keysPaused);
                 pw.print(" inHistory="); pw.print(inHistory);
+                pw.print(" visible="); pw.print(visible);
                 pw.print(" sleeping="); pw.print(sleeping);
                 pw.print(" idle="); pw.print(idle);
                 pw.print(" mStartingWindowState=");
@@ -854,14 +855,12 @@
             pw.println(requestedVrComponent);
         }
         super.dump(pw, prefix, dumpAll);
-        pw.print(" visible="); pw.print(mVisible);
         if (appToken != null) {
             pw.println(prefix + "app=true mVoiceInteraction=" + mVoiceInteraction);
         }
         pw.print(prefix); pw.print(" mOccludesParent="); pw.print(mOccludesParent);
         pw.print(" mOrientation="); pw.println(mOrientation);
-        pw.println(prefix + "mVisibleRequested=" + mVisibleRequested
-                + " mClientVisible=" + mClientVisible
+        pw.println(prefix + "hiddenRequested=" + hiddenRequested + " mClientHidden=" + mClientHidden
                 + ((mDeferHidingClient) ? " mDeferHidingClient=" + mDeferHidingClient : "")
                 + " reportedDrawn=" + reportedDrawn + " reportedVisible=" + reportedVisible);
         if (paused) {
@@ -886,13 +885,13 @@
             pw.print(" mIsExiting="); pw.println(mIsExiting);
         }
         if (startingWindow != null || startingSurface != null
-                || startingDisplayed || startingMoved || mVisibleSetFromTransferredStartingWindow) {
+                || startingDisplayed || startingMoved || mHiddenSetFromTransferredStartingWindow) {
             pw.print(prefix); pw.print("startingWindow="); pw.print(startingWindow);
             pw.print(" startingSurface="); pw.print(startingSurface);
             pw.print(" startingDisplayed="); pw.print(startingDisplayed);
             pw.print(" startingMoved="); pw.print(startingMoved);
             pw.println(" mHiddenSetFromTransferredStartingWindow="
-                    + mVisibleSetFromTransferredStartingWindow);
+                    + mHiddenSetFromTransferredStartingWindow);
         }
         if (!mFrozenBounds.isEmpty()) {
             pw.print(prefix); pw.print("mFrozenBounds="); pw.println(mFrozenBounds);
@@ -1488,8 +1487,8 @@
         }
 
         // Application tokens start out hidden.
-        setVisible(false);
-        mVisibleRequested = false;
+        setHidden(true);
+        hiddenRequested = true;
 
         ColorDisplayService.ColorDisplayServiceInternal cds = LocalServices.getService(
                 ColorDisplayService.ColorDisplayServiceInternal.class);
@@ -1518,9 +1517,9 @@
         deferRelaunchUntilPaused = false;
         keysPaused = false;
         inHistory = false;
+        visible = false;
         nowVisible = false;
         mDrawn = false;
-        mClientVisible = true;
         idle = false;
         hasBeenLaunched = false;
         mStackSupervisor = supervisor;
@@ -2201,7 +2200,7 @@
      * 2. App is delayed closing since it might enter PIP.
      */
     boolean isClosingOrEnteringPip() {
-        return (isAnimating(TRANSITION | PARENTS) && !mVisibleRequested) || mWillCloseOrEnterPip;
+        return (isAnimating(TRANSITION | PARENTS) && hiddenRequested) || mWillCloseOrEnterPip;
     }
     /**
      * @return Whether AppOps allows this package to enter picture-in-picture.
@@ -2460,7 +2459,7 @@
                     mAtmService.getLockTaskController().clearLockedTask(task);
                 }
             } else if (!isState(PAUSING)) {
-                if (mVisibleRequested) {
+                if (visible) {
                     // Prepare and execute close transition.
                     prepareActivityHideTransitionAnimation(transit);
                 }
@@ -2539,13 +2538,12 @@
         // TODO(b/137329632): find the next activity directly underneath this one, not just anywhere
         final ActivityRecord next = getDisplay().topRunningActivity(
                 true /* considerKeyguardState */);
-        final boolean isVisible = mVisibleRequested || nowVisible;
+        final boolean isVisible = visible || nowVisible;
         // isNextNotYetVisible is to check if the next activity is invisible, or it has been
         // requested to be invisible but its windows haven't reported as invisible.  If so, it
         // implied that the current finishing activity should be added into stopping list rather
         // than destroy immediately.
-        final boolean isNextNotYetVisible = next != null
-                && (!next.nowVisible || !next.mVisibleRequested);
+        final boolean isNextNotYetVisible = next != null && (!next.nowVisible || !next.visible);
         if (isVisible && isNextNotYetVisible) {
             // Add this activity to the list of stopping activities. It will be processed and
             // destroyed when the next activity reports idle.
@@ -3213,7 +3211,7 @@
                         "Removing starting %s from %s", tStartingWindow, fromActivity);
                 fromActivity.removeChild(tStartingWindow);
                 fromActivity.postWindowRemoveStartingWindowCleanup(tStartingWindow);
-                fromActivity.mVisibleSetFromTransferredStartingWindow = false;
+                fromActivity.mHiddenSetFromTransferredStartingWindow = false;
                 addWindow(tStartingWindow);
 
                 // Propagate other interesting state between the tokens. If the old token is displayed,
@@ -3226,12 +3224,12 @@
                 if (fromActivity.firstWindowDrawn) {
                     firstWindowDrawn = true;
                 }
-                if (fromActivity.isVisible()) {
-                    setVisible(true);
-                    mVisibleRequested = true;
-                    mVisibleSetFromTransferredStartingWindow = true;
+                if (!fromActivity.isHidden()) {
+                    setHidden(false);
+                    hiddenRequested = false;
+                    mHiddenSetFromTransferredStartingWindow = true;
                 }
-                setClientVisible(fromActivity.mClientVisible);
+                setClientHidden(fromActivity.mClientHidden);
 
                 transferAnimation(fromActivity);
 
@@ -3277,7 +3275,7 @@
             if (fromActivity == this) {
                 return;
             }
-            if (!fromActivity.mVisibleRequested && transferStartingWindow(fromActivity.token)) {
+            if (fromActivity.hiddenRequested && transferStartingWindow(fromActivity.token)) {
                 return;
             }
         }
@@ -3792,10 +3790,6 @@
         return opts;
     }
 
-    boolean allowMoveToFront() {
-        return pendingOptions == null || !pendingOptions.getAvoidMoveToFront();
-    }
-
     void removeUriPermissionsLocked() {
         if (uriPermissions != null) {
             uriPermissions.removeUriPermissions();
@@ -3832,7 +3826,7 @@
             return;
         }
         mDeferHidingClient = deferHidingClient;
-        if (!mDeferHidingClient && !mVisibleRequested) {
+        if (!mDeferHidingClient && !visible) {
             // Hiding the client is no longer deferred and the app isn't visible still, go ahead and
             // update the visibility.
             setVisibility(false);
@@ -3843,14 +3837,7 @@
     boolean isVisible() {
         // If the activity isn't hidden then it is considered visible and there is no need to check
         // its children windows to see if they are visible.
-        return mVisible;
-    }
-
-    void setVisible(boolean visible) {
-        if (visible != mVisible) {
-            mVisible = visible;
-            scheduleAnimation();
-        }
+        return !isHidden();
     }
 
     void setVisibility(boolean visible) {
@@ -3859,17 +3846,20 @@
                     + appToken);
             return;
         }
-        if (visible) {
-            mDeferHidingClient = false;
-        }
         setVisibility(visible, mDeferHidingClient);
         mAtmService.addWindowLayoutReasons(
                 ActivityTaskManagerService.LAYOUT_REASON_VISIBILITY_CHANGED);
         mStackSupervisor.getActivityMetricsLogger().notifyVisibilityChanged(this);
+    }
+
+    // TODO: Look into merging with #commitVisibility()
+    void setVisible(boolean newVisible) {
+        visible = newVisible;
+        mDeferHidingClient = !visible && mDeferHidingClient;
+        setVisibility(visible);
         mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = true;
     }
 
-    @VisibleForTesting
     void setVisibility(boolean visible, boolean deferHidingClient) {
         final AppTransition appTransition = getDisplayContent().mAppTransition;
 
@@ -3880,20 +3870,20 @@
         // transition can be selected.
         // TODO: Probably a good idea to separate the concept of opening/closing apps from the
         // concept of setting visibility...
-        if (!visible && !mVisibleRequested) {
+        if (!visible && hiddenRequested) {
 
             if (!deferHidingClient && mLastDeferHidingClient) {
                 // We previously deferred telling the client to hide itself when visibility was
                 // initially set to false. Now we would like it to hide, so go ahead and set it.
                 mLastDeferHidingClient = deferHidingClient;
-                setClientVisible(false);
+                setClientHidden(true);
             }
             return;
         }
 
         ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
-                "setAppVisibility(%s, visible=%b): %s visible=%b mVisibleRequested=%b Callers=%s",
-                appToken, visible, appTransition, isVisible(), mVisibleRequested,
+                "setAppVisibility(%s, visible=%b): %s hidden=%b hiddenRequested=%b Callers=%s",
+                appToken, visible, appTransition, isHidden(), hiddenRequested,
                 Debug.getCallers(6));
 
         final DisplayContent displayContent = getDisplayContent();
@@ -3904,7 +3894,7 @@
         }
         displayContent.mChangingApps.remove(this);
         waitingToShow = false;
-        mVisibleRequested = visible;
+        hiddenRequested = !visible;
         mLastDeferHidingClient = deferHidingClient;
 
         if (!visible) {
@@ -3923,15 +3913,15 @@
             startingMoved = false;
             // If the token is currently hidden (should be the common case), or has been
             // stopped, then we need to set up to wait for its windows to be ready.
-            if (!isVisible() || mAppStopped) {
+            if (isHidden() || mAppStopped) {
                 clearAllDrawn();
 
                 // If the app was already visible, don't reset the waitingToShow state.
-                if (!isVisible()) {
+                if (isHidden()) {
                     waitingToShow = true;
 
                     // If the client isn't hidden, we don't need to reset the drawing state.
-                    if (!isClientVisible()) {
+                    if (isClientHidden()) {
                         // Let's reset the draw state in order to prevent the starting window to be
                         // immediately dismissed when the app still has the surface.
                         forAllWindows(w -> {
@@ -3951,7 +3941,7 @@
             // we still need to tell the client to make its windows visible so they get drawn.
             // Otherwise, we will wait on performing the transition until all windows have been
             // drawn, they never will be, and we are sad.
-            setClientVisible(true);
+            setClientHidden(false);
 
             requestUpdateWallpaperIfNeeded();
 
@@ -3997,9 +3987,9 @@
             boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) {
 
         boolean delayed = false;
-        // Reset the state of mVisibleSetFromTransferredStartingWindow since visibility is actually
+        // Reset the state of mHiddenSetFromTransferredStartingWindow since visibility is actually
         // been set by the app now.
-        mVisibleSetFromTransferredStartingWindow = false;
+        mHiddenSetFromTransferredStartingWindow = false;
 
         // Allow for state changes and animation to be applied if:
         // * token is transitioning visibility state
@@ -4009,7 +3999,7 @@
         // * or the token is the opening app and visible while opening task behind existing one.
         final DisplayContent displayContent = getDisplayContent();
         boolean visibilityChanged = false;
-        if (isVisible() != visible || (!isVisible() && mIsExiting)
+        if (isHidden() == visible || (isHidden() && mIsExiting)
                 || (visible && waitingForReplacement())
                 || (visible && displayContent.mOpeningApps.contains(this)
                 && displayContent.mAppTransition.getAppTransition() == TRANSIT_TASK_OPEN_BEHIND)) {
@@ -4017,7 +4007,7 @@
                     mWmService.mAccessibilityController;
             boolean changed = false;
             ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
-                    "Changing app %s visible=%b performLayout=%b", this, isVisible(),
+                    "Changing app %s hidden=%b performLayout=%b", this, isHidden(),
                     performLayout);
 
             boolean runningAppAnimation = false;
@@ -4042,8 +4032,8 @@
                 changed |= win.onAppVisibilityChanged(visible, runningAppAnimation);
             }
 
-            setVisible(visible);
-            mVisibleRequested = visible;
+            setHidden(!visible);
+            hiddenRequested = !visible;
             visibilityChanged = true;
             if (!visible) {
                 stopFreezingScreen(true, true);
@@ -4061,8 +4051,8 @@
             }
 
             ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
-                    "commitVisibility: %s: visible=%b visibleRequested=%b", this,
-                    isVisible(), mVisibleRequested);
+                    "commitVisibility: %s: hidden=%b hiddenRequested=%b", this,
+                    isHidden(), hiddenRequested);
 
             if (changed) {
                 displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();
@@ -4099,7 +4089,7 @@
             // If we're becoming invisible, update the client visibility if we are not running an
             // animation. Otherwise, we'll update client visibility in onAnimationFinished.
             if (visible || !isAnimating()) {
-                setClientVisible(visible);
+                setClientHidden(!visible);
             }
 
             if (!displayContent.mClosingApps.contains(this)
@@ -4126,7 +4116,7 @@
             // no animation but there will still be a transition set.
             // We still need to delay hiding the surface such that it
             // can be synchronized with showing the next surface in the transition.
-            if (!isVisible() && !delayed && !displayContent.mAppTransition.isTransitionSet()) {
+            if (isHidden() && !delayed && !displayContent.mAppTransition.isTransitionSet()) {
                 SurfaceControl.openTransaction();
                 for (int i = mChildren.size() - 1; i >= 0; i--) {
                     mChildren.get(i).mWinAnimator.hide("immediately hidden");
@@ -4139,6 +4129,12 @@
     }
 
     @Override
+    void setHidden(boolean hidden) {
+        super.setHidden(hidden);
+        scheduleAnimation();
+    }
+
+    @Override
     void onAppTransitionDone() {
         sendingToBottom = false;
     }
@@ -4401,7 +4397,7 @@
                 updateOptionsLocked(returningOptions);
                 stack.mUndrawnActivitiesBelowTopTranslucent.add(this);
             }
-            setVisibility(true);
+            setVisible(true);
             sleeping = false;
             app.postPendingUiCleanMsg(true);
             if (reportToClient) {
@@ -4437,7 +4433,7 @@
     }
 
     void makeInvisible() {
-        if (!mVisibleRequested) {
+        if (!visible) {
             if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Already invisible: " + this);
             return;
         }
@@ -4459,7 +4455,7 @@
             final boolean deferHidingClient = canEnterPictureInPicture
                     && !isState(STOPPING, STOPPED, PAUSED);
             setDeferHidingClient(deferHidingClient);
-            setVisibility(false);
+            setVisible(false);
 
             switch (getState()) {
                 case STOPPING:
@@ -4646,8 +4642,8 @@
      * state to match that fact.
      */
     void completeResumeLocked() {
-        final boolean wasVisible = mVisibleRequested;
-        setVisibility(true);
+        final boolean wasVisible = visible;
+        setVisible(true);
         if (!wasVisible) {
             // Visibility has changed, so take a note of it so we call the TaskStackChangedListener
             mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = true;
@@ -4731,16 +4727,15 @@
             }
             setState(STOPPING, "stopIfPossible");
             if (DEBUG_VISIBILITY) {
-                Slog.v(TAG_VISIBILITY, "Stopping visibleRequested="
-                        + mVisibleRequested + " for " + this);
+                Slog.v(TAG_VISIBILITY, "Stopping visible=" + visible + " for " + this);
             }
-            if (!mVisibleRequested) {
-                setVisibility(false);
+            if (!visible) {
+                setVisible(false);
             }
             EventLogTags.writeAmStopActivity(
                     mUserId, System.identityHashCode(this), shortComponentName);
             mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
-                    StopActivityItem.obtain(mVisibleRequested, configChangeFlags));
+                    StopActivityItem.obtain(visible, configChangeFlags));
             if (stack.shouldSleepOrShutDownActivities()) {
                 setSleeping(true);
             }
@@ -4903,10 +4898,10 @@
 
     void startFreezingScreen() {
         ProtoLog.i(WM_DEBUG_ORIENTATION,
-                "Set freezing of %s: visible=%b freezing=%b visibleRequested=%b. %s",
-                appToken, isVisible(), mFreezingScreen, mVisibleRequested,
+                "Set freezing of %s: hidden=%b freezing=%b hiddenRequested=%b. %s",
+                appToken, isHidden(), mFreezingScreen, hiddenRequested,
                 new RuntimeException().fillInStackTrace());
-        if (mVisibleRequested) {
+        if (!hiddenRequested) {
             if (!mFreezingScreen) {
                 mFreezingScreen = true;
                 mWmService.registerAppFreezeListener(this);
@@ -4942,8 +4937,8 @@
                 return;
             }
             ProtoLog.v(WM_DEBUG_ORIENTATION,
-                        "Clear freezing of %s: visible=%b freezing=%b", appToken,
-                                isVisible(), isFreezingScreen());
+                        "Clear freezing of %s: hidden=%b freezing=%b", appToken,
+                                isHidden(), isFreezingScreen());
             stopFreezingScreen(true, force);
         }
     }
@@ -5103,7 +5098,7 @@
         boolean nowGone = mReportedVisibilityResults.nowGone;
 
         boolean nowDrawn = numInteresting > 0 && numDrawn >= numInteresting;
-        boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting && isVisible();
+        boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting && !isHidden();
         if (!nowGone) {
             // If the app is not yet gone, then it can only become visible/drawn.
             if (!nowDrawn) {
@@ -5131,18 +5126,18 @@
         }
     }
 
-    boolean isClientVisible() {
-        return mClientVisible;
+    boolean isClientHidden() {
+        return mClientHidden;
     }
 
-    void setClientVisible(boolean clientVisible) {
-        if (mClientVisible == clientVisible || (!clientVisible && mDeferHidingClient)) {
+    void setClientHidden(boolean hideClient) {
+        if (mClientHidden == hideClient || (hideClient && mDeferHidingClient)) {
             return;
         }
         ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
-                "setClientVisible: %s clientVisible=%b Callers=%s", this, clientVisible,
+                "setClientHidden: %s clientHidden=%b Callers=%s", this, hideClient,
                 Debug.getCallers(5));
-        mClientVisible = clientVisible;
+        mClientHidden = hideClient;
         sendAppVisibilityToClients();
     }
 
@@ -5185,7 +5180,7 @@
                     Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceController
                             + " pv=" + w.isVisibleByPolicy()
                             + " mDrawState=" + winAnimator.drawStateToString()
-                            + " ph=" + w.isParentWindowHidden() + " th=" + mVisibleRequested
+                            + " ph=" + w.isParentWindowHidden() + " th=" + hiddenRequested
                             + " a=" + isAnimating(TRANSITION));
                 }
             }
@@ -5293,7 +5288,7 @@
      * currently pausing, or is resumed.
      */
     public boolean isInterestingToUserLocked() {
-        return mVisibleRequested || nowVisible || mState == PAUSING || mState == RESUMED;
+        return visible || nowVisible || mState == PAUSING || mState == RESUMED;
     }
 
     void setSleeping(boolean _sleeping) {
@@ -5367,7 +5362,7 @@
             // We're not ready for this kind of thing.
             return false;
         }
-        if (mVisibleRequested) {
+        if (visible) {
             // The user would notice this!
             return false;
         }
@@ -5464,11 +5459,11 @@
             // well there is no point now.
             ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Nulling last startingData");
             mStartingData = null;
-            if (mVisibleSetFromTransferredStartingWindow) {
-                // We set the visible state to true for the token from a transferred starting
-                // window. We now reset it back to false since the starting window was the last
-                // window in the token.
-                setVisible(false);
+            if (mHiddenSetFromTransferredStartingWindow) {
+                // We set the hidden state to false for the token from a transferred starting window.
+                // We now reset it back to true since the starting window was the last window in the
+                // token.
+                setHidden(true);
             }
         } else if (mChildren.size() == 1 && startingSurface != null && !isRelaunching()) {
             // If this is the last window except for a starting transition window,
@@ -5806,7 +5801,7 @@
 
     @Override
     void prepareSurfaces() {
-        final boolean show = isVisible() || isAnimating();
+        final boolean show = !isHidden() || isAnimating();
 
         if (mSurfaceControl != null) {
             if (show && !mLastSurfaceShowing) {
@@ -5925,7 +5920,7 @@
                 "AppWindowToken");
 
         clearThumbnail();
-        setClientVisible(isVisible() || mVisibleRequested);
+        setClientHidden(isHidden() && hiddenRequested);
 
         getDisplayContent().computeImeTargetIfNeeded(this);
 
@@ -6522,7 +6517,7 @@
         if (display == null) {
             return;
         }
-        if (mVisibleRequested) {
+        if (visible) {
             // It may toggle the UI for user to restart the size compatibility mode activity.
             display.handleActivitySizeCompatModeIfNeeded(this);
         } else if (mCompatDisplayInsets != null) {
@@ -6819,7 +6814,7 @@
             } else {
                 if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                         "Config is relaunching " + this);
-                if (DEBUG_STATES && !mVisibleRequested) {
+                if (DEBUG_STATES && !visible) {
                     Slog.v(TAG_STATES, "Config is relaunching invisible activity " + this
                             + " called by " + Debug.getCallers(4));
                 }
@@ -7005,7 +7000,7 @@
         // Reset the existing override configuration so it can be updated according to the latest
         // configuration.
         clearSizeCompatMode();
-        if (mVisibleRequested) {
+        if (visible) {
             // Configuration will be ensured when becoming visible, so if it is already visible,
             // then the manual update is needed.
             updateSizeCompatMode();
@@ -7018,7 +7013,7 @@
         // The restarting state avoids removing this record when process is died.
         setState(RESTARTING_PROCESS, "restartActivityProcess");
 
-        if (!mVisibleRequested || mHaveState) {
+        if (!visible || mHaveState) {
             // Kill its process immediately because the activity should be in background.
             // The activity state will be update to {@link #DESTROYED} in
             // {@link ActivityStack#cleanUp} when handling process died.
@@ -7309,13 +7304,12 @@
         writeToProto(proto, APP_WINDOW_TOKEN, WindowTraceLogLevel.ALL);
         writeIdentifierToProto(proto, IDENTIFIER);
         proto.write(STATE, mState.toString());
-        proto.write(VISIBLE_REQUESTED, mVisibleRequested);
+        proto.write(VISIBLE, visible);
         proto.write(FRONT_OF_TASK, isRootOfTask());
         if (hasProcess()) {
             proto.write(PROC_ID, app.getPid());
         }
         proto.write(TRANSLUCENT, !occludesParent());
-        proto.write(VISIBLE, mVisible);
     }
 
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
@@ -7346,8 +7340,8 @@
         }
         proto.write(FILLS_PARENT, mOccludesParent);
         proto.write(APP_STOPPED, mAppStopped);
-        proto.write(com.android.server.wm.AppWindowTokenProto.VISIBLE_REQUESTED, mVisibleRequested);
-        proto.write(CLIENT_VISIBLE, mClientVisible);
+        proto.write(HIDDEN_REQUESTED, hiddenRequested);
+        proto.write(CLIENT_HIDDEN, mClientHidden);
         proto.write(DEFER_HIDING_CLIENT, mDeferHidingClient);
         proto.write(REPORTED_DRAWN, reportedDrawn);
         proto.write(REPORTED_VISIBLE, reportedVisible);
@@ -7361,12 +7355,11 @@
         }
         proto.write(STARTING_DISPLAYED, startingDisplayed);
         proto.write(STARTING_MOVED, startingMoved);
-        proto.write(VISIBLE_SET_FROM_TRANSFERRED_STARTING_WINDOW,
-                mVisibleSetFromTransferredStartingWindow);
+        proto.write(HIDDEN_SET_FROM_TRANSFERRED_STARTING_WINDOW,
+                mHiddenSetFromTransferredStartingWindow);
         for (Rect bounds : mFrozenBounds) {
             bounds.writeToProto(proto, FROZEN_BOUNDS);
         }
-        proto.write(com.android.server.wm.AppWindowTokenProto.VISIBLE, mVisible);
         proto.end(token);
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
index 6e75f9c..c56a9e2 100644
--- a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
+++ b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
@@ -73,7 +73,7 @@
 
     public boolean isActivityVisible() {
         synchronized (mService.mGlobalLock) {
-            return mActivity.mVisibleRequested || mActivity.isState(RESUMED, PAUSING);
+            return mActivity.visible || mActivity.isState(RESUMED, PAUSING);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 2aea817..6f6e659 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -1629,8 +1629,7 @@
                 prev = prev.completeFinishing("completePausedLocked");
             } else if (prev.hasProcess()) {
                 if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueue pending stop if needed: " + prev
-                        + " wasStopping=" + wasStopping
-                        + " visibleRequested=" + prev.mVisibleRequested);
+                        + " wasStopping=" + wasStopping + " visible=" + prev.visible);
                 if (prev.deferRelaunchUntilPaused) {
                     // Complete the deferred relaunch that was waiting for pause to complete.
                     if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Re-launching after pause: " + prev);
@@ -1640,7 +1639,7 @@
                     // We can't clobber it, because the stop confirmation will not be handled.
                     // We don't need to schedule another stop, we only need to let it happen.
                     prev.setState(STOPPING, "completePausedLocked");
-                } else if (!prev.mVisibleRequested || shouldSleepOrShutDownActivities()) {
+                } else if (!prev.visible || shouldSleepOrShutDownActivities()) {
                     // Clear out any deferred client hide we might currently have.
                     prev.setDeferHidingClient(false);
                     // If we were visible then resumeTopActivities will release resources before
@@ -1761,7 +1760,7 @@
 
     boolean isTopActivityVisible() {
         final ActivityRecord topActivity = getTopActivity();
-        return topActivity != null && topActivity.mVisibleRequested;
+        return topActivity != null && topActivity.visible;
     }
 
     /**
@@ -1902,7 +1901,7 @@
         for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
             final Task task = getChildAt(taskNdx);
             ActivityRecord r = task.topRunningActivityLocked();
-            if (r == null || r.finishing || !r.mVisibleRequested) {
+            if (r == null || r.finishing || !r.visible) {
                 task.mLayerRank = -1;
             } else {
                 task.mLayerRank = baseLayer + layer++;
@@ -1991,7 +1990,7 @@
                         if (!r.attachedToProcess()) {
                             makeVisibleAndRestartIfNeeded(starting, configChanges, isTop,
                                     resumeTopActivity && isTop, r);
-                        } else if (r.mVisibleRequested) {
+                        } else if (r.visible) {
                             // If this activity is already visible, then there is nothing to do here.
                             if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
                                     "Skipping: already visible at " + r);
@@ -2168,16 +2167,16 @@
         // invisible. If the app is already visible, it must have died while it was visible. In this
         // case, we'll show the dead window but will not restart the app. Otherwise we could end up
         // thrashing.
-        if (isTop || !r.mVisibleRequested) {
+        if (isTop || !r.visible) {
             // This activity needs to be visible, but isn't even running...
             // get it started and resume if no other stack in this stack is resumed.
             if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Start and freeze screen for " + r);
             if (r != starting) {
                 r.startFreezingScreenLocked(configChanges);
             }
-            if (!r.mVisibleRequested || r.mLaunchTaskBehind) {
+            if (!r.visible || r.mLaunchTaskBehind) {
                 if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Starting and making visible: " + r);
-                r.setVisibility(true);
+                r.setVisible(true);
             }
             if (r != starting) {
                 mStackSupervisor.startSpecificActivityLocked(r, andResume, true /* checkConfig */);
@@ -2620,8 +2619,7 @@
 
         if (next.attachedToProcess()) {
             if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resume running: " + next
-                    + " stopped=" + next.stopped
-                    + " visibleRequested=" + next.mVisibleRequested);
+                    + " stopped=" + next.stopped + " visible=" + next.visible);
 
             // If the previous activity is translucent, force a visibility update of
             // the next activity, so that it's added to WM's opening app list, and
@@ -2636,7 +2634,7 @@
                     && !lastFocusedStack.mLastPausedActivity.occludesParent()));
 
             // This activity is now becoming visible.
-            if (!next.mVisibleRequested || next.stopped || lastActivityTranslucent) {
+            if (!next.visible || next.stopped || lastActivityTranslucent) {
                 next.setVisibility(true);
             }
 
@@ -2691,7 +2689,7 @@
                     // Do over!
                     mStackSupervisor.scheduleResumeTopActivities();
                 }
-                if (!next.mVisibleRequested || next.stopped) {
+                if (!next.visible || next.stopped) {
                     next.setVisibility(true);
                 }
                 next.completeResumeLocked();
@@ -3360,7 +3358,7 @@
 
         final ActivityRecord top = stack.topRunningActivityLocked();
 
-        if (stack.isActivityTypeHome() && (top == null || !top.mVisibleRequested)) {
+        if (stack.isActivityTypeHome() && (top == null || !top.visible)) {
             // If we will be focusing on the home stack next and its current top activity isn't
             // visible, then use the move the home stack task to top to make the activity visible.
             stack.getDisplay().moveHomeActivityToTop(reason);
@@ -3857,7 +3855,7 @@
                         "Record #" + targetIndex + " " + r + ": app=" + r.app);
 
                 if (r.app == app) {
-                    if (r.mVisibleRequested) {
+                    if (r.visible) {
                         hasVisibleActivities = true;
                     }
                     final boolean remove;
@@ -3873,8 +3871,8 @@
                         // Don't currently have state for the activity, or
                         // it is finishing -- always remove it.
                         remove = true;
-                    } else if (!r.mVisibleRequested && r.launchCount > 2
-                            && r.lastLaunchTime > (SystemClock.uptimeMillis() - 60000)) {
+                    } else if (!r.visible && r.launchCount > 2 &&
+                            r.lastLaunchTime > (SystemClock.uptimeMillis() - 60000)) {
                         // We have launched this activity too many times since it was
                         // able to run, so give up and remove it.
                         // (Note if the activity is visible, we don't remove the record.
@@ -3910,7 +3908,7 @@
                         // it died, we leave the dead window on screen so it's basically visible.
                         // This is needed when user later tap on the dead window, we need to stop
                         // other apps when user transfers focus to the restarted activity.
-                        r.nowVisible = r.mVisibleRequested;
+                        r.nowVisible = r.visible;
                     }
                     r.cleanUp(true /* cleanServices */, true /* setState */);
                     if (remove) {
@@ -4092,7 +4090,7 @@
      * Ensures all visible activities at or below the input activity have the right configuration.
      */
     void ensureVisibleActivitiesConfigurationLocked(ActivityRecord start, boolean preserveWindow) {
-        if (start == null || !start.mVisibleRequested) {
+        if (start == null || !start.visible) {
             return;
         }
 
@@ -4487,7 +4485,7 @@
                 final ActivityRecord a = task.getChildAt(activityNdx);
                 if (a.info.packageName.equals(packageName)) {
                     a.forceNewConfig = true;
-                    if (starting != null && a == starting && a.mVisibleRequested) {
+                    if (starting != null && a == starting && a.visible) {
                         a.startFreezingScreenLocked(CONFIG_SCREEN_LAYOUT);
                     }
                 }
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 4828a8d..572bf83 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -773,11 +773,12 @@
             }
 
             if (r.getActivityStack().checkKeyguardVisibility(r, true /* shouldBeVisible */,
-                    true /* isTop */) && r.allowMoveToFront()) {
-                // We only set the visibility to true if the activity is not being launched in
-                // background, and is allowed to be visible based on keyguard state. This avoids
-                // setting this into motion in window manager that is later cancelled due to later
-                // calls to ensure visible activities that set visibility back to false.
+                    true /* isTop */)) {
+                // We only set the visibility to true if the activity is allowed to be visible
+                // based on
+                // keyguard state. This avoids setting this into motion in window manager that is
+                // later cancelled due to later calls to ensure visible activities that set
+                // visibility back to false.
                 r.setVisibility(true);
             }
 
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index ff1b423..bef6af3 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -688,16 +688,15 @@
      * compare z-order.
      *
      * @param apps The list of apps to search.
-     * @param ignoreInvisible If set to true, ignores apps that are not
-     *                        {@link ActivityRecord#isVisible}.
+     * @param ignoreHidden If set to true, ignores apps that are {@link ActivityRecord#isHidden}.
      * @return The top {@link ActivityRecord}.
      */
-    private ActivityRecord getTopApp(ArraySet<ActivityRecord> apps, boolean ignoreInvisible) {
+    private ActivityRecord getTopApp(ArraySet<ActivityRecord> apps, boolean ignoreHidden) {
         int topPrefixOrderIndex = Integer.MIN_VALUE;
         ActivityRecord topApp = null;
         for (int i = apps.size() - 1; i >= 0; i--) {
             final ActivityRecord app = apps.valueAt(i);
-            if (ignoreInvisible && !app.isVisible()) {
+            if (ignoreHidden && app.isHidden()) {
                 continue;
             }
             final int prefixOrderIndex = app.getPrefixOrderIndex();
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 6eb9dba..cb868e1a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -652,12 +652,12 @@
                     + " config reported=" + w.isLastConfigReportedToClient());
             final ActivityRecord activity = w.mActivityRecord;
             if (gone) Slog.v(TAG, "  GONE: mViewVisibility=" + w.mViewVisibility
-                    + " mRelayoutCalled=" + w.mRelayoutCalled + " visible=" + w.mToken.isVisible()
-                    + " visibleRequested=" + (activity != null && activity.mVisibleRequested)
+                    + " mRelayoutCalled=" + w.mRelayoutCalled + " hidden=" + w.mToken.isHidden()
+                    + " hiddenRequested=" + (activity != null && activity.hiddenRequested)
                     + " parentHidden=" + w.isParentWindowHidden());
             else Slog.v(TAG, "  VIS: mViewVisibility=" + w.mViewVisibility
-                    + " mRelayoutCalled=" + w.mRelayoutCalled + " visible=" + w.mToken.isVisible()
-                    + " visibleRequested=" + (activity != null && activity.mVisibleRequested)
+                    + " mRelayoutCalled=" + w.mRelayoutCalled + " hidden=" + w.mToken.isHidden()
+                    + " hiddenRequested=" + (activity != null && activity.hiddenRequested)
                     + " parentHidden=" + w.isParentWindowHidden());
         }
 
@@ -3061,7 +3061,7 @@
                 wsa.destroySurface();
                 mWmService.mForceRemoves.add(w);
                 mTmpWindow = w;
-            } else if (w.mActivityRecord != null && !w.mActivityRecord.isClientVisible()) {
+            } else if (w.mActivityRecord != null && w.mActivityRecord.isClientHidden()) {
                 Slog.w(TAG_WM, "LEAKED SURFACE (app token hidden): "
                         + w + " surface=" + wsa.mSurfaceController
                         + " token=" + w.mActivityRecord);
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index fc74d00..caf95de 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -107,7 +107,7 @@
                 mTargetActivityType);
         ActivityRecord targetActivity = getTargetActivity(targetStack);
         if (targetActivity != null) {
-            if (targetActivity.mVisibleRequested || targetActivity.isTopRunningActivity()) {
+            if (targetActivity.visible || targetActivity.isTopRunningActivity()) {
                 // The activity is ready.
                 return;
             }
@@ -189,7 +189,7 @@
 
         // Send launch hint if we are actually launching the target. If it's already visible
         // (shouldn't happen in general) we don't need to send it.
-        if (targetActivity == null || !targetActivity.mVisibleRequested) {
+        if (targetActivity == null || !targetActivity.visible) {
             mService.mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded(
                     true /* forceSend */, targetActivity);
         }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 520c26e..a4c7bcd 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -60,7 +60,9 @@
 import static com.android.server.EventLogTags.WM_TASK_REMOVED;
 import static com.android.server.am.TaskRecordProto.ACTIVITIES;
 import static com.android.server.am.TaskRecordProto.ACTIVITY_TYPE;
+import static com.android.server.am.TaskRecordProto.BOUNDS;
 import static com.android.server.am.TaskRecordProto.FULLSCREEN;
+import static com.android.server.am.TaskRecordProto.ID;
 import static com.android.server.am.TaskRecordProto.LAST_NON_FULLSCREEN_BOUNDS;
 import static com.android.server.am.TaskRecordProto.MIN_HEIGHT;
 import static com.android.server.am.TaskRecordProto.MIN_WIDTH;
@@ -87,8 +89,10 @@
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.server.wm.TaskProto.APP_WINDOW_TOKENS;
+import static com.android.server.wm.TaskProto.BOUNDS;
 import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS;
 import static com.android.server.wm.TaskProto.FILLS_PARENT;
+import static com.android.server.wm.TaskProto.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.WINDOW_CONTAINER;
@@ -2198,7 +2202,7 @@
     void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
         for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
             final ActivityRecord r = getChildAt(activityNdx);
-            if (r.mVisibleRequested) {
+            if (r.visible) {
                 r.showStartingWindow(null /* prev */, false /* newTask */, taskSwitch);
             }
         }
@@ -2520,7 +2524,7 @@
         for (int i = mChildren.size() - 1; i >= 0; i--) {
             final ActivityRecord token = mChildren.get(i);
             // skip hidden (or about to hide) apps
-            if (token.mIsExiting || !token.isClientVisible() || !token.mVisibleRequested) {
+            if (token.mIsExiting || token.isClientHidden() || token.hiddenRequested) {
                 continue;
             }
             final WindowState win = token.findMainWindow();
@@ -2738,9 +2742,10 @@
 
     ActivityRecord getTopVisibleActivity() {
         for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final ActivityRecord activity = mChildren.get(i);
-            if (!activity.mIsExiting && activity.isClientVisible() && activity.mVisibleRequested) {
-                return activity;
+            final ActivityRecord token = mChildren.get(i);
+            // skip hidden (or about to hide) apps
+            if (!token.mIsExiting && !token.isClientHidden() && !token.hiddenRequested) {
+                return token;
             }
         }
         return null;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 0d4ec65..35f61a8 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm;
 
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+
 import static com.android.server.wm.TaskSnapshotPersister.DISABLE_FULL_SIZED_BITMAPS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -248,6 +250,12 @@
     @Nullable
     SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task,
             float scaleFraction) {
+        return createTaskSnapshot(task, scaleFraction, PixelFormat.RGBA_8888);
+    }
+
+    @Nullable
+    SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task,
+            float scaleFraction, int pixelFormat) {
         if (task.getSurfaceControl() == null) {
             if (DEBUG_SCREENSHOT) {
                 Slog.w(TAG_WM, "Failed to take screenshot. No surface control for " + task);
@@ -258,7 +266,7 @@
         mTmpRect.offsetTo(0, 0);
         final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
                 SurfaceControl.captureLayers(
-                        task.getSurfaceControl(), mTmpRect, scaleFraction);
+                        task.getSurfaceControl(), mTmpRect, scaleFraction, pixelFormat);
         final GraphicBuffer buffer = screenshotBuffer != null ? screenshotBuffer.getGraphicBuffer()
                 : null;
         if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
@@ -299,8 +307,14 @@
             Slog.w(TAG_WM, "Failed to take screenshot. No main window for " + task);
             return null;
         }
+        final boolean isWindowTranslucent = mainWindow.getAttrs().format != PixelFormat.OPAQUE;
+        final boolean isShowWallpaper = (mainWindow.getAttrs().flags & FLAG_SHOW_WALLPAPER) != 0;
+        final int pixelFormat = mPersister.use16BitFormat() && activity.fillsParent()
+                && !(isWindowTranslucent && isShowWallpaper)
+                ? PixelFormat.RGB_565
+                : PixelFormat.RGBA_8888;
         final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
-                createTaskSnapshot(task, scaleFraction);
+                createTaskSnapshot(task, scaleFraction, pixelFormat);
 
         if (screenshotBuffer == null) {
             if (DEBUG_SCREENSHOT) {
@@ -308,7 +322,8 @@
             }
             return null;
         }
-        final boolean isWindowTranslucent = mainWindow.getAttrs().format != PixelFormat.OPAQUE;
+        final boolean isTranslucent = PixelFormat.formatHasAlpha(pixelFormat)
+                && (!activity.fillsParent() || isWindowTranslucent);
         return new TaskSnapshot(
                 System.currentTimeMillis() /* id */,
                 activity.mActivityComponent, screenshotBuffer.getGraphicBuffer(),
@@ -316,7 +331,7 @@
                 activity.getTask().getConfiguration().orientation,
                 getInsets(mainWindow), isLowRamDevice /* reduced */, scaleFraction /* scale */,
                 true /* isRealSnapshot */, task.getWindowingMode(), getSystemUiVisibility(task),
-                !activity.fillsParent() || isWindowTranslucent);
+                isTranslucent);
     }
 
     private boolean shouldDisableSnapshots() {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
index 696e1c3..22c1ea5 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
@@ -75,25 +75,35 @@
             final byte[] bytes = Files.readAllBytes(protoFile.toPath());
             final TaskSnapshotProto proto = TaskSnapshotProto.parseFrom(bytes);
             final Options options = new Options();
-            options.inPreferredConfig = Config.HARDWARE;
+            options.inPreferredConfig = mPersister.use16BitFormat() && !proto.isTranslucent
+                    ? Config.RGB_565
+                    : Config.ARGB_8888;
             final Bitmap bitmap = BitmapFactory.decodeFile(bitmapFile.getPath(), options);
             if (bitmap == null) {
                 Slog.w(TAG, "Failed to load bitmap: " + bitmapFile.getPath());
                 return null;
             }
-            final GraphicBuffer buffer = bitmap.createGraphicBufferHandle();
+
+            final Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false);
+            bitmap.recycle();
+            if (hwBitmap == null) {
+                Slog.w(TAG, "Failed to create hardware bitmap: " + bitmapFile.getPath());
+                return null;
+            }
+            final GraphicBuffer buffer = hwBitmap.createGraphicBufferHandle();
             if (buffer == null) {
                 Slog.w(TAG, "Failed to retrieve gralloc buffer for bitmap: "
                         + bitmapFile.getPath());
                 return null;
             }
+
             final ComponentName topActivityComponent = ComponentName.unflattenFromString(
                     proto.topActivityComponent);
             // For legacy snapshots, restore the scale based on the reduced resolution state
             final float legacyScale = reducedResolution ? mPersister.getReducedScale() : 1f;
             final float scale = Float.compare(proto.scale, 0f) != 0 ? proto.scale : legacyScale;
-            return new TaskSnapshot(proto.id, topActivityComponent, buffer, bitmap.getColorSpace(),
-                    proto.orientation,
+            return new TaskSnapshot(proto.id, topActivityComponent, buffer,
+                    hwBitmap.getColorSpace(), proto.orientation,
                     new Rect(proto.insetLeft, proto.insetTop, proto.insetRight, proto.insetBottom),
                     reducedResolution, scale, proto.isRealSnapshot, proto.windowingMode,
                     proto.systemUiVisibility, proto.isTranslucent);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index a156f5c..5915590 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -74,6 +74,7 @@
     private final Object mLock = new Object();
     private final DirectoryResolver mDirectoryResolver;
     private final float mReducedScale;
+    private final boolean mUse16BitFormat;
 
     /**
      * The list of ids of the tasks that have been persisted since {@link #removeObsoleteFiles} was
@@ -92,6 +93,8 @@
             mReducedScale = ActivityManager.isLowRamDeviceStatic()
                     ? LOW_RAM_REDUCED_SCALE : REDUCED_SCALE;
         }
+        mUse16BitFormat = service.mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_use16BitTaskSnapshotPixelFormat);
     }
 
     /**
@@ -164,6 +167,15 @@
         return mReducedScale;
     }
 
+    /**
+     * Return if task snapshots are stored in 16 bit pixel format.
+     *
+     * @return true if task snapshots are stored in 16 bit pixel format.
+     */
+    boolean use16BitFormat() {
+        return mUse16BitFormat;
+    }
+
     @TestApi
     void waitForQueueEmpty() {
         while (true) {
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 6ff4b2e..3632284 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -120,7 +120,7 @@
         }
 
         mFindResults.resetTopWallpaper = true;
-        if (w.mActivityRecord != null && !w.mActivityRecord.isVisible()
+        if (w.mActivityRecord != null && w.mActivityRecord.isHidden()
                 && !w.mActivityRecord.isAnimating(TRANSITION)) {
 
             // If this window's app token is hidden and not animating, it is of no interest to us.
@@ -278,11 +278,9 @@
         for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
             final WallpaperWindowToken token = mWallpaperTokens.get(i);
             token.hideWallpaperToken(wasDeferred, "hideWallpapers");
-            if (DEBUG_WALLPAPER_LIGHT && token.isVisible()) {
-                Slog.d(TAG, "Hiding wallpaper " + token
-                        + " from " + winGoingAway + " target=" + mWallpaperTarget + " prev="
-                        + mPrevWallpaperTarget + "\n" + Debug.getCallers(5, "  "));
-            }
+            if (DEBUG_WALLPAPER_LIGHT && !token.isHidden()) Slog.d(TAG, "Hiding wallpaper " + token
+                    + " from " + winGoingAway + " target=" + mWallpaperTarget + " prev="
+                    + mPrevWallpaperTarget + "\n" + Debug.getCallers(5, "  "));
         }
     }
 
@@ -534,9 +532,9 @@
         }
 
         final boolean newTargetHidden = wallpaperTarget.mActivityRecord != null
-                && !wallpaperTarget.mActivityRecord.mVisibleRequested;
+                && wallpaperTarget.mActivityRecord.hiddenRequested;
         final boolean oldTargetHidden = prevWallpaperTarget.mActivityRecord != null
-                && !prevWallpaperTarget.mActivityRecord.mVisibleRequested;
+                && prevWallpaperTarget.mActivityRecord.hiddenRequested;
 
         if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Animating wallpapers:" + " old: "
                 + prevWallpaperTarget + " hidden=" + oldTargetHidden + " new: " + wallpaperTarget
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index d23bf97..528cece 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -56,6 +56,7 @@
             final WindowState wallpaper = mChildren.get(j);
             wallpaper.hideWallpaperWindow(wasDeferred, reason);
         }
+        setHidden(true);
     }
 
     void sendWindowWallpaperCommand(
@@ -87,7 +88,9 @@
         final int dw = displayInfo.logicalWidth;
         final int dh = displayInfo.logicalHeight;
 
-        if (isVisible() != visible) {
+        if (isHidden() == visible) {
+            setHidden(!visible);
+
             // Need to do a layout to ensure the wallpaper now has the correct size.
             mDisplayContent.setLayoutNeeded();
         }
@@ -115,9 +118,10 @@
 
     void updateWallpaperWindows(boolean visible) {
 
-        if (isVisible() != visible) {
+        if (isHidden() == visible) {
             if (DEBUG_WALLPAPER_LIGHT) Slog.d(TAG,
-                    "Wallpaper token " + token + " visible=" + visible);
+                    "Wallpaper token " + token + " hidden=" + !visible);
+            setHidden(!visible);
             // Need to do a layout to ensure the wallpaper now has the correct size.
             mDisplayContent.setLayoutNeeded();
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6e4f1ee..88c0fad 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1702,7 +1702,7 @@
             if (mInTouchMode) {
                 res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
             }
-            if (win.mActivityRecord == null || win.mActivityRecord.isClientVisible()) {
+            if (win.mActivityRecord == null || !win.mActivityRecord.isClientHidden()) {
                 res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
             }
 
@@ -2235,7 +2235,7 @@
             // associated appToken is not hidden.
             final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
                     (win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
-                            || win.mActivityRecord.isClientVisible());
+                            || !win.mActivityRecord.isClientHidden());
 
             // If we are not currently running the exit animation, we need to see about starting
             // one.
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 2e188b7..d63fbc21 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -533,7 +533,7 @@
         synchronized (mAtm.mGlobalLockWithoutBoost) {
             for (int i = mActivities.size() - 1; i >= 0; --i) {
                 final ActivityRecord r = mActivities.get(i);
-                if (r.mVisibleRequested) {
+                if (r.visible) {
                     return true;
                 }
             }
@@ -555,7 +555,7 @@
                 continue;
             }
             ActivityRecord topActivity = task.getTopActivity();
-            if (topActivity != null && topActivity.mVisibleRequested) {
+            if (topActivity != null && topActivity.visible) {
                 return true;
             }
         }
@@ -589,7 +589,7 @@
         // - no longer visible OR
         // - not focusable (in PiP mode for instance)
         if (topDisplay == null
-                || !mPreQTopResumedActivity.mVisibleRequested
+                || !mPreQTopResumedActivity.visible
                 || !mPreQTopResumedActivity.isFocusable()) {
             canUpdate = true;
         }
@@ -739,7 +739,7 @@
             }
             // Don't consider any activities that are currently not in a state where they
             // can be destroyed.
-            if (r.mVisibleRequested || !r.stopped || !r.hasSavedState()
+            if (r.visible || !r.stopped || !r.hasSavedState()
                     || r.isState(STARTED, RESUMED, PAUSING, PAUSED, STOPPING)) {
                 if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Not releasing in-use activity: " + r);
                 continue;
@@ -793,7 +793,7 @@
                         continue;
                     }
                 }
-                if (r.mVisibleRequested) {
+                if (r.visible) {
                     final Task task = r.getTask();
                     if (task != null && minTaskLayer > 0) {
                         final int layer = task.mLayerRank;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b816831..62a3512 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1539,7 +1539,7 @@
      */
     // TODO: Can we consolidate this with #isVisible() or have a more appropriate name for this?
     boolean isWinVisibleLw() {
-        return (mActivityRecord == null || mActivityRecord.mVisibleRequested
+        return (mActivityRecord == null || !mActivityRecord.hiddenRequested
                 || mActivityRecord.isAnimating(TRANSITION)) && isVisible();
     }
 
@@ -1548,7 +1548,7 @@
      * not the pending requested hidden state.
      */
     boolean isVisibleNow() {
-        return (mToken.isVisible() || mAttrs.type == TYPE_APPLICATION_STARTING)
+        return (!mToken.isHidden() || mAttrs.type == TYPE_APPLICATION_STARTING)
                 && isVisible();
     }
 
@@ -1570,7 +1570,7 @@
         final ActivityRecord atoken = mActivityRecord;
         return (mHasSurface || (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
                 && isVisibleByPolicy() && !isParentWindowHidden()
-                && (atoken == null || atoken.mVisibleRequested)
+                && (atoken == null || !atoken.hiddenRequested)
                 && !mAnimatingExit && !mDestroying;
     }
 
@@ -1585,7 +1585,7 @@
         }
         final ActivityRecord atoken = mActivityRecord;
         if (atoken != null) {
-            return ((!isParentWindowHidden() && atoken.mVisibleRequested)
+            return ((!isParentWindowHidden() && !atoken.hiddenRequested)
                     || isAnimating(TRANSITION | PARENTS));
         }
         return !isParentWindowHidden() || isAnimating(TRANSITION | PARENTS);
@@ -1621,7 +1621,7 @@
             return false;
         }
         final boolean parentAndClientVisible = !isParentWindowHidden()
-                && mViewVisibility == View.VISIBLE && mToken.isVisible();
+                && mViewVisibility == View.VISIBLE && !mToken.isHidden();
         return mHasSurface && isVisibleByPolicy() && !mDestroying
                 && (parentAndClientVisible || isAnimating(TRANSITION | PARENTS));
     }
@@ -1640,7 +1640,7 @@
         } else {
             final Task task = getTask();
             final boolean canFromTask = task != null && task.canAffectSystemUiFlags();
-            return canFromTask && mActivityRecord.isVisible();
+            return canFromTask && !mActivityRecord.isHidden();
         }
     }
 
@@ -1652,7 +1652,7 @@
     public boolean isDisplayedLw() {
         final ActivityRecord atoken = mActivityRecord;
         return isDrawnLw() && isVisibleByPolicy()
-                && ((!isParentWindowHidden() && (atoken == null || atoken.mVisibleRequested))
+                && ((!isParentWindowHidden() && (atoken == null || !atoken.hiddenRequested))
                         || isAnimating(TRANSITION | PARENTS));
     }
 
@@ -1669,8 +1669,8 @@
         final ActivityRecord atoken = mActivityRecord;
         return mViewVisibility == View.GONE
                 || !mRelayoutCalled
-                || (atoken == null && !mToken.isVisible())
-                || (atoken != null && !atoken.mVisibleRequested)
+                || (atoken == null && mToken.isHidden())
+                || (atoken != null && atoken.hiddenRequested)
                 || isParentWindowGoneForLayout()
                 || (mAnimatingExit && !isAnimatingLw())
                 || mDestroying;
@@ -2162,8 +2162,7 @@
                         + " parentHidden=" + isParentWindowHidden()
                         + " exiting=" + mAnimatingExit + " destroying=" + mDestroying);
                 if (mActivityRecord != null) {
-                    Slog.i(TAG_WM, "  mActivityRecord.visibleRequested="
-                            + mActivityRecord.mVisibleRequested);
+                    Slog.i(TAG_WM, "  mActivityRecord.hiddenRequested=" + mActivityRecord.hiddenRequested);
                 }
             }
         }
@@ -2574,7 +2573,7 @@
      * interacts with it.
      */
     private boolean shouldKeepVisibleDeadAppWindow() {
-        if (!isWinVisibleLw() || mActivityRecord == null || !mActivityRecord.isClientVisible()) {
+        if (!isWinVisibleLw() || mActivityRecord == null || mActivityRecord.isClientHidden()) {
             // Not a visible app window or the app isn't dead.
             return false;
         }
@@ -2611,14 +2610,14 @@
         return showBecauseOfActivity || showBecauseOfWindow;
     }
 
-    /** @return {@code false} if this window desires touch events. */
+    /** @return false if this window desires touch events. */
     boolean cantReceiveTouchInput() {
         if (mActivityRecord == null || mActivityRecord.getTask() == null) {
             return false;
         }
 
         return mActivityRecord.getTask().getTaskStack().shouldIgnoreInput()
-                || !mActivityRecord.mVisibleRequested
+                || mActivityRecord.hiddenRequested
                 || isAnimatingToRecents();
     }
 
@@ -2886,13 +2885,13 @@
     void sendAppVisibilityToClients() {
         super.sendAppVisibilityToClients();
 
-        final boolean clientVisible = mActivityRecord.isClientVisible();
-        if (mAttrs.type == TYPE_APPLICATION_STARTING && !clientVisible) {
+        final boolean clientHidden = mActivityRecord.isClientHidden();
+        if (mAttrs.type == TYPE_APPLICATION_STARTING && clientHidden) {
             // Don't hide the starting window.
             return;
         }
 
-        if (!clientVisible) {
+        if (clientHidden) {
             // Once we are notifying the client that it's visibility has changed, we need to prevent
             // it from destroying child surfaces until the animation has finished. We do this by
             // detaching any surface control the client added from the client.
@@ -2906,8 +2905,8 @@
 
         try {
             if (DEBUG_VISIBILITY) Slog.v(TAG,
-                    "Setting visibility of " + this + ": " + clientVisible);
-            mClient.dispatchAppVisibility(clientVisible);
+                    "Setting visibility of " + this + ": " + (!clientHidden));
+            mClient.dispatchAppVisibility(!clientHidden);
         } catch (RemoteException e) {
         }
     }
@@ -4147,9 +4146,9 @@
                     + " starting=" + (mAttrs.type == TYPE_APPLICATION_STARTING)
                     + " during animation: policyVis=" + isVisibleByPolicy()
                     + " parentHidden=" + isParentWindowHidden()
-                    + " tok.visibleRequested="
-                    + (mActivityRecord != null && mActivityRecord.mVisibleRequested)
-                    + " tok.visible=" + (mActivityRecord != null && mActivityRecord.isVisible())
+                    + " tok.hiddenRequested="
+                    + (mActivityRecord != null && mActivityRecord.hiddenRequested)
+                    + " tok.hidden=" + (mActivityRecord != null && mActivityRecord.isHidden())
                     + " animating=" + isAnimating(TRANSITION | PARENTS)
                     + " tok animating="
                     + (mActivityRecord != null && mActivityRecord.isAnimating(TRANSITION))
@@ -4556,7 +4555,7 @@
                         + " pv=" + isVisibleByPolicy()
                         + " mDrawState=" + mWinAnimator.mDrawState
                         + " ph=" + isParentWindowHidden()
-                        + " th=" + (mActivityRecord != null && mActivityRecord.mVisibleRequested)
+                        + " th=" + (mActivityRecord != null ? mActivityRecord.hiddenRequested : false)
                         + " a=" + isAnimating(TRANSITION | PARENTS));
             }
         }
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 6480a15..88a1458 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -29,6 +29,7 @@
 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.HIDDEN;
 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;
@@ -71,6 +72,9 @@
     // Is key dispatching paused for this token?
     boolean paused = false;
 
+    // Should this token's windows be hidden?
+    private boolean mHidden;
+
     // Temporary for finding which tokens no longer have visible windows.
     boolean hasVisible;
 
@@ -124,6 +128,16 @@
         }
     }
 
+    void setHidden(boolean hidden) {
+        if (hidden != mHidden) {
+            mHidden = hidden;
+        }
+    }
+
+    boolean isHidden() {
+        return mHidden;
+    }
+
     void removeAllWindowsIfPossible() {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             final WindowState win = mChildren.get(i);
@@ -142,7 +156,7 @@
         // This token is exiting, so allow it to be removed when it no longer contains any windows.
         mPersistOnEmpty = false;
 
-        if (!isVisible()) {
+        if (mHidden) {
             return;
         }
 
@@ -155,10 +169,7 @@
             changed |= win.onSetAppExiting();
         }
 
-        final ActivityRecord app = asActivityRecord();
-        if (app != null) {
-            app.setVisible(false);
-        }
+        setHidden(true);
 
         if (changed) {
             mWmService.mWindowPlacerLocked.performSurfacePlacement();
@@ -275,6 +286,7 @@
             final WindowState w = mChildren.get(i);
             w.writeToProto(proto, WINDOWS, logLevel);
         }
+        proto.write(HIDDEN, mHidden);
         proto.write(WAITING_TO_SHOW, waitingToShow);
         proto.write(PAUSED, paused);
         proto.end(token);
@@ -284,7 +296,8 @@
         super.dump(pw, prefix, dumpAll);
         pw.print(prefix); pw.print("windows="); pw.println(mChildren);
         pw.print(prefix); pw.print("windowType="); pw.print(windowType);
-        pw.print(" hasVisible="); pw.println(hasVisible);
+                pw.print(" hidden="); pw.print(mHidden);
+                pw.print(" hasVisible="); pw.println(hasVisible);
         if (waitingToShow || sendingToBottom) {
             pw.print(prefix); pw.print("waitingToShow="); pw.print(waitingToShow);
                     pw.print(" sendingToBottom="); pw.print(sendingToBottom);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index 597d337..99dd9a1 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -44,6 +44,7 @@
 import android.testing.DexmakerShareClassLoaderRule;
 import android.view.Display;
 
+import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
 import org.junit.After;
@@ -80,6 +81,7 @@
     @Mock ResolveInfo mMockResolveInfo;
     @Mock AccessibilitySecurityPolicy mMockSecurityPolicy;
     @Mock AccessibilityWindowManager mMockA11yWindowManager;
+    @Mock ActivityTaskManagerInternal mMockActivityTaskManagerInternal;
     @Mock AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport;
     @Mock WindowManagerInternal mMockWindowManagerInternal;
     @Mock SystemActionPerformer mMockSystemActionPerformer;
@@ -111,7 +113,8 @@
         mConnection = new AccessibilityServiceConnection(mMockUserState, mMockContext,
                 COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, mHandler, new Object(),
                 mMockSecurityPolicy, mMockSystemSupport, mMockWindowManagerInternal,
-                mMockSystemActionPerformer, mMockA11yWindowManager);
+                mMockSystemActionPerformer, mMockA11yWindowManager,
+                mMockActivityTaskManagerInternal);
         when(mMockSecurityPolicy.canPerformGestures(mConnection)).thenReturn(true);
     }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
index bcff2f8..608625f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
@@ -18,6 +18,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -25,7 +26,9 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.AlarmManager;
 import android.app.NotificationHistory.HistoricalNotification;
+import android.content.Context;
 import android.graphics.drawable.Icon;
 import android.os.Handler;
 import android.util.AtomicFile;
@@ -42,8 +45,17 @@
 import org.mockito.MockitoAnnotations;
 
 import java.io.File;
+import java.nio.file.FileSystem;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileTime;
+import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 @RunWith(AndroidJUnit4.class)
 public class NotificationHistoryDatabaseTest extends UiServiceTestCase {
@@ -51,6 +63,11 @@
     File mRootDir;
     @Mock
     Handler mFileWriteHandler;
+    @Mock
+    Context mContext;
+    @Mock
+    AlarmManager mAlarmManager;
+    TestFileAttrProvider mFileAttrProvider;
 
     NotificationHistoryDatabase mDataBase;
 
@@ -85,36 +102,56 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        when(mContext.getSystemService(AlarmManager.class)).thenReturn(mAlarmManager);
+        when(mContext.getUser()).thenReturn(getContext().getUser());
+        when(mContext.getPackageName()).thenReturn(getContext().getPackageName());
 
+        mFileAttrProvider = new TestFileAttrProvider();
         mRootDir = new File(mContext.getFilesDir(), "NotificationHistoryDatabaseTest");
 
-        mDataBase = new NotificationHistoryDatabase(mRootDir);
+        mDataBase = new NotificationHistoryDatabase(mContext, mRootDir, mFileAttrProvider);
         mDataBase.init(mFileWriteHandler);
     }
 
     @Test
-    public void testPrune() {
+    public void testDeletionReceiver() {
+        verify(mContext, times(1)).registerReceiver(any(), any());
+    }
+
+    @Test
+    public void testPrune() throws Exception {
+        GregorianCalendar cal = new GregorianCalendar();
+        cal.setTimeInMillis(10);
         int retainDays = 1;
-        for (long i = 10; i >= 5; i--) {
+
+        List<AtomicFile> expectedFiles = new ArrayList<>();
+
+        // add 5 files with a creation date of "today"
+        for (long i = cal.getTimeInMillis(); i >= 5; i--) {
             File file = mock(File.class);
-            when(file.lastModified()).thenReturn(i);
+            mFileAttrProvider.creationDates.put(file, i);
             AtomicFile af = new AtomicFile(file);
+            expectedFiles.add(af);
             mDataBase.mHistoryFiles.addLast(af);
         }
-        GregorianCalendar cal = new GregorianCalendar();
-        cal.setTimeInMillis(5);
+
         cal.add(Calendar.DATE, -1 * retainDays);
+        // Add 5 more files more than retainDays old
         for (int i = 5; i >= 0; i--) {
             File file = mock(File.class);
-            when(file.lastModified()).thenReturn(cal.getTimeInMillis() - i);
+            mFileAttrProvider.creationDates.put(file, cal.getTimeInMillis() - i);
             AtomicFile af = new AtomicFile(file);
             mDataBase.mHistoryFiles.addLast(af);
         }
-        mDataBase.prune(retainDays, 10);
 
-        for (AtomicFile file : mDataBase.mHistoryFiles) {
-            assertThat(file.getBaseFile().lastModified() > 0);
-        }
+        // back to today; trim everything a day + old
+        cal.add(Calendar.DATE, 1 * retainDays);
+        mDataBase.prune(retainDays, cal.getTimeInMillis());
+
+        assertThat(mDataBase.mHistoryFiles).containsExactlyElementsIn(expectedFiles);
+
+        verify(mAlarmManager, times(6)).setExactAndAllowWhileIdle(anyInt(), anyLong(), any());
+
     }
 
     @Test
@@ -181,4 +218,12 @@
         verify(af2, never()).openRead();
     }
 
+    private class TestFileAttrProvider implements NotificationHistoryDatabase.FileAttrProvider {
+        public Map<File, Long> creationDates = new HashMap<>();
+
+        @Override
+        public long getCreationTime(File file) {
+            return creationDates.get(file);
+        }
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index e560cb9..9df7b45 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -346,7 +346,7 @@
                 ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
         activity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
         activity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-        activity.mVisibleRequested = true;
+        activity.visible = true;
         activity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
 
         final ArrayList<CompletableFuture<IBinder>> resultWrapper = new ArrayList<>();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 734761f..3c619f7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -160,7 +160,7 @@
     public void testOnActivityLaunchCancelled_hasDrawn() {
         onActivityLaunched();
 
-        mTopActivity.mVisibleRequested = mTopActivity.mDrawn = true;
+        mTopActivity.visible = mTopActivity.mDrawn = true;
 
         // Cannot time already-visible activities.
         mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity);
@@ -171,7 +171,7 @@
 
     @Test
     public void testOnActivityLaunchCancelled_finishedBeforeDrawn() {
-        mTopActivity.mVisibleRequested = mTopActivity.mDrawn = true;
+        mTopActivity.visible = mTopActivity.mDrawn = true;
 
         // Suppress resume when creating the record because we want to notify logger manually.
         mSupervisor.beginDeferResume();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index e22c419..c51a46a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -221,7 +221,7 @@
     @Test
     public void testRestartProcessIfVisible() {
         doNothing().when(mSupervisor).scheduleRestartTimeout(mActivity);
-        mActivity.mVisibleRequested = true;
+        mActivity.visible = true;
         mActivity.setSavedState(null /* savedState */);
         mActivity.setState(ActivityStack.ActivityState.RESUMED, "testRestart");
         prepareFixedAspectRatioUnresizableActivity();
@@ -502,7 +502,7 @@
         mTask.setBounds(100, 100, 400, 600);
         mActivity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
         mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-        mActivity.mVisibleRequested = true;
+        mActivity.visible = true;
         ensureActivityConfiguration();
 
         final Rect bounds = new Rect(mActivity.getBounds());
@@ -547,7 +547,7 @@
                 .when(mActivity).getRequestedOrientation();
         mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
         mActivity.info.minAspectRatio = mActivity.info.maxAspectRatio = 1;
-        mActivity.mVisibleRequested = true;
+        mActivity.visible = true;
         ensureActivityConfiguration();
         // The parent configuration doesn't change since the first resolved configuration, so the
         // activity shouldn't be in the size compatibility mode.
@@ -589,7 +589,7 @@
                 .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
                 .setMaxAspectRatio(1.5f)
                 .build();
-        mActivity.mVisibleRequested = true;
+        mActivity.visible = true;
 
         final Rect originalBounds = new Rect(mActivity.getBounds());
         final int originalDpi = mActivity.getConfiguration().densityDpi;
@@ -614,7 +614,7 @@
         mTask.getRequestedOverrideConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT;
         mActivity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
         mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-        mActivity.mVisibleRequested = true;
+        mActivity.visible = true;
 
         ensureActivityConfiguration();
         final Rect originalBounds = new Rect(mActivity.getBounds());
@@ -661,7 +661,7 @@
 
         prepareFixedAspectRatioUnresizableActivity();
         mActivity.setState(STOPPED, "testSizeCompatMode");
-        mActivity.mVisibleRequested = false;
+        mActivity.visible = false;
         mActivity.app.setReportedProcState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY);
         // Make the parent bounds to be different so the activity is in size compatibility mode.
         setupDisplayAndParentSize(600, 1200);
@@ -829,7 +829,7 @@
         // Prepare the activity record to be ready for immediate removal. It should be invisible and
         // have no process. Otherwise, request to finish it will send a message to client first.
         mActivity.setState(STOPPED, "test");
-        mActivity.mVisibleRequested = false;
+        mActivity.visible = false;
         mActivity.nowVisible = false;
         // Set process to 'null' to allow immediate removal, but don't call mActivity.setProcess() -
         // this will cause NPE when updating task's process.
@@ -838,7 +838,7 @@
         // Put a visible activity on top, so the finishing activity doesn't have to wait until the
         // next activity reports idle to destroy it.
         final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
-        topActivity.mVisibleRequested = true;
+        topActivity.visible = true;
         topActivity.nowVisible = true;
         topActivity.setState(RESUMED, "test");
 
@@ -924,7 +924,7 @@
     @Test
     public void testFinishActivityIfPossible_visibleResumedPreparesAppTransition() {
         mActivity.finishing = false;
-        mActivity.mVisibleRequested = true;
+        mActivity.visible = true;
         mActivity.setState(RESUMED, "test");
         mActivity.finishIfPossible("test", false /* oomAdj */);
 
@@ -940,7 +940,7 @@
     @Test
     public void testFinishActivityIfPossible_visibleNotResumedExecutesAppTransition() {
         mActivity.finishing = false;
-        mActivity.mVisibleRequested = true;
+        mActivity.visible = true;
         mActivity.setState(PAUSED, "test");
         mActivity.finishIfPossible("test", false /* oomAdj */);
 
@@ -958,7 +958,7 @@
         // Put an activity on top of test activity to make it invisible and prevent us from
         // accidentally resuming the topmost one again.
         new ActivityBuilder(mService).build();
-        mActivity.mVisibleRequested = false;
+        mActivity.visible = false;
         mActivity.setState(STOPPED, "test");
 
         mActivity.finishIfPossible("test", false /* oomAdj */);
@@ -1010,7 +1010,7 @@
     @Test
     public void testCompleteFinishing_keepStateOfNextInvisible() {
         final ActivityRecord currentTop = mActivity;
-        currentTop.mVisibleRequested = currentTop.nowVisible = true;
+        currentTop.visible = currentTop.nowVisible = true;
 
         // Simulates that {@code currentTop} starts an existing activity from background (so its
         // state is stopped) and the starting flow just goes to place it at top.
@@ -1036,13 +1036,13 @@
     @Test
     public void testCompleteFinishing_waitForNextVisible() {
         final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
-        topActivity.mVisibleRequested = true;
+        topActivity.visible = true;
         topActivity.nowVisible = true;
         topActivity.finishing = true;
         topActivity.setState(PAUSED, "true");
         // Mark the bottom activity as not visible, so that we will wait for it before removing
         // the top one.
-        mActivity.mVisibleRequested = false;
+        mActivity.visible = false;
         mActivity.nowVisible = false;
         mActivity.setState(STOPPED, "test");
 
@@ -1061,13 +1061,13 @@
     @Test
     public void testCompleteFinishing_noWaitForNextVisible_alreadyInvisible() {
         final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
-        topActivity.mVisibleRequested = false;
+        topActivity.visible = false;
         topActivity.nowVisible = false;
         topActivity.finishing = true;
         topActivity.setState(PAUSED, "true");
         // Mark the bottom activity as not visible, so that we would wait for it before removing
         // the top one.
-        mActivity.mVisibleRequested = false;
+        mActivity.visible = false;
         mActivity.nowVisible = false;
         mActivity.setState(STOPPED, "test");
 
@@ -1083,12 +1083,12 @@
     @Test
     public void testCompleteFinishing_waitForIdle() {
         final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
-        topActivity.mVisibleRequested = true;
+        topActivity.visible = true;
         topActivity.nowVisible = true;
         topActivity.finishing = true;
         topActivity.setState(PAUSED, "true");
         // Mark the bottom activity as already visible, so that there is no need to wait for it.
-        mActivity.mVisibleRequested = true;
+        mActivity.visible = true;
         mActivity.nowVisible = true;
         mActivity.setState(RESUMED, "test");
 
@@ -1104,12 +1104,12 @@
     @Test
     public void testCompleteFinishing_noWaitForNextVisible_stopped() {
         final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
-        topActivity.mVisibleRequested = false;
+        topActivity.visible = false;
         topActivity.nowVisible = false;
         topActivity.finishing = true;
         topActivity.setState(STOPPED, "true");
         // Mark the bottom activity as already visible, so that there is no need to wait for it.
-        mActivity.mVisibleRequested = true;
+        mActivity.visible = true;
         mActivity.nowVisible = true;
         mActivity.setState(RESUMED, "test");
 
@@ -1125,12 +1125,12 @@
     @Test
     public void testCompleteFinishing_noWaitForNextVisible_nonFocusedStack() {
         final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
-        topActivity.mVisibleRequested = true;
+        topActivity.visible = true;
         topActivity.nowVisible = true;
         topActivity.finishing = true;
         topActivity.setState(PAUSED, "true");
         // Mark the bottom activity as already visible, so that there is no need to wait for it.
-        mActivity.mVisibleRequested = true;
+        mActivity.visible = true;
         mActivity.nowVisible = true;
         mActivity.setState(RESUMED, "test");
 
@@ -1139,7 +1139,7 @@
         final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
         final ActivityRecord focusedActivity = stack.getChildAt(0).getChildAt(0);
         focusedActivity.nowVisible = true;
-        focusedActivity.mVisibleRequested = true;
+        focusedActivity.visible = true;
         focusedActivity.setState(RESUMED, "test");
         stack.mResumedActivity = focusedActivity;
 
@@ -1346,7 +1346,7 @@
         setupDisplayContentForCompatDisplayInsets();
         mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
         mActivity.info.maxAspectRatio = 1.5f;
-        mActivity.mVisibleRequested = true;
+        mActivity.visible = true;
         ensureActivityConfiguration();
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index fc44652..d0e07b6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -1006,7 +1006,7 @@
 
         // There is still an activity1 in stack1 so the activity2 should be added to finishing list
         // that will be destroyed until idle.
-        stack2.getTopActivity().mVisibleRequested = true;
+        stack2.getTopActivity().visible = true;
         final ActivityRecord activity2 = finishTopActivity(stack2);
         assertEquals(STOPPING, activity2.getState());
         assertThat(mSupervisor.mStoppingActivities).contains(activity2);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 1632681..47b39b0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -271,7 +271,7 @@
                 doReturn(true).when(activity).occludesParent();
                 mTask.addChild(activity);
                 // Make visible by default...
-                activity.setVisible(true);
+                activity.setHidden(false);
             }
 
             final WindowProcessController wpc = new WindowProcessController(mService,
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index d415f25..6020453 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -62,7 +62,7 @@
         final ActivityRecord translucentOpening = createActivityRecord(mDisplayContent,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
         translucentOpening.setOccludesParent(false);
-        translucentOpening.setVisible(false);
+        translucentOpening.setHidden(true);
         mDisplayContent.mOpeningApps.add(behind);
         mDisplayContent.mOpeningApps.add(translucentOpening);
         assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN,
@@ -90,7 +90,7 @@
         final ActivityRecord translucentOpening = createActivityRecord(mDisplayContent,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
         translucentOpening.setOccludesParent(false);
-        translucentOpening.setVisible(false);
+        translucentOpening.setHidden(true);
         mDisplayContent.mOpeningApps.add(behind);
         mDisplayContent.mOpeningApps.add(translucentOpening);
         assertEquals(TRANSIT_TASK_CHANGE_WINDOWING_MODE,
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index d491569..bd336ad 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -252,7 +252,7 @@
     @Test
     @Presubmit
     public void testGetOrientation() {
-        mActivity.setVisible(true);
+        mActivity.setHidden(false);
 
         mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
 
@@ -261,7 +261,7 @@
         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mActivity.getOrientation());
 
         mActivity.setOccludesParent(true);
-        mActivity.setVisible(false);
+        mActivity.setHidden(true);
         mActivity.sendingToBottom = true;
         // Can not specify orientation if app isn't visible even though it occludes parent.
         assertEquals(SCREEN_ORIENTATION_UNSET, mActivity.getOrientation());
@@ -314,7 +314,7 @@
 
     @Test
     public void testSetOrientation() {
-        mActivity.setVisible(true);
+        mActivity.setHidden(false);
 
         // Assert orientation is unspecified to start.
         assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mActivity.getOrientation());
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 8db4858..9f4143f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -394,7 +394,7 @@
         assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
 
         // Make sure top focused display not changed if there is a focused app.
-        window1.mActivityRecord.mVisibleRequested = false;
+        window1.mActivityRecord.hiddenRequested = true;
         window1.getDisplayContent().setFocusedApp(window1.mActivityRecord);
         updateFocusedWindow();
         assertTrue(!window1.isFocused());
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 1abd366..7026004 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -149,7 +149,7 @@
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
         final ActivityRecord hiddenActivity = createActivityRecord(mDisplayContent,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
-        hiddenActivity.setVisible(false);
+        hiddenActivity.setHidden(true);
         mDisplayContent.getConfiguration().windowConfiguration.setRotation(
                 mDisplayContent.getRotation());
         mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray(), homeActivity);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 06d96fee3..41cbd81 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -106,12 +106,12 @@
         RecentsAnimationCallbacks recentsAnimation = startRecentsActivity(
                 mRecentsComponent, true /* getRecentsAnimation */);
         // The launch-behind state should make the recents activity visible.
-        assertTrue(recentActivity.mVisibleRequested);
+        assertTrue(recentActivity.visible);
 
         // Simulate the animation is cancelled without changing the stack order.
         recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, false /* sendUserLeaveHint */);
         // The non-top recents activity should be invisible by the restored launch-behind state.
-        assertFalse(recentActivity.mVisibleRequested);
+        assertFalse(recentActivity.visible);
     }
 
     @Test
@@ -158,7 +158,7 @@
         // The activity is started in background so it should be invisible and will be stopped.
         assertThat(recentsActivity).isNotNull();
         assertThat(mSupervisor.mStoppingActivities).contains(recentsActivity);
-        assertFalse(recentsActivity.mVisibleRequested);
+        assertFalse(recentsActivity.visible);
 
         // Assume it is stopped to test next use case.
         recentsActivity.activityStoppedLocked(null /* newIcicle */, null /* newPersistentState */,
@@ -361,7 +361,7 @@
                 true);
 
         // Ensure we find the task for the right user and it is made visible
-        assertTrue(otherUserHomeActivity.mVisibleRequested);
+        assertTrue(otherUserHomeActivity.visible);
     }
 
     private void startRecentsActivity() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 0b7cbce..e1f92dd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -383,11 +383,11 @@
     @Test
     public void testCanAffectSystemUiFlags() {
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
-        app.mActivityRecord.setVisible(true);
+        app.mToken.setHidden(false);
         assertTrue(app.canAffectSystemUiFlags());
-        app.mActivityRecord.setVisible(false);
+        app.mToken.setHidden(true);
         assertFalse(app.canAffectSystemUiFlags());
-        app.mActivityRecord.setVisible(true);
+        app.mToken.setHidden(false);
         app.mAttrs.alpha = 0.0f;
         assertFalse(app.canAffectSystemUiFlags());
     }
@@ -395,7 +395,7 @@
     @Test
     public void testCanAffectSystemUiFlags_disallow() {
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
-        app.mActivityRecord.setVisible(true);
+        app.mToken.setHidden(false);
         assertTrue(app.canAffectSystemUiFlags());
         app.getTask().setCanAffectSystemUiFlags(false);
         assertFalse(app.canAffectSystemUiFlags());
@@ -569,7 +569,7 @@
     @Test
     public void testCantReceiveTouchWhenAppTokenHiddenRequested() {
         final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
-        win0.mActivityRecord.mVisibleRequested = false;
+        win0.mActivityRecord.hiddenRequested = true;
         assertTrue(win0.cantReceiveTouchInput());
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index 26743c8..797a6bc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -74,8 +74,8 @@
     private static void postCreateActivitySetup(ActivityRecord activity, DisplayContent dc) {
         activity.onDisplayChanged(dc);
         activity.setOccludesParent(true);
-        activity.setVisible(true);
-        activity.mVisibleRequested = true;
+        activity.setHidden(false);
+        activity.hiddenRequested = false;
     }
 
     static TestWindowToken createTestWindowToken(int type, DisplayContent dc) {
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 047fcec..9967beb 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -488,7 +488,7 @@
      *  Opens the specified USB device
      */
     public ParcelFileDescriptor openDevice(String deviceAddress,
-            UsbUserPermissionManager permissions, String packageName, int uid) {
+            UsbUserPermissionManager permissions, String packageName, int pid, int uid) {
         synchronized (mLock) {
             if (isBlackListed(deviceAddress)) {
                 throw new SecurityException("USB device is on a restricted bus");
@@ -500,7 +500,7 @@
                         "device " + deviceAddress + " does not exist or is restricted");
             }
 
-            permissions.checkPermission(device, packageName, uid);
+            permissions.checkPermission(device, packageName, pid, uid);
             return nativeOpenDevice(deviceAddress);
         }
     }
diff --git a/services/usb/java/com/android/server/usb/UsbSerialReader.java b/services/usb/java/com/android/server/usb/UsbSerialReader.java
index 3151679..86016bb 100644
--- a/services/usb/java/com/android/server/usb/UsbSerialReader.java
+++ b/services/usb/java/com/android/server/usb/UsbSerialReader.java
@@ -93,7 +93,7 @@
                         int userId = UserHandle.getUserId(uid);
                         if (mDevice instanceof UsbDevice) {
                             mPermissionManager.getPermissionsForUser(userId)
-                                    .checkPermission((UsbDevice) mDevice, packageName, uid);
+                                    .checkPermission((UsbDevice) mDevice, packageName, pid, uid);
                         } else {
                             mPermissionManager.getPermissionsForUser(userId)
                                     .checkPermission((UsbAccessory) mDevice, uid);
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 0493637..27531949 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -262,6 +262,7 @@
         if (mHostManager != null) {
             if (deviceName != null) {
                 int uid = Binder.getCallingUid();
+                int pid = Binder.getCallingPid();
                 int user = UserHandle.getUserId(uid);
 
                 long ident = clearCallingIdentity();
@@ -269,7 +270,7 @@
                     synchronized (mLock) {
                         if (mUserManager.isSameProfileGroup(user, mCurrentUserId)) {
                             fd = mHostManager.openDevice(deviceName, getPermissionsForUser(user),
-                                    packageName, uid);
+                                    packageName, pid, uid);
                         } else {
                             Slog.w(TAG, "Cannot open " + deviceName + " for user " + user
                                     + " as user is not active.");
@@ -469,11 +470,12 @@
     @Override
     public boolean hasDevicePermission(UsbDevice device, String packageName) {
         final int uid = Binder.getCallingUid();
+        final int pid = Binder.getCallingPid();
         final int userId = UserHandle.getUserId(uid);
 
         final long token = Binder.clearCallingIdentity();
         try {
-            return getPermissionsForUser(userId).hasPermission(device, packageName, uid);
+            return getPermissionsForUser(userId).hasPermission(device, packageName, pid, uid);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -495,11 +497,12 @@
     @Override
     public void requestDevicePermission(UsbDevice device, String packageName, PendingIntent pi) {
         final int uid = Binder.getCallingUid();
+        final int pid = Binder.getCallingPid();
         final int userId = UserHandle.getUserId(uid);
 
         final long token = Binder.clearCallingIdentity();
         try {
-            getPermissionsForUser(userId).requestPermission(device, packageName, pi, uid);
+            getPermissionsForUser(userId).requestPermission(device, packageName, pi, pid, uid);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
diff --git a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
index e700f19..58f5484 100644
--- a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
@@ -186,12 +186,14 @@
      * Returns true if package with uid has permission to access the device.
      *
      * @param device to check permission for
+     * @param pid to check permission for
      * @param uid to check permission for
      * @return {@code true} if package with uid has permission
      */
-    boolean hasPermission(@NonNull UsbDevice device, @NonNull String packageName, int uid) {
+    boolean hasPermission(@NonNull UsbDevice device, @NonNull String packageName, int pid,
+            int uid) {
         if (isCameraDevicePresent(device)) {
-            if (!isCameraPermissionGranted(packageName, uid)) {
+            if (!isCameraPermissionGranted(packageName, pid, uid)) {
                 return false;
             }
         }
@@ -615,10 +617,11 @@
      * Check for camera permission of the calling process.
      *
      * @param packageName Package name of the caller.
+     * @param pid         Linux pid of the calling process.
      * @param uid         Linux uid of the calling process.
      * @return True in case camera permission is available, False otherwise.
      */
-    private boolean isCameraPermissionGranted(String packageName, int uid) {
+    private boolean isCameraPermissionGranted(String packageName, int pid, int uid) {
         int targetSdkVersion = android.os.Build.VERSION_CODES.P;
         try {
             ApplicationInfo aInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0);
@@ -634,7 +637,7 @@
         }
 
         if (targetSdkVersion >= android.os.Build.VERSION_CODES.P) {
-            int allowed = mContext.checkCallingPermission(android.Manifest.permission.CAMERA);
+            int allowed = mContext.checkPermission(android.Manifest.permission.CAMERA, pid, uid);
             if (android.content.pm.PackageManager.PERMISSION_DENIED == allowed) {
                 Slog.i(TAG, "Camera permission required for USB video class devices");
                 return false;
@@ -644,8 +647,8 @@
         return true;
     }
 
-    public void checkPermission(UsbDevice device, String packageName, int uid) {
-        if (!hasPermission(device, packageName, uid)) {
+    public void checkPermission(UsbDevice device, String packageName, int pid, int uid) {
+        if (!hasPermission(device, packageName, pid, uid)) {
             throw new SecurityException("User has not given " + uid + "/" + packageName
                     + " permission to access device " + device.getDeviceName());
         }
@@ -678,11 +681,12 @@
         requestPermissionDialog(device, accessory, canBeDefault, packageName, uid, mContext, pi);
     }
 
-    public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int uid) {
+    public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int pid,
+            int uid) {
         Intent intent = new Intent();
 
         // respond immediately if permission has already been granted
-        if (hasPermission(device, packageName, uid)) {
+        if (hasPermission(device, packageName, pid, uid)) {
             intent.putExtra(UsbManager.EXTRA_DEVICE, device);
             intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
             try {
@@ -693,7 +697,7 @@
             return;
         }
         if (isCameraDevicePresent(device)) {
-            if (!isCameraPermissionGranted(packageName, uid)) {
+            if (!isCameraPermissionGranted(packageName, pid, uid)) {
                 intent.putExtra(UsbManager.EXTRA_DEVICE, device);
                 intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
                 try {
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index dfcfed7..1770671 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -3182,13 +3182,14 @@
     }
 
     /**
-     * Get active data subscription id.
+     * Get active data subscription id. Active data subscription refers to the subscription
+     * currently chosen to provide cellular internet connection to the user. This may be
+     * different from getDefaultDataSubscriptionId(). Eg. Opportunistics data
+     *
      * See {@link PhoneStateListener#onActiveDataSubscriptionIdChanged(int)} for the details.
      *
-     * @return Active data subscription id
-     *
-     * //TODO: Refactor this API in b/134702460
-     * @hide
+     * @return Active data subscription id if any is chosen, or
+     * SubscriptionManager.INVALID_SUBSCRIPTION_ID if not.
      */
     public static int getActiveDataSubscriptionId() {
         try {
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 2f90a3d..cb66a96 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -863,7 +863,7 @@
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      *
      * @deprecated From R, callers should specify a flag for specific set of subscriptions to erase
-     * and use {@link #eraseSubscriptionsWithOptions(int, PendingIntent)} instead
+     * and use {@link #eraseSubscriptions(int, PendingIntent)} instead
      *
      * @hide
      */
@@ -895,7 +895,7 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
-    public void eraseSubscriptionsWithOptions(
+    public void eraseSubscriptions(
             @ResetOption int options, @NonNull PendingIntent callbackIntent) {
         if (!isEnabled()) {
             sendUnavailableError(callbackIntent);
diff --git a/test-mock/src/android/test/mock/MockContentResolver.java b/test-mock/src/android/test/mock/MockContentResolver.java
index a70152c..8283019 100644
--- a/test-mock/src/android/test/mock/MockContentResolver.java
+++ b/test-mock/src/android/test/mock/MockContentResolver.java
@@ -16,6 +16,8 @@
 
 package android.test.mock;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -130,17 +132,47 @@
     }
 
     /**
-     * Overrides {@link android.content.ContentResolver#notifyChange(Uri, ContentObserver, boolean)
-     * ContentResolver.notifChange(Uri, ContentObserver, boolean)}. All parameters are ignored.
-     * The method hides providers linked to MockContentResolver from other observers in the system.
-     *
-     * @param uri (Ignored) The uri of the content provider.
-     * @param observer (Ignored) The observer that originated the change.
-     * @param syncToNetwork (Ignored) If true, attempt to sync the change to the network.
+     * Overrides the behavior from the parent class to completely ignore any
+     * content notifications sent to this object. This effectively hides clients
+     * from observers elsewhere in the system.
      */
     @Override
-    public void notifyChange(Uri uri,
-            ContentObserver observer,
+    public void notifyChange(@NonNull Uri uri, @Nullable ContentObserver observer) {
+    }
+
+    /**
+     * Overrides the behavior from the parent class to completely ignore any
+     * content notifications sent to this object. This effectively hides clients
+     * from observers elsewhere in the system.
+     *
+     * @deprecated callers should consider migrating to
+     *             {@link #notifyChange(Uri, ContentObserver, int)}, as it
+     *             offers support for many more options than just
+     *             {@link #NOTIFY_SYNC_TO_NETWORK}.
+     */
+    @Override
+    @Deprecated
+    public void notifyChange(@NonNull Uri uri, @Nullable ContentObserver observer,
             boolean syncToNetwork) {
     }
+
+    /**
+     * Overrides the behavior from the parent class to completely ignore any
+     * content notifications sent to this object. This effectively hides clients
+     * from observers elsewhere in the system.
+     */
+    @Override
+    public void notifyChange(@NonNull Uri uri, @Nullable ContentObserver observer,
+            @NotifyFlags int flags) {
+    }
+
+    /**
+     * Overrides the behavior from the parent class to completely ignore any
+     * content notifications sent to this object. This effectively hides clients
+     * from observers elsewhere in the system.
+     */
+    @Override
+    public void notifyChange(@NonNull Iterable<Uri> uris, @Nullable ContentObserver observer,
+            @NotifyFlags int flags) {
+    }
 }
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 1be4ea8..6f44230 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -210,6 +210,7 @@
     tools: [":soong_zip"],
     srcs: [
         "Configuration.proto",
+        "ResourcesInternal.proto",
         "Resources.proto",
     ],
     out: ["aapt2-protos.zip"],
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 30c24d3..90343d4 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -2627,7 +2627,6 @@
         out.writeInt(apBand);
         out.writeInt(apChannel);
         BackupUtils.writeString(out, preSharedKey);
-        BackupUtils.writeString(out, saePasswordId);
         out.writeInt(getAuthType());
         out.writeBoolean(hiddenSSID);
         return baos.toByteArray();
@@ -2651,7 +2650,6 @@
         config.apBand = in.readInt();
         config.apChannel = in.readInt();
         config.preSharedKey = BackupUtils.readString(in);
-        config.saePasswordId = BackupUtils.readString(in);
         config.allowedKeyManagement.set(in.readInt());
         if (version >= 3) {
             config.hiddenSSID = in.readBoolean();