Merge "Revert "Revert "Activity start: Send featureId from context->AppOpsManager"""
diff --git a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
index 8e0ea98..f6af09c 100644
--- a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
+++ b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
@@ -30,6 +30,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -89,6 +90,7 @@
         runShellCommand("cmd jobscheduler run -f android 191934935");
     }
 
+    @Ignore
     @Test
     public void testComputeDigest() throws Exception {
         mAtraceUtils.startTrace(ATRACE_CATEGORY_SYSTEM_SERVER);
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
index d339afa..9c3bd81 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
@@ -129,7 +129,8 @@
      * @param label a label indicating what the blob is, that can be surfaced to the user.
      * @param expiryTimeMillis the time in secs after which the blob should be invalidated and not
      *                         allowed to be accessed by any other app,
-     *                         in {@link System#currentTimeMillis()} timebase.
+     *                         in {@link System#currentTimeMillis()} timebase or {@code 0} to
+     *                         indicate that there is no expiry time associated with this blob.
      * @param tag an opaque {@link String} associated with the blob. The length of the tag
      *            cannot be more than 128 characters.
      *
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
index 8cea645..f53f1f1 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
@@ -142,6 +142,9 @@
     /** @hide */
     public static final int COMMIT_RESULT_ERROR = 1;
 
+    /** @hide */
+    public static final int INVALID_RES_ID = -1;
+
     private final Context mContext;
     private final IBlobStoreManager mService;
 
@@ -263,6 +266,9 @@
      * <p> This lease information is persisted and calling this more than once will result in
      * latest lease overriding any previous lease.
      *
+     * <p> When an app acquires a lease on a blob, the System will try to keep this
+     * blob around but note that it can still be deleted if it was requested by the user.
+     *
      * @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
      *                   acquire a lease for.
      * @param descriptionResId the resource id for a short description string that can be surfaced
@@ -285,11 +291,68 @@
      *                               caller is trying to acquire too many leases.
      *
      * @see {@link #acquireLease(BlobHandle, int)}
+     * @see {@link #acquireLease(BlobHandle, CharSequence)}
      */
     public void acquireLease(@NonNull BlobHandle blobHandle, @IdRes int descriptionResId,
             @CurrentTimeMillisLong long leaseExpiryTimeMillis) throws IOException {
         try {
-            mService.acquireLease(blobHandle, descriptionResId, leaseExpiryTimeMillis,
+            mService.acquireLease(blobHandle, descriptionResId, null, leaseExpiryTimeMillis,
+                    mContext.getOpPackageName());
+        } catch (ParcelableException e) {
+            e.maybeRethrow(IOException.class);
+            throw new RuntimeException(e);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Acquire a lease to the blob represented by {@code blobHandle}. This lease indicates to the
+     * system that the caller wants the blob to be kept around.
+     *
+     * <p> This is a variant of {@link #acquireLease(BlobHandle, int, long)} taking a
+     * {@link CharSequence} for {@code description}. It is highly recommended that callers only
+     * use this when a valid resource ID for {@code description} could not be provided. Otherwise,
+     * apps should prefer using {@link #acquireLease(BlobHandle, int)} which will allow
+     * {@code description} to be localized.
+     *
+     * <p> Any active leases will be automatically released when the blob's expiry time
+     * ({@link BlobHandle#getExpiryTimeMillis()}) is elapsed.
+     *
+     * <p> This lease information is persisted and calling this more than once will result in
+     * latest lease overriding any previous lease.
+     *
+     * <p> When an app acquires a lease on a blob, the System will try to keep this
+     * blob around but note that it can still be deleted if it was requested by the user.
+     *
+     * @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
+     *                   acquire a lease for.
+     * @param description a short description string that can be surfaced
+     *                    to the user explaining what the blob is used for.
+     * @param leaseExpiryTimeMillis the time in milliseconds after which the lease can be
+     *                              automatically released, in {@link System#currentTimeMillis()}
+     *                              timebase. If its value is {@code 0}, then the behavior of this
+     *                              API is identical to {@link #acquireLease(BlobHandle, int)}
+     *                              where clients have to explicitly call
+     *                              {@link #releaseLease(BlobHandle)} when they don't
+     *                              need the blob anymore.
+     *
+     * @throws IOException when there is an I/O error while acquiring a lease to the blob.
+     * @throws SecurityException when the blob represented by the {@code blobHandle} does not
+     *                           exist or the caller does not have access to it.
+     * @throws IllegalArgumentException when {@code blobHandle} is invalid or
+     *                                  if the {@code leaseExpiryTimeMillis} is greater than the
+     *                                  {@link BlobHandle#getExpiryTimeMillis()}.
+     * @throws IllegalStateException when a lease could not be acquired, such as when the
+     *                               caller is trying to acquire too many leases.
+     *
+     * @see {@link #acquireLease(BlobHandle, int, long)}
+     * @see {@link #acquireLease(BlobHandle, CharSequence)}
+     */
+    public void acquireLease(@NonNull BlobHandle blobHandle, @NonNull CharSequence description,
+            @CurrentTimeMillisLong long leaseExpiryTimeMillis) throws IOException {
+        try {
+            mService.acquireLease(blobHandle, INVALID_RES_ID, description, leaseExpiryTimeMillis,
                     mContext.getOpPackageName());
         } catch (ParcelableException e) {
             e.maybeRethrow(IOException.class);
@@ -314,6 +377,9 @@
      * <p> This lease information is persisted and calling this more than once will result in
      * latest lease overriding any previous lease.
      *
+     * <p> When an app acquires a lease on a blob, the System will try to keep this
+     * blob around but note that it can still be deleted if it was requested by the user.
+     *
      * @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
      *                   acquire a lease for.
      * @param descriptionResId the resource id for a short description string that can be surfaced
@@ -327,6 +393,7 @@
      *                               caller is trying to acquire too many leases.
      *
      * @see {@link #acquireLease(BlobHandle, int, long)}
+     * @see {@link #acquireLease(BlobHandle, CharSequence, long)}
      */
     public void acquireLease(@NonNull BlobHandle blobHandle, @IdRes int descriptionResId)
             throws IOException {
@@ -334,6 +401,50 @@
     }
 
     /**
+     * Acquire a lease to the blob represented by {@code blobHandle}. This lease indicates to the
+     * system that the caller wants the blob to be kept around.
+     *
+     * <p> This is a variant of {@link #acquireLease(BlobHandle, int)} taking a {@link CharSequence}
+     * for {@code description}. It is highly recommended that callers only use this when a valid
+     * resource ID for {@code description} could not be provided. Otherwise, apps should prefer
+     * using {@link #acquireLease(BlobHandle, int)} which will allow {@code description} to be
+     * localized.
+     *
+     * <p> This is similar to {@link #acquireLease(BlobHandle, CharSequence, long)} except clients
+     * don't have to specify the lease expiry time upfront using this API and need to explicitly
+     * release the lease using {@link #releaseLease(BlobHandle)} when they no longer like to keep
+     * a blob around.
+     *
+     * <p> Any active leases will be automatically released when the blob's expiry time
+     * ({@link BlobHandle#getExpiryTimeMillis()}) is elapsed.
+     *
+     * <p> This lease information is persisted and calling this more than once will result in
+     * latest lease overriding any previous lease.
+     *
+     * <p> When an app acquires a lease on a blob, the System will try to keep this
+     * blob around but note that it can still be deleted if it was requested by the user.
+     *
+     * @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
+     *                   acquire a lease for.
+     * @param description a short description string that can be surfaced
+     *                    to the user explaining what the blob is used for.
+     *
+     * @throws IOException when there is an I/O error while acquiring a lease to the blob.
+     * @throws SecurityException when the blob represented by the {@code blobHandle} does not
+     *                           exist or the caller does not have access to it.
+     * @throws IllegalArgumentException when {@code blobHandle} is invalid.
+     * @throws IllegalStateException when a lease could not be acquired, such as when the
+     *                               caller is trying to acquire too many leases.
+     *
+     * @see {@link #acquireLease(BlobHandle, int)}
+     * @see {@link #acquireLease(BlobHandle, CharSequence, long)}
+     */
+    public void acquireLease(@NonNull BlobHandle blobHandle, @NonNull CharSequence description)
+            throws IOException {
+        acquireLease(blobHandle, description, 0);
+    }
+
+    /**
      * Release all active leases to the blob represented by {@code blobHandle} which are
      * currently held by the caller.
      *
diff --git a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
index e2128b4..a85a25c 100644
--- a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
+++ b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
@@ -26,8 +26,8 @@
     ParcelFileDescriptor openBlob(in BlobHandle handle, in String packageName);
     void deleteSession(long sessionId, in String packageName);
 
-    void acquireLease(in BlobHandle handle, int descriptionResId, long leaseTimeout,
-            in String packageName);
+    void acquireLease(in BlobHandle handle, int descriptionResId, in CharSequence description,
+            long leaseTimeoutMillis, in String packageName);
     void releaseLease(in BlobHandle handle, in String packageName);
 
     void waitForIdle(in RemoteCallback callback);
diff --git a/apex/blobstore/framework/java/android/app/blob/XmlTags.java b/apex/blobstore/framework/java/android/app/blob/XmlTags.java
index 803c9a4..9834d74 100644
--- a/apex/blobstore/framework/java/android/app/blob/XmlTags.java
+++ b/apex/blobstore/framework/java/android/app/blob/XmlTags.java
@@ -52,4 +52,5 @@
     // For leasee
     public static final String TAG_LEASEE = "l";
     public static final String ATTR_DESCRIPTION_RES_ID = "rid";
+    public static final String ATTR_DESCRIPTION = "d";
 }
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
index c12e0ec..c7d803c 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.blob;
 
+import static android.app.blob.XmlTags.ATTR_DESCRIPTION;
 import static android.app.blob.XmlTags.ATTR_DESCRIPTION_RES_ID;
 import static android.app.blob.XmlTags.ATTR_EXPIRY_TIME;
 import static android.app.blob.XmlTags.ATTR_ID;
@@ -28,12 +29,14 @@
 import static android.system.OsConstants.O_RDONLY;
 
 import static com.android.server.blob.BlobStoreConfig.TAG;
+import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_STRING_DESC;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.blob.BlobHandle;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.res.ResourceId;
 import android.content.res.Resources;
 import android.os.ParcelFileDescriptor;
 import android.os.RevocableFileDescriptor;
@@ -141,11 +144,11 @@
         }
     }
 
-    void addLeasee(String callingPackage, int callingUid,
-            int descriptionResId, long leaseExpiryTimeMillis) {
+    void addLeasee(String callingPackage, int callingUid, int descriptionResId,
+            CharSequence description, long leaseExpiryTimeMillis) {
         synchronized (mMetadataLock) {
             mLeasees.add(new Leasee(callingPackage, callingUid,
-                    descriptionResId, leaseExpiryTimeMillis));
+                    descriptionResId, description, leaseExpiryTimeMillis));
         }
     }
 
@@ -308,7 +311,7 @@
     }
 
     @Nullable
-    static BlobMetadata createFromXml(Context context, XmlPullParser in)
+    static BlobMetadata createFromXml(XmlPullParser in, int version, Context context)
             throws XmlPullParserException, IOException {
         final long blobId = XmlUtils.readLongAttribute(in, ATTR_ID);
         final int userId = XmlUtils.readIntAttribute(in, ATTR_USER_ID);
@@ -321,12 +324,12 @@
             if (TAG_BLOB_HANDLE.equals(in.getName())) {
                 blobHandle = BlobHandle.createFromXml(in);
             } else if (TAG_COMMITTER.equals(in.getName())) {
-                final Committer committer = Committer.createFromXml(in);
+                final Committer committer = Committer.createFromXml(in, version);
                 if (committer != null) {
                     committers.add(committer);
                 }
             } else if (TAG_LEASEE.equals(in.getName())) {
-                leasees.add(Leasee.createFromXml(in));
+                leasees.add(Leasee.createFromXml(in, version));
             }
         }
 
@@ -366,7 +369,7 @@
         }
 
         @Nullable
-        static Committer createFromXml(@NonNull XmlPullParser in)
+        static Committer createFromXml(@NonNull XmlPullParser in, int version)
                 throws XmlPullParserException, IOException {
             final String packageName = XmlUtils.readStringAttribute(in, ATTR_PACKAGE);
             final int uid = XmlUtils.readIntAttribute(in, ATTR_UID);
@@ -388,12 +391,15 @@
 
     static final class Leasee extends Accessor {
         public final int descriptionResId;
+        public final CharSequence description;
         public final long expiryTimeMillis;
 
-        Leasee(String packageName, int uid, int descriptionResId, long expiryTimeMillis) {
+        Leasee(String packageName, int uid, int descriptionResId, CharSequence description,
+                long expiryTimeMillis) {
             super(packageName, uid);
             this.descriptionResId = descriptionResId;
             this.expiryTimeMillis = expiryTimeMillis;
+            this.description = description;
         }
 
         boolean isStillValid() {
@@ -401,35 +407,51 @@
         }
 
         void dump(Context context, IndentingPrintWriter fout) {
-            String desc = null;
-            try {
-                final Resources leaseeRes = context.getPackageManager()
-                        .getResourcesForApplicationAsUser(packageName, UserHandle.getUserId(uid));
-                desc = leaseeRes.getString(descriptionResId);
-            } catch (PackageManager.NameNotFoundException e) {
-                Slog.d(TAG, "Unknown package in user " + UserHandle.getUserId(uid) + ": "
-                        + packageName, e);
-                desc = "<none>";
-            }
-            fout.println("desc: " + desc);
+            fout.println("desc: " + getDescriptionToDump(context));
             fout.println("expiryMs: " + expiryTimeMillis);
         }
 
+        private String getDescriptionToDump(Context context) {
+            String desc = null;
+            if (ResourceId.isValid(descriptionResId)) {
+                try {
+                    final Resources leaseeRes = context.getPackageManager()
+                            .getResourcesForApplicationAsUser(
+                                    packageName, UserHandle.getUserId(uid));
+                    desc = leaseeRes.getString(descriptionResId);
+                } catch (PackageManager.NameNotFoundException e) {
+                    Slog.d(TAG, "Unknown package in user " + UserHandle.getUserId(uid) + ": "
+                            + packageName, e);
+                    desc = "<none>";
+                }
+            } else {
+                desc = description.toString();
+            }
+            return desc;
+        }
+
         void writeToXml(@NonNull XmlSerializer out) throws IOException {
             XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, packageName);
             XmlUtils.writeIntAttribute(out, ATTR_UID, uid);
             XmlUtils.writeIntAttribute(out, ATTR_DESCRIPTION_RES_ID, descriptionResId);
             XmlUtils.writeLongAttribute(out, ATTR_EXPIRY_TIME, expiryTimeMillis);
+            XmlUtils.writeStringAttribute(out, ATTR_DESCRIPTION, description);
         }
 
         @NonNull
-        static Leasee createFromXml(@NonNull XmlPullParser in) throws IOException {
+        static Leasee createFromXml(@NonNull XmlPullParser in, int version) throws IOException {
             final String packageName = XmlUtils.readStringAttribute(in, ATTR_PACKAGE);
             final int uid = XmlUtils.readIntAttribute(in, ATTR_UID);
             final int descriptionResId = XmlUtils.readIntAttribute(in, ATTR_DESCRIPTION_RES_ID);
             final long expiryTimeMillis = XmlUtils.readLongAttribute(in, ATTR_EXPIRY_TIME);
+            final CharSequence description;
+            if (version >= XML_VERSION_ADD_STRING_DESC) {
+                description = XmlUtils.readStringAttribute(in, ATTR_DESCRIPTION);
+            } else {
+                description = null;
+            }
 
-            return new Leasee(packageName, uid, descriptionResId, expiryTimeMillis);
+            return new Leasee(packageName, uid, descriptionResId, description, expiryTimeMillis);
         }
     }
 
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
index ba2e559..bcc1610 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
@@ -28,7 +28,12 @@
     public static final String TAG = "BlobStore";
     public static final boolean LOGV = Log.isLoggable(TAG, Log.VERBOSE);
 
-    public static final int CURRENT_XML_VERSION = 1;
+    // Initial version.
+    public static final int XML_VERSION_INIT = 1;
+    // Added a string variant of lease description.
+    public static final int XML_VERSION_ADD_STRING_DESC = 2;
+
+    public static final int XML_VERSION_CURRENT = XML_VERSION_ADD_STRING_DESC;
 
     private static final String ROOT_DIR_NAME = "blobstore";
     private static final String BLOBS_DIR_NAME = "blobs";
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index 0ba34ca..1efdbda 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -27,10 +27,10 @@
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
 import static android.os.UserHandle.USER_NULL;
 
-import static com.android.server.blob.BlobStoreConfig.CURRENT_XML_VERSION;
 import static com.android.server.blob.BlobStoreConfig.LOGV;
 import static com.android.server.blob.BlobStoreConfig.SESSION_EXPIRY_TIMEOUT_MILLIS;
 import static com.android.server.blob.BlobStoreConfig.TAG;
+import static com.android.server.blob.BlobStoreConfig.XML_VERSION_CURRENT;
 import static com.android.server.blob.BlobStoreSession.STATE_ABANDONED;
 import static com.android.server.blob.BlobStoreSession.STATE_COMMITTED;
 import static com.android.server.blob.BlobStoreSession.STATE_VERIFIED_INVALID;
@@ -324,7 +324,8 @@
     }
 
     private void acquireLeaseInternal(BlobHandle blobHandle, int descriptionResId,
-            long leaseExpiryTimeMillis, int callingUid, String callingPackage) {
+            CharSequence description, long leaseExpiryTimeMillis,
+            int callingUid, String callingPackage) {
         synchronized (mBlobsLock) {
             final BlobMetadata blobMetadata = getUserBlobsLocked(UserHandle.getUserId(callingUid))
                     .get(blobHandle);
@@ -338,7 +339,7 @@
                         "Lease expiry cannot be later than blobs expiry time");
             }
             blobMetadata.addLeasee(callingPackage, callingUid,
-                    descriptionResId, leaseExpiryTimeMillis);
+                    descriptionResId, description, leaseExpiryTimeMillis);
             if (LOGV) {
                 Slog.v(TAG, "Acquired lease on " + blobHandle
                         + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
@@ -450,7 +451,7 @@
             out.setOutput(fos, StandardCharsets.UTF_8.name());
             out.startDocument(null, true);
             out.startTag(null, TAG_SESSIONS);
-            XmlUtils.writeIntAttribute(out, ATTR_VERSION, CURRENT_XML_VERSION);
+            XmlUtils.writeIntAttribute(out, ATTR_VERSION, XML_VERSION_CURRENT);
 
             for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) {
                 final LongSparseArray<BlobStoreSession> userSessions =
@@ -491,6 +492,7 @@
             final XmlPullParser in = Xml.newPullParser();
             in.setInput(fis, StandardCharsets.UTF_8.name());
             XmlUtils.beginDocument(in, TAG_SESSIONS);
+            final int version = XmlUtils.readIntAttribute(in, ATTR_VERSION);
             while (true) {
                 XmlUtils.nextElement(in);
                 if (in.getEventType() == XmlPullParser.END_DOCUMENT) {
@@ -499,7 +501,7 @@
 
                 if (TAG_SESSION.equals(in.getName())) {
                     final BlobStoreSession session = BlobStoreSession.createFromXml(
-                            in, mContext, mSessionStateChangeListener);
+                            in, version, mContext, mSessionStateChangeListener);
                     if (session == null) {
                         continue;
                     }
@@ -539,7 +541,7 @@
             out.setOutput(fos, StandardCharsets.UTF_8.name());
             out.startDocument(null, true);
             out.startTag(null, TAG_BLOBS);
-            XmlUtils.writeIntAttribute(out, ATTR_VERSION, CURRENT_XML_VERSION);
+            XmlUtils.writeIntAttribute(out, ATTR_VERSION, XML_VERSION_CURRENT);
 
             for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
                 final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
@@ -579,6 +581,7 @@
             final XmlPullParser in = Xml.newPullParser();
             in.setInput(fis, StandardCharsets.UTF_8.name());
             XmlUtils.beginDocument(in, TAG_BLOBS);
+            final int version = XmlUtils.readIntAttribute(in, ATTR_VERSION);
             while (true) {
                 XmlUtils.nextElement(in);
                 if (in.getEventType() == XmlPullParser.END_DOCUMENT) {
@@ -586,7 +589,8 @@
                 }
 
                 if (TAG_BLOB.equals(in.getName())) {
-                    final BlobMetadata blobMetadata = BlobMetadata.createFromXml(mContext, in);
+                    final BlobMetadata blobMetadata = BlobMetadata.createFromXml(
+                            in, version, mContext);
                     final SparseArray<String> userPackages = allPackages.get(
                             blobMetadata.getUserId());
                     if (userPackages == null) {
@@ -1032,11 +1036,14 @@
 
         @Override
         public void acquireLease(@NonNull BlobHandle blobHandle, @IdRes int descriptionResId,
+                @Nullable CharSequence description,
                 @CurrentTimeSecondsLong long leaseExpiryTimeMillis, @NonNull String packageName) {
             Objects.requireNonNull(blobHandle, "blobHandle must not be null");
             blobHandle.assertIsValid();
-            Preconditions.checkArgument(ResourceId.isValid(descriptionResId),
-                    "descriptionResId is not valid");
+            Preconditions.checkArgument(
+                    ResourceId.isValid(descriptionResId) || description != null,
+                    "Description must be valid; descriptionId=" + descriptionResId
+                            + ", description=" + description);
             Preconditions.checkArgumentNonnegative(leaseExpiryTimeMillis,
                     "leaseExpiryTimeMillis must not be negative");
             Objects.requireNonNull(packageName, "packageName must not be null");
@@ -1044,7 +1051,7 @@
             final int callingUid = Binder.getCallingUid();
             verifyCallingPackage(callingUid, packageName);
 
-            acquireLeaseInternal(blobHandle, descriptionResId, leaseExpiryTimeMillis,
+            acquireLeaseInternal(blobHandle, descriptionResId, description, leaseExpiryTimeMillis,
                     callingUid, packageName);
         }
 
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
index bd35b86..80b4235 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -511,7 +511,7 @@
     }
 
     @Nullable
-    static BlobStoreSession createFromXml(@NonNull XmlPullParser in,
+    static BlobStoreSession createFromXml(@NonNull XmlPullParser in, int version,
             @NonNull Context context, @NonNull SessionStateChangeListener stateChangeListener)
             throws IOException, XmlPullParserException {
         final int sessionId = XmlUtils.readIntAttribute(in, ATTR_ID);
diff --git a/apex/extservices/testing/Android.bp b/apex/extservices/testing/Android.bp
new file mode 100644
index 0000000..88a4724
--- /dev/null
+++ b/apex/extservices/testing/Android.bp
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+apex_test {
+    name: "test_com.android.extservices",
+    visibility: [
+        "//system/apex/tests",
+    ],
+    defaults: ["com.android.extservices-defaults"],
+    manifest: "test_manifest.json",
+    file_contexts: ":com.android.extservices-file_contexts",
+    // Test APEX, should never be installed
+    installable: false,
+}
diff --git a/apex/extservices/testing/test_manifest.json b/apex/extservices/testing/test_manifest.json
new file mode 100644
index 0000000..23a50e3
--- /dev/null
+++ b/apex/extservices/testing/test_manifest.json
@@ -0,0 +1,4 @@
+{
+  "name": "com.android.extservices",
+  "version": 2147483647
+}
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 939164e..b516279 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -473,13 +473,17 @@
      */
     public static class Constants {
         // Key names stored in the settings value.
-        private static final String KEY_MIN_IDLE_COUNT = "min_idle_count";
-        private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count";
-        private static final String KEY_MIN_BATTERY_NOT_LOW_COUNT = "min_battery_not_low_count";
-        private static final String KEY_MIN_STORAGE_NOT_LOW_COUNT = "min_storage_not_low_count";
-        private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count";
-        private static final String KEY_MIN_CONTENT_COUNT = "min_content_count";
-        private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
+        // TODO(124466289): remove deprecated flags when we migrate to DeviceConfig
+        private static final String DEPRECATED_KEY_MIN_IDLE_COUNT = "min_idle_count";
+        private static final String DEPRECATED_KEY_MIN_CHARGING_COUNT = "min_charging_count";
+        private static final String DEPRECATED_KEY_MIN_BATTERY_NOT_LOW_COUNT =
+                "min_battery_not_low_count";
+        private static final String DEPRECATED_KEY_MIN_STORAGE_NOT_LOW_COUNT =
+                "min_storage_not_low_count";
+        private static final String DEPRECATED_KEY_MIN_CONNECTIVITY_COUNT =
+                "min_connectivity_count";
+        private static final String DEPRECATED_KEY_MIN_CONTENT_COUNT = "min_content_count";
+        private static final String DEPRECATED_KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
         private static final String KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT =
                 "min_ready_non_active_jobs_count";
         private static final String KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS =
@@ -515,13 +519,6 @@
         private static final String KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION =
                 "aq_schedule_throw_exception";
 
-        private static final int DEFAULT_MIN_IDLE_COUNT = 1;
-        private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
-        private static final int DEFAULT_MIN_BATTERY_NOT_LOW_COUNT = 1;
-        private static final int DEFAULT_MIN_STORAGE_NOT_LOW_COUNT = 1;
-        private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1;
-        private static final int DEFAULT_MIN_CONTENT_COUNT = 1;
-        private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
         private static final int DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT = 5;
         private static final long DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = 31 * MINUTE_IN_MILLIS;
         private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
@@ -536,44 +533,6 @@
         private static final boolean DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION = true;
 
         /**
-         * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
-         * early.
-         */
-        int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT;
-        /**
-         * Minimum # of charging jobs that must be ready in order to force the JMS to schedule
-         * things early.
-         */
-        int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT;
-        /**
-         * Minimum # of "battery not low" jobs that must be ready in order to force the JMS to
-         * schedule things early.
-         */
-        int MIN_BATTERY_NOT_LOW_COUNT = DEFAULT_MIN_BATTERY_NOT_LOW_COUNT;
-        /**
-         * Minimum # of "storage not low" jobs that must be ready in order to force the JMS to
-         * schedule things early.
-         */
-        int MIN_STORAGE_NOT_LOW_COUNT = DEFAULT_MIN_STORAGE_NOT_LOW_COUNT;
-        /**
-         * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
-         * things early.  1 == Run connectivity jobs as soon as ready.
-         */
-        int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT;
-        /**
-         * Minimum # of content trigger jobs that must be ready in order to force the JMS to
-         * schedule things early.
-         */
-        int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT;
-        /**
-         * Minimum # of jobs (with no particular constraints) for which the JMS will be happy
-         * running some work early.  This (and thus the other min counts) is now set to 1, to
-         * prevent any batching at this level.  Since we now do batching through doze, that is
-         * a much better mechanism.
-         */
-        int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
-
-        /**
          * Minimum # of non-ACTIVE jobs for which the JMS will be happy running some work early.
          */
         int MIN_READY_NON_ACTIVE_JOBS_COUNT = DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT;
@@ -687,20 +646,6 @@
                 Slog.e(TAG, "Bad jobscheduler settings", e);
             }
 
-            MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT,
-                    DEFAULT_MIN_IDLE_COUNT);
-            MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT,
-                    DEFAULT_MIN_CHARGING_COUNT);
-            MIN_BATTERY_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_BATTERY_NOT_LOW_COUNT,
-                    DEFAULT_MIN_BATTERY_NOT_LOW_COUNT);
-            MIN_STORAGE_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_STORAGE_NOT_LOW_COUNT,
-                    DEFAULT_MIN_STORAGE_NOT_LOW_COUNT);
-            MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT,
-                    DEFAULT_MIN_CONNECTIVITY_COUNT);
-            MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT,
-                    DEFAULT_MIN_CONTENT_COUNT);
-            MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT,
-                    DEFAULT_MIN_READY_JOBS_COUNT);
             MIN_READY_NON_ACTIVE_JOBS_COUNT = mParser.getInt(
                     KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT,
                     DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT);
@@ -749,13 +694,6 @@
         void dump(IndentingPrintWriter pw) {
             pw.println("Settings:");
             pw.increaseIndent();
-            pw.printPair(KEY_MIN_IDLE_COUNT, MIN_IDLE_COUNT).println();
-            pw.printPair(KEY_MIN_CHARGING_COUNT, MIN_CHARGING_COUNT).println();
-            pw.printPair(KEY_MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT).println();
-            pw.printPair(KEY_MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT).println();
-            pw.printPair(KEY_MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT).println();
-            pw.printPair(KEY_MIN_CONTENT_COUNT, MIN_CONTENT_COUNT).println();
-            pw.printPair(KEY_MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT).println();
             pw.printPair(KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT,
                     MIN_READY_NON_ACTIVE_JOBS_COUNT).println();
             pw.printPair(KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS,
@@ -790,13 +728,6 @@
         }
 
         void dump(ProtoOutputStream proto) {
-            proto.write(ConstantsProto.MIN_IDLE_COUNT, MIN_IDLE_COUNT);
-            proto.write(ConstantsProto.MIN_CHARGING_COUNT, MIN_CHARGING_COUNT);
-            proto.write(ConstantsProto.MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT);
-            proto.write(ConstantsProto.MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT);
-            proto.write(ConstantsProto.MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT);
-            proto.write(ConstantsProto.MIN_CONTENT_COUNT, MIN_CONTENT_COUNT);
-            proto.write(ConstantsProto.MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT);
             proto.write(ConstantsProto.MIN_READY_NON_ACTIVE_JOBS_COUNT,
                     MIN_READY_NON_ACTIVE_JOBS_COUNT);
             proto.write(ConstantsProto.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS,
@@ -2106,13 +2037,6 @@
      * policies on when we want to execute jobs.
      */
     final class MaybeReadyJobQueueFunctor implements Consumer<JobStatus> {
-        int chargingCount;
-        int batteryNotLowCount;
-        int storageNotLowCount;
-        int idleCount;
-        int backoffCount;
-        int connectivityCount;
-        int contentCount;
         int forceBatchedCount;
         int unbatchedCount;
         final List<JobStatus> runnableJobs = new ArrayList<>();
@@ -2135,13 +2059,25 @@
                     }
                 } catch (RemoteException e) {
                 }
+
+                final boolean shouldForceBatchJob;
                 // Restricted jobs must always be batched
-                if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX
-                        || (mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT > 1
-                        && job.getEffectiveStandbyBucket() != ACTIVE_INDEX
-                        && (job.getFirstForceBatchedTimeElapsed() == 0
-                        || sElapsedRealtimeClock.millis() - job.getFirstForceBatchedTimeElapsed()
-                                < mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS))) {
+                if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX) {
+                    shouldForceBatchJob = true;
+                } else if (job.getNumFailures() > 0) {
+                    shouldForceBatchJob = false;
+                } else {
+                    final long nowElapsed = sElapsedRealtimeClock.millis();
+                    final boolean batchDelayExpired = job.getFirstForceBatchedTimeElapsed() > 0
+                            && nowElapsed - job.getFirstForceBatchedTimeElapsed()
+                            >= mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS;
+                    shouldForceBatchJob =
+                            mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT > 1
+                                    && job.getEffectiveStandbyBucket() != ACTIVE_INDEX
+                                    && !batchDelayExpired;
+                }
+
+                if (shouldForceBatchJob) {
                     // Force batching non-ACTIVE jobs. Don't include them in the other counts.
                     forceBatchedCount++;
                     if (job.getFirstForceBatchedTimeElapsed() == 0) {
@@ -2149,27 +2085,6 @@
                     }
                 } else {
                     unbatchedCount++;
-                    if (job.getNumFailures() > 0) {
-                        backoffCount++;
-                    }
-                    if (job.hasIdleConstraint()) {
-                        idleCount++;
-                    }
-                    if (job.hasConnectivityConstraint()) {
-                        connectivityCount++;
-                    }
-                    if (job.hasChargingConstraint()) {
-                        chargingCount++;
-                    }
-                    if (job.hasBatteryNotLowConstraint()) {
-                        batteryNotLowCount++;
-                    }
-                    if (job.hasStorageNotLowConstraint()) {
-                        storageNotLowCount++;
-                    }
-                    if (job.hasContentTriggerConstraint()) {
-                        contentCount++;
-                    }
                 }
                 runnableJobs.add(job);
             } else {
@@ -2178,16 +2093,8 @@
         }
 
         public void postProcess() {
-            if (backoffCount > 0 ||
-                    idleCount >= mConstants.MIN_IDLE_COUNT ||
-                    connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
-                    chargingCount >= mConstants.MIN_CHARGING_COUNT ||
-                    batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT ||
-                    storageNotLowCount >= mConstants.MIN_STORAGE_NOT_LOW_COUNT ||
-                    contentCount >= mConstants.MIN_CONTENT_COUNT ||
-                    forceBatchedCount >= mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT ||
-                    (unbatchedCount > 0 && (unbatchedCount + forceBatchedCount)
-                            >= mConstants.MIN_READY_JOBS_COUNT)) {
+            if (unbatchedCount > 0
+                    || forceBatchedCount >= mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT) {
                 if (DEBUG) {
                     Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs.");
                 }
@@ -2208,13 +2115,6 @@
 
         @VisibleForTesting
         void reset() {
-            chargingCount = 0;
-            idleCount =  0;
-            backoffCount = 0;
-            connectivityCount = 0;
-            batteryNotLowCount = 0;
-            storageNotLowCount = 0;
-            contentCount = 0;
             forceBatchedCount = 0;
             unbatchedCount = 0;
             runnableJobs.clear();
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index b1b8fba..f1bfa04 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -136,25 +136,59 @@
     private static final long ONE_HOUR = ONE_MINUTE * 60;
     private static final long ONE_DAY = ONE_HOUR * 24;
 
-    static final long[] SCREEN_TIME_THRESHOLDS = {
+    /**
+     * The minimum amount of time the screen must have been on before an app can time out from its
+     * current bucket to the next bucket.
+     */
+    private static final long[] SCREEN_TIME_THRESHOLDS = {
             0,
             0,
-            COMPRESS_TIME ? 120 * 1000 : 1 * ONE_HOUR,
-            COMPRESS_TIME ? 240 * 1000 : 2 * ONE_HOUR
+            COMPRESS_TIME ? 2 * ONE_MINUTE : 1 * ONE_HOUR,
+            COMPRESS_TIME ? 4 * ONE_MINUTE : 2 * ONE_HOUR,
+            COMPRESS_TIME ? 8 * ONE_MINUTE : 6 * ONE_HOUR
     };
 
-    static final long[] ELAPSED_TIME_THRESHOLDS = {
+    /** The minimum allowed values for each index in {@link #SCREEN_TIME_THRESHOLDS}. */
+    private static final long[] MINIMUM_SCREEN_TIME_THRESHOLDS = COMPRESS_TIME
+            ? new long[SCREEN_TIME_THRESHOLDS.length]
+            : new long[]{
+                    0,
+                    0,
+                    0,
+                    30 * ONE_MINUTE,
+                    ONE_HOUR
+            };
+
+    /**
+     * The minimum amount of elapsed time that must have passed before an app can time out from its
+     * current bucket to the next bucket.
+     */
+    private static final long[] ELAPSED_TIME_THRESHOLDS = {
             0,
             COMPRESS_TIME ?  1 * ONE_MINUTE : 12 * ONE_HOUR,
             COMPRESS_TIME ?  4 * ONE_MINUTE : 24 * ONE_HOUR,
-            COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR
+            COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR,
+            // TODO(149050681): increase timeout to 30+ days
+            COMPRESS_TIME ? 32 * ONE_MINUTE : 4 * ONE_DAY
     };
 
-    static final int[] THRESHOLD_BUCKETS = {
+    /** The minimum allowed values for each index in {@link #ELAPSED_TIME_THRESHOLDS}. */
+    private static final long[] MINIMUM_ELAPSED_TIME_THRESHOLDS = COMPRESS_TIME
+            ? new long[ELAPSED_TIME_THRESHOLDS.length]
+            : new long[]{
+                    0,
+                    ONE_HOUR,
+                    ONE_HOUR,
+                    2 * ONE_HOUR,
+                    4 * ONE_DAY
+            };
+
+    private static final int[] THRESHOLD_BUCKETS = {
             STANDBY_BUCKET_ACTIVE,
             STANDBY_BUCKET_WORKING_SET,
             STANDBY_BUCKET_FREQUENT,
-            STANDBY_BUCKET_RARE
+            STANDBY_BUCKET_RARE,
+            STANDBY_BUCKET_RESTRICTED
     };
 
     /** Default expiration time for bucket prediction. After this, use thresholds to downgrade. */
@@ -204,7 +238,15 @@
     static final int MSG_REPORT_EXEMPTED_SYNC_START = 13;
 
     long mCheckIdleIntervalMillis;
+    /**
+     * The minimum amount of time the screen must have been on before an app can time out from its
+     * current bucket to the next bucket.
+     */
     long[] mAppStandbyScreenThresholds = SCREEN_TIME_THRESHOLDS;
+    /**
+     * The minimum amount of elapsed time that must have passed before an app can time out from its
+     * current bucket to the next bucket.
+     */
     long[] mAppStandbyElapsedThresholds = ELAPSED_TIME_THRESHOLDS;
     /** Minimum time a strong usage event should keep the bucket elevated. */
     long mStrongUsageTimeoutMillis;
@@ -396,12 +438,9 @@
                 }
                 if (!packageName.equals(providerPkgName)) {
                     synchronized (mAppIdleLock) {
-                        AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId,
-                                STANDBY_BUCKET_ACTIVE, REASON_SUB_USAGE_SYNC_ADAPTER,
-                                0,
-                                elapsedRealtime + mSyncAdapterTimeoutMillis);
-                        maybeInformListeners(packageName, userId, elapsedRealtime,
-                                appUsage.currentBucket, appUsage.bucketingReason, false);
+                        reportNoninteractiveUsageLocked(packageName, userId, STANDBY_BUCKET_ACTIVE,
+                                REASON_SUB_USAGE_SYNC_ADAPTER, elapsedRealtime,
+                                mSyncAdapterTimeoutMillis);
                     }
                 }
             } catch (PackageManager.NameNotFoundException e) {
@@ -432,12 +471,8 @@
         final long elapsedRealtime = mInjector.elapsedRealtime();
 
         synchronized (mAppIdleLock) {
-            AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId,
-                    bucketToPromote, usageReason,
-                    0,
-                    elapsedRealtime + durationMillis);
-            maybeInformListeners(packageName, userId, elapsedRealtime,
-                    appUsage.currentBucket, appUsage.bucketingReason, false);
+            reportNoninteractiveUsageLocked(packageName, userId, bucketToPromote,
+                    usageReason, elapsedRealtime, durationMillis);
         }
     }
 
@@ -450,12 +485,9 @@
                     mAppIdleHistory.getAppStandbyBucket(packageName, userId, elapsedRealtime);
             if (currentBucket == STANDBY_BUCKET_NEVER) {
                 // Bring the app out of the never bucket
-                AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId,
-                        STANDBY_BUCKET_WORKING_SET, REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED,
-                        0,
-                        elapsedRealtime + mUnexemptedSyncScheduledTimeoutMillis);
-                maybeInformListeners(packageName, userId, elapsedRealtime,
-                        appUsage.currentBucket, appUsage.bucketingReason, false);
+                reportNoninteractiveUsageLocked(packageName, userId, STANDBY_BUCKET_WORKING_SET,
+                        REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED, elapsedRealtime,
+                        mUnexemptedSyncScheduledTimeoutMillis);
             }
         }
     }
@@ -466,15 +498,23 @@
         final long elapsedRealtime = mInjector.elapsedRealtime();
 
         synchronized (mAppIdleLock) {
-            AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId,
-                    STANDBY_BUCKET_ACTIVE, REASON_SUB_USAGE_EXEMPTED_SYNC_START,
-                    0,
-                    elapsedRealtime + mExemptedSyncStartTimeoutMillis);
-            maybeInformListeners(packageName, userId, elapsedRealtime,
-                    appUsage.currentBucket, appUsage.bucketingReason, false);
+            reportNoninteractiveUsageLocked(packageName, userId, STANDBY_BUCKET_ACTIVE,
+                    REASON_SUB_USAGE_EXEMPTED_SYNC_START, elapsedRealtime,
+                    mExemptedSyncStartTimeoutMillis);
         }
     }
 
+    private void reportNoninteractiveUsageLocked(String packageName, int userId, int bucket,
+            int subReason, long elapsedRealtime, long nextCheckDelay) {
+        final AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId, bucket,
+                subReason, 0, elapsedRealtime + nextCheckDelay);
+        mHandler.sendMessageDelayed(
+                mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, packageName),
+                nextCheckDelay);
+        maybeInformListeners(packageName, userId, elapsedRealtime, appUsage.currentBucket,
+                appUsage.bucketingReason, false);
+    }
+
     @Override
     public void postCheckIdleStates(int userId) {
         mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
@@ -623,7 +663,7 @@
 
                 if (app.lastRestrictAttemptElapsedTime > app.lastUsedByUserElapsedTime
                         && elapsedTimeAdjusted - app.lastUsedByUserElapsedTime
-                        >= mInjector.getRestrictedBucketDelayMs()) {
+                        >= mInjector.getAutoRestrictedBucketDelayMs()) {
                     newBucket = STANDBY_BUCKET_RESTRICTED;
                     reason = app.lastRestrictReason;
                     if (DEBUG) {
@@ -699,73 +739,75 @@
     @Override
     public void reportEvent(UsageEvents.Event event, long elapsedRealtime, int userId) {
         if (!mAppIdleEnabled) return;
-        synchronized (mAppIdleLock) {
-            final String pkg = event.getPackageName();
-            final int eventType = event.getEventType();
-            // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
-            // about apps that are on some kind of whitelist anyway.
-            final boolean previouslyIdle = mAppIdleHistory.isIdle(
-                    pkg, userId, elapsedRealtime);
-            // Inform listeners if necessary
-            if ((eventType == UsageEvents.Event.ACTIVITY_RESUMED
-                    || eventType == UsageEvents.Event.ACTIVITY_PAUSED
-                    || eventType == UsageEvents.Event.SYSTEM_INTERACTION
-                    || eventType == UsageEvents.Event.USER_INTERACTION
-                    || eventType == UsageEvents.Event.NOTIFICATION_SEEN
-                    || eventType == UsageEvents.Event.SLICE_PINNED
-                    || eventType == UsageEvents.Event.SLICE_PINNED_PRIV
-                    || eventType == UsageEvents.Event.FOREGROUND_SERVICE_START)) {
-
-                final AppUsageHistory appHistory = mAppIdleHistory.getAppUsageHistory(
-                        pkg, userId, elapsedRealtime);
-                final int prevBucket = appHistory.currentBucket;
-                final int prevBucketReason = appHistory.bucketingReason;
-                final long nextCheckTime;
-                final int subReason = usageEventToSubReason(eventType);
-                final int reason = REASON_MAIN_USAGE | subReason;
-                if (eventType == UsageEvents.Event.NOTIFICATION_SEEN
-                        || eventType == UsageEvents.Event.SLICE_PINNED) {
-                    // Mild usage elevates to WORKING_SET but doesn't change usage time.
-                    mAppIdleHistory.reportUsage(appHistory, pkg,
-                            STANDBY_BUCKET_WORKING_SET, subReason,
-                            0, elapsedRealtime + mNotificationSeenTimeoutMillis);
-                    nextCheckTime = mNotificationSeenTimeoutMillis;
-                } else if (eventType == UsageEvents.Event.SYSTEM_INTERACTION) {
-                    mAppIdleHistory.reportUsage(appHistory, pkg,
-                            STANDBY_BUCKET_ACTIVE, subReason,
-                            0, elapsedRealtime + mSystemInteractionTimeoutMillis);
-                    nextCheckTime = mSystemInteractionTimeoutMillis;
-                } else if (eventType == UsageEvents.Event.FOREGROUND_SERVICE_START) {
-                    // Only elevate bucket if this is the first usage of the app
-                    if (prevBucket != STANDBY_BUCKET_NEVER) return;
-                    mAppIdleHistory.reportUsage(appHistory, pkg,
-                            STANDBY_BUCKET_ACTIVE, subReason,
-                            0, elapsedRealtime + mInitialForegroundServiceStartTimeoutMillis);
-                    nextCheckTime = mInitialForegroundServiceStartTimeoutMillis;
-                } else {
-                    mAppIdleHistory.reportUsage(appHistory, pkg,
-                            STANDBY_BUCKET_ACTIVE, subReason,
-                            elapsedRealtime, elapsedRealtime + mStrongUsageTimeoutMillis);
-                    nextCheckTime = mStrongUsageTimeoutMillis;
-                }
-                if (appHistory.currentBucket != prevBucket) {
-                    mHandler.sendMessageDelayed(
-                            mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, pkg),
-                            nextCheckTime);
-                    final boolean userStartedInteracting =
-                            appHistory.currentBucket == STANDBY_BUCKET_ACTIVE
-                                    && (prevBucketReason & REASON_MAIN_MASK) != REASON_MAIN_USAGE;
-                    maybeInformListeners(pkg, userId, elapsedRealtime,
-                            appHistory.currentBucket, reason, userStartedInteracting);
-                }
-
-                if (previouslyIdle) {
-                    notifyBatteryStats(pkg, userId, false);
-                }
+        final int eventType = event.getEventType();
+        if ((eventType == UsageEvents.Event.ACTIVITY_RESUMED
+                || eventType == UsageEvents.Event.ACTIVITY_PAUSED
+                || eventType == UsageEvents.Event.SYSTEM_INTERACTION
+                || eventType == UsageEvents.Event.USER_INTERACTION
+                || eventType == UsageEvents.Event.NOTIFICATION_SEEN
+                || eventType == UsageEvents.Event.SLICE_PINNED
+                || eventType == UsageEvents.Event.SLICE_PINNED_PRIV
+                || eventType == UsageEvents.Event.FOREGROUND_SERVICE_START)) {
+            synchronized (mAppIdleLock) {
+                reportEventLocked(event.getPackageName(), eventType, elapsedRealtime, userId);
             }
         }
     }
 
+    private void reportEventLocked(String pkg, int eventType, long elapsedRealtime, int userId) {
+        // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
+        // about apps that are on some kind of whitelist anyway.
+        final boolean previouslyIdle = mAppIdleHistory.isIdle(
+                pkg, userId, elapsedRealtime);
+
+        final AppUsageHistory appHistory = mAppIdleHistory.getAppUsageHistory(
+                pkg, userId, elapsedRealtime);
+        final int prevBucket = appHistory.currentBucket;
+        final int prevBucketReason = appHistory.bucketingReason;
+        final long nextCheckDelay;
+        final int subReason = usageEventToSubReason(eventType);
+        final int reason = REASON_MAIN_USAGE | subReason;
+        if (eventType == UsageEvents.Event.NOTIFICATION_SEEN
+                || eventType == UsageEvents.Event.SLICE_PINNED) {
+            // Mild usage elevates to WORKING_SET but doesn't change usage time.
+            mAppIdleHistory.reportUsage(appHistory, pkg,
+                    STANDBY_BUCKET_WORKING_SET, subReason,
+                    0, elapsedRealtime + mNotificationSeenTimeoutMillis);
+            nextCheckDelay = mNotificationSeenTimeoutMillis;
+        } else if (eventType == UsageEvents.Event.SYSTEM_INTERACTION) {
+            mAppIdleHistory.reportUsage(appHistory, pkg,
+                    STANDBY_BUCKET_ACTIVE, subReason,
+                    0, elapsedRealtime + mSystemInteractionTimeoutMillis);
+            nextCheckDelay = mSystemInteractionTimeoutMillis;
+        } else if (eventType == UsageEvents.Event.FOREGROUND_SERVICE_START) {
+            // Only elevate bucket if this is the first usage of the app
+            if (prevBucket != STANDBY_BUCKET_NEVER) return;
+            mAppIdleHistory.reportUsage(appHistory, pkg,
+                    STANDBY_BUCKET_ACTIVE, subReason,
+                    0, elapsedRealtime + mInitialForegroundServiceStartTimeoutMillis);
+            nextCheckDelay = mInitialForegroundServiceStartTimeoutMillis;
+        } else {
+            mAppIdleHistory.reportUsage(appHistory, pkg,
+                    STANDBY_BUCKET_ACTIVE, subReason,
+                    elapsedRealtime, elapsedRealtime + mStrongUsageTimeoutMillis);
+            nextCheckDelay = mStrongUsageTimeoutMillis;
+        }
+        if (appHistory.currentBucket != prevBucket) {
+            mHandler.sendMessageDelayed(
+                    mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, pkg),
+                    nextCheckDelay);
+            final boolean userStartedInteracting =
+                    appHistory.currentBucket == STANDBY_BUCKET_ACTIVE
+                            && (prevBucketReason & REASON_MAIN_MASK) != REASON_MAIN_USAGE;
+            maybeInformListeners(pkg, userId, elapsedRealtime,
+                    appHistory.currentBucket, reason, userStartedInteracting);
+        }
+
+        if (previouslyIdle) {
+            notifyBatteryStats(pkg, userId, false);
+        }
+    }
+
     private int usageEventToSubReason(int eventType) {
         switch (eventType) {
             case UsageEvents.Event.ACTIVITY_RESUMED: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
@@ -1147,9 +1189,11 @@
             final boolean isForcedByUser =
                     (reason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_USER;
 
-            // If the current bucket is RESTRICTED, only user force or usage should bring it out.
+            // If the current bucket is RESTRICTED, only user force or usage should bring it out,
+            // unless the app was put into the bucket due to timing out.
             if (app.currentBucket == STANDBY_BUCKET_RESTRICTED && !isUserUsage(reason)
-                    && !isForcedByUser) {
+                    && !isForcedByUser
+                    && (app.bucketingReason & REASON_MAIN_MASK) != REASON_MAIN_TIMEOUT) {
                 return;
             }
 
@@ -1175,7 +1219,7 @@
                     }
                 } else {
                     final long timeUntilRestrictPossibleMs = app.lastUsedByUserElapsedTime
-                            + mInjector.getRestrictedBucketDelayMs() - elapsedRealtime;
+                            + mInjector.getAutoRestrictedBucketDelayMs() - elapsedRealtime;
                     if (timeUntilRestrictPossibleMs > 0) {
                         Slog.w(TAG, "Tried to restrict recently used app: " + packageName
                                 + " due to " + reason);
@@ -1522,10 +1566,9 @@
         int mBootPhase;
         /**
          * The minimum amount of time required since the last user interaction before an app can be
-         * placed in the RESTRICTED bucket.
+         * automatically placed in the RESTRICTED bucket.
          */
-        // TODO: make configurable via DeviceConfig
-        private long mRestrictedBucketDelayMs = ONE_DAY;
+        long mAutoRestrictedBucketDelayMs = ONE_DAY;
 
         Injector(Context context, Looper looper) {
             mContext = context;
@@ -1554,7 +1597,7 @@
                 final ActivityManager activityManager =
                         (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
                 if (activityManager.isLowRamDevice() || ActivityManager.isSmallBatteryDevice()) {
-                    mRestrictedBucketDelayMs = 12 * ONE_HOUR;
+                    mAutoRestrictedBucketDelayMs = 12 * ONE_HOUR;
                 }
             }
             mBootPhase = phase;
@@ -1595,8 +1638,13 @@
             return Environment.getDataSystemDirectory();
         }
 
-        long getRestrictedBucketDelayMs() {
-            return mRestrictedBucketDelayMs;
+        /**
+         * Return the minimum amount of time that must have passed since the last user usage before
+         * an app can be automatically put into the
+         * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket.
+         */
+        long getAutoRestrictedBucketDelayMs() {
+            return mAutoRestrictedBucketDelayMs;
         }
 
         void noteEvent(int event, String packageName, int uid) throws RemoteException {
@@ -1772,6 +1820,8 @@
                 "system_interaction_duration";
         private static final String KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION =
                 "initial_foreground_service_start_duration";
+        private static final String KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS =
+                "auto_restricted_bucket_delay_ms";
         public static final long DEFAULT_STRONG_USAGE_TIMEOUT = 1 * ONE_HOUR;
         public static final long DEFAULT_NOTIFICATION_TIMEOUT = 12 * ONE_HOUR;
         public static final long DEFAULT_SYSTEM_UPDATE_TIMEOUT = 2 * ONE_HOUR;
@@ -1782,6 +1832,7 @@
         public static final long DEFAULT_EXEMPTED_SYNC_START_TIMEOUT = 10 * ONE_MINUTE;
         public static final long DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT = 10 * ONE_MINUTE;
         public static final long DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT = 30 * ONE_MINUTE;
+        public static final long DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS = ONE_DAY;
 
         private final KeyValueListParser mParser = new KeyValueListParser(',');
 
@@ -1829,12 +1880,12 @@
 
                 String screenThresholdsValue = mParser.getString(KEY_SCREEN_TIME_THRESHOLDS, null);
                 mAppStandbyScreenThresholds = parseLongArray(screenThresholdsValue,
-                        SCREEN_TIME_THRESHOLDS);
+                        SCREEN_TIME_THRESHOLDS, MINIMUM_SCREEN_TIME_THRESHOLDS);
 
                 String elapsedThresholdsValue = mParser.getString(KEY_ELAPSED_TIME_THRESHOLDS,
                         null);
                 mAppStandbyElapsedThresholds = parseLongArray(elapsedThresholdsValue,
-                        ELAPSED_TIME_THRESHOLDS);
+                        ELAPSED_TIME_THRESHOLDS, MINIMUM_ELAPSED_TIME_THRESHOLDS);
                 mCheckIdleIntervalMillis = Math.min(mAppStandbyElapsedThresholds[1] / 4,
                         COMPRESS_TIME ? ONE_MINUTE : 4 * 60 * ONE_MINUTE); // 4 hours
                 mStrongUsageTimeoutMillis = mParser.getDurationMillis(
@@ -1870,8 +1921,8 @@
 
                 mUnexemptedSyncScheduledTimeoutMillis = mParser.getDurationMillis(
                         KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION,
-                                COMPRESS_TIME ? ONE_MINUTE
-                                        : DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT); // TODO
+                                COMPRESS_TIME
+                                        ? ONE_MINUTE : DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT);
 
                 mSystemInteractionTimeoutMillis = mParser.getDurationMillis(
                         KEY_SYSTEM_INTERACTION_HOLD_DURATION,
@@ -1881,6 +1932,12 @@
                         KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION,
                         COMPRESS_TIME ? ONE_MINUTE :
                                 DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT);
+
+                mInjector.mAutoRestrictedBucketDelayMs = Math.max(
+                        COMPRESS_TIME ? ONE_MINUTE : 2 * ONE_HOUR,
+                        mParser.getDurationMillis(KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS,
+                                COMPRESS_TIME
+                                        ? ONE_MINUTE : DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS));
             }
 
             // Check if app_idle_enabled has changed. Do this after getting the rest of the settings
@@ -1888,7 +1945,7 @@
             setAppIdleEnabled(mInjector.isAppIdleEnabled());
         }
 
-        long[] parseLongArray(String values, long[] defaults) {
+        long[] parseLongArray(String values, long[] defaults, long[] minValues) {
             if (values == null) return defaults;
             if (values.isEmpty()) {
                 // Reset to defaults
@@ -1896,13 +1953,19 @@
             } else {
                 String[] thresholds = values.split("/");
                 if (thresholds.length == THRESHOLD_BUCKETS.length) {
+                    if (minValues.length != THRESHOLD_BUCKETS.length) {
+                        Slog.wtf(TAG, "minValues array is the wrong size");
+                        // Use zeroes as the minimums.
+                        minValues = new long[THRESHOLD_BUCKETS.length];
+                    }
                     long[] array = new long[THRESHOLD_BUCKETS.length];
                     for (int i = 0; i < THRESHOLD_BUCKETS.length; i++) {
                         try {
                             if (thresholds[i].startsWith("P") || thresholds[i].startsWith("p")) {
-                                array[i] = Duration.parse(thresholds[i]).toMillis();
+                                array[i] = Math.max(minValues[i],
+                                        Duration.parse(thresholds[i]).toMillis());
                             } else {
-                                array[i] = Long.parseLong(thresholds[i]);
+                                array[i] = Math.max(minValues[i], Long.parseLong(thresholds[i]));
                             }
                         } catch (NumberFormatException|DateTimeParseException e) {
                             return defaults;
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index 23ae8af..821dd9e 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -19,21 +19,6 @@
         ":updatable-media-srcs",
     ],
 
-    aidl: {
-        export_include_dirs: [
-            "java",
-        ],
-
-        // It would be great if we don't need to add include_dirs for public
-        // parcelable classes. Find a better way.
-        include_dirs: [
-            // To refer:
-            // android.os.Bundle
-            // android.os.ResultReceiver
-            "frameworks/base/core/java",
-        ],
-    },
-
     permitted_packages: [
         "android.media",
     ],
@@ -46,13 +31,9 @@
 
     installable: true,
 
-    // TODO: build against stable API surface. Use core_platform for now to avoid
-    // link-check failure with exoplayer building against "current".
-    sdk_version: "core_platform",
+    sdk_version: "module_current",
     libs: [
-        // The order matters. android_system_* library should come later.
         "framework_media_annotation",
-        "android_system_stubs_current",
     ],
 
     static_libs: [
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index 96110e1..3eed26b 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -811,11 +811,14 @@
 
     private static MediaFormat toMediaFormat(Format format) {
 
-        // TODO: Add if (value != Format.NO_VALUE);
-
         MediaFormat result = new MediaFormat();
-        result.setInteger(MediaFormat.KEY_BIT_RATE, format.bitrate);
-        result.setInteger(MediaFormat.KEY_CHANNEL_COUNT, format.channelCount);
+        if (format.bitrate != Format.NO_VALUE) {
+            result.setInteger(MediaFormat.KEY_BIT_RATE, format.bitrate);
+        }
+        if (format.channelCount != Format.NO_VALUE) {
+            result.setInteger(MediaFormat.KEY_CHANNEL_COUNT, format.channelCount);
+        }
+
         if (format.colorInfo != null) {
             result.setInteger(MediaFormat.KEY_COLOR_TRANSFER, format.colorInfo.colorTransfer);
             result.setInteger(MediaFormat.KEY_COLOR_RANGE, format.colorInfo.colorRange);
@@ -826,25 +829,44 @@
                         ByteBuffer.wrap(format.colorInfo.hdrStaticInfo));
             }
         }
-        result.setString(MediaFormat.KEY_MIME, format.sampleMimeType);
-        result.setFloat(MediaFormat.KEY_FRAME_RATE, format.frameRate);
-        result.setInteger(MediaFormat.KEY_WIDTH, format.width);
-        result.setInteger(MediaFormat.KEY_HEIGHT, format.height);
+
+        if (format.sampleMimeType != null) {
+            result.setString(MediaFormat.KEY_MIME, format.sampleMimeType);
+        }
+        if (format.codecs != null) {
+            result.setString(MediaFormat.KEY_CODECS_STRING, format.codecs);
+        }
+        if (format.frameRate != Format.NO_VALUE) {
+            result.setFloat(MediaFormat.KEY_FRAME_RATE, format.frameRate);
+        }
+        if (format.width != Format.NO_VALUE) {
+            result.setInteger(MediaFormat.KEY_WIDTH, format.width);
+        }
+        if (format.height != Format.NO_VALUE) {
+            result.setInteger(MediaFormat.KEY_HEIGHT, format.height);
+        }
         List<byte[]> initData = format.initializationData;
         if (initData != null) {
             for (int i = 0; i < initData.size(); i++) {
                 result.setByteBuffer("csd-" + i, ByteBuffer.wrap(initData.get(i)));
             }
         }
-        result.setString(MediaFormat.KEY_LANGUAGE, format.language);
-        result.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, format.maxInputSize);
-        result.setInteger(MediaFormat.KEY_PCM_ENCODING, format.pcmEncoding);
-        result.setInteger(MediaFormat.KEY_ROTATION, format.rotationDegrees);
-        result.setInteger(MediaFormat.KEY_SAMPLE_RATE, format.sampleRate);
-
+        if (format.language != null) {
+            result.setString(MediaFormat.KEY_LANGUAGE, format.language);
+        }
+        if (format.maxInputSize != Format.NO_VALUE) {
+            result.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, format.maxInputSize);
+        }
+        if (format.pcmEncoding != Format.NO_VALUE) {
+            result.setInteger(MediaFormat.KEY_PCM_ENCODING, format.pcmEncoding);
+        }
+        if (format.rotationDegrees != Format.NO_VALUE) {
+            result.setInteger(MediaFormat.KEY_ROTATION, format.rotationDegrees);
+        }
+        if (format.sampleRate != Format.NO_VALUE) {
+            result.setInteger(MediaFormat.KEY_SAMPLE_RATE, format.sampleRate);
+        }
         int selectionFlags = format.selectionFlags;
-        // We avoid setting selection flags in the MediaFormat, unless explicitly signaled by the
-        // extractor.
         if ((selectionFlags & C.SELECTION_FLAG_AUTOSELECT) != 0) {
             result.setInteger(MediaFormat.KEY_IS_AUTOSELECT, 1);
         }
@@ -854,28 +876,29 @@
         if ((selectionFlags & C.SELECTION_FLAG_FORCED) != 0) {
             result.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, 1);
         }
+        if (format.encoderDelay != Format.NO_VALUE) {
+            result.setInteger(MediaFormat.KEY_ENCODER_DELAY, format.encoderDelay);
+        }
+        if (format.encoderPadding != Format.NO_VALUE) {
+            result.setInteger(MediaFormat.KEY_ENCODER_PADDING, format.encoderPadding);
+        }
+        // TODO: Implement float to fraction conversion.
+        // if (format.pixelWidthHeightRatio != Format.NO_VALUE) {
+        //     result.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH, );
+        //     result.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT, );
+        // }
 
         // LACK OF SUPPORT FOR:
         //    format.accessibilityChannel;
-        //    format.codecs;
         //    format.containerMimeType;
-        //    format.drmInitData;
-        //    format.encoderDelay;
-        //    format.encoderPadding;
         //    format.id;
         //    format.metadata;
-        //    format.pixelWidthHeightRatio;
         //    format.roleFlags;
         //    format.stereoMode;
         //    format.subsampleOffsetUs;
         return result;
     }
 
-    private static int toFrameworkFlags(int flags) {
-        // TODO: Implement.
-        return 0;
-    }
-
     private static DrmInitData toFrameworkDrmInitData(
             com.google.android.exoplayer2.drm.DrmInitData drmInitData) {
         // TODO: Implement.
diff --git a/apex/permission/framework/Android.bp b/apex/permission/framework/Android.bp
index 126fa00..6d96200 100644
--- a/apex/permission/framework/Android.bp
+++ b/apex/permission/framework/Android.bp
@@ -26,13 +26,7 @@
     srcs: [
         ":framework-permission-sources",
     ],
-    // TODO(b/146758669): Use "system_current" after nullability annotations are system APIs.
-    sdk_version: "core_current",
-    libs: [
-        "framework-annotations-lib",
-        // TODO(b/146758669): Remove this line after nullability annotations are system APIs.
-        "android_system_stubs_current",
-    ],
+    sdk_version: "module_current",
     apex_available: [
         "com.android.permission",
         "test_com.android.permission",
diff --git a/apex/permission/service/Android.bp b/apex/permission/service/Android.bp
index 4172e95..8d66431 100644
--- a/apex/permission/service/Android.bp
+++ b/apex/permission/service/Android.bp
@@ -24,12 +24,9 @@
     srcs: [
         ":service-permission-sources",
     ],
-    // TODO(b/146758669): Use "system_current" after nullability annotations are system APIs.
-    sdk_version: "core_current",
+    sdk_version: "module_current",
     libs: [
         "framework-annotations-lib",
-        // TODO(b/146758669): Remove this line after nullability annotations are system APIs.
-        "android_system_stubs_current",
         "framework-permission",
     ],
     apex_available: [
diff --git a/apex/statsd/aidl/Android.bp b/apex/statsd/aidl/Android.bp
index db5f439..7c93bc7 100644
--- a/apex/statsd/aidl/Android.bp
+++ b/apex/statsd/aidl/Android.bp
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 filegroup {
-    name: "statsd_java_aidl",
+    name: "framework-statsd-aidl-sources",
     srcs: ["**/*.aidl"],
 }
 
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index 80def47..63a853a 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -31,35 +31,56 @@
     name: "framework-statsd-sources",
     srcs: [
         "java/**/*.java",
-        ":statsd_java_aidl",
+        ":framework-statsd-aidl-sources",
         ":statslog-statsd-java-gen",
     ],
 }
 
+java_defaults {
+    name: "framework-statsd-defaults",
+
+    // TODO(b/146757305): Use "module_current" once it's ready.
+    sdk_version: "core_current",
+
+    libs: [
+        "framework-annotations-lib",
+
+        // TODO(b/146757305): should be unnecessary once
+        // sdk_version="module_lib_current" or "module_current"
+        "android_module_lib_stubs_current",
+    ],
+}
+
 java_library {
     name: "framework-statsd",
+    defaults: [
+        "framework-statsd-defaults",
+    ],
     installable: true,
-    // TODO(b/146209659): Use system_current instead.
-    sdk_version: "core_platform",
+
     srcs: [
         ":framework-statsd-sources",
     ],
+
+    aidl: {
+        // TODO(b/146757305): should be unnecessary once
+        // sdk_version="module_lib_current" or "module_current"
+        include_dirs: [
+            // To refer:
+            // android.app.PendintIntent
+            "frameworks/base/core/java",
+        ],
+    },
+
     permitted_packages: [
         "android.app",
         "android.os",
         "android.util",
     ],
-    libs: [
-        "framework-annotations-lib",
-        // TODO(b/146230220): Use android_module_lib_stubs_current instead.
-        //"android_module_lib_stubs_current",
-        "framework-all",
-    ],
+
     hostdex: true, // for hiddenapi check
     visibility: [
         "//frameworks/base/apex/statsd:__subpackages__",
-        //TODO(b/146167933) remove this
-        "//frameworks/opt/net/wifi/service",
     ],
     apex_available: [
         "com.android.os.statsd",
@@ -69,13 +90,14 @@
 
 stubs_defaults {
     name: "framework-statsd-stubs-srcs-defaults",
-    srcs: [ ":framework-statsd-sources" ],
+    srcs: [
+        ":framework-statsd-sources",
+    ],
+
     libs: [
-        // TODO(b/148218250): Change to android_system_stubs_current
-        "framework-all",
         "framework-annotations-lib",
     ],
-    sdk_version: "core_platform",
+    sdk_version: "system_current",
 }
 
 droidstubs {
@@ -92,6 +114,7 @@
         "framework-module-stubs-defaults-systemapi",
         "framework-statsd-stubs-srcs-defaults",
     ],
+
 }
 
 droidstubs {
@@ -112,24 +135,24 @@
 
 java_library {
     name: "framework-statsd-stubs-publicapi",
+    defaults: [
+        "framework-statsd-defaults",
+    ],
     srcs: [ ":framework-statsd-stubs-srcs-publicapi" ],
-    // TODO(b/148218250): Change to current
-    libs: [ "framework-all" ],
-    sdk_version: "core_platform",
 }
 
 java_library {
     name: "framework-statsd-stubs-systemapi",
+    defaults: [
+        "framework-statsd-defaults",
+    ],
     srcs: [ ":framework-statsd-stubs-srcs-systemapi" ],
-    // TODO(b/148218250): Change to system_current
-    libs: [ "framework-all" ],
-    sdk_version: "core_platform",
 }
 
 java_library {
     name: "framework-statsd-stubs-module_libs_api",
-    srcs: [ ":framework-statsd-stubs-srcs-systemapi" ],
-    // TODO(b/148218250): Change to system_current
-    libs: [ "framework-all" ],
-    sdk_version: "core_platform",
+    defaults: [
+        "framework-statsd-defaults",
+    ],
+    srcs: [ ":framework-statsd-stubs-srcs-module_libs_api" ],
 }
diff --git a/apex/statsd/framework/java/android/os/StatsFrameworkInitializer.java b/apex/statsd/framework/java/android/os/StatsFrameworkInitializer.java
index 3d95533..8dc9123 100644
--- a/apex/statsd/framework/java/android/os/StatsFrameworkInitializer.java
+++ b/apex/statsd/framework/java/android/os/StatsFrameworkInitializer.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.annotation.SystemApi.Client;
 import android.app.StatsManager;
 import android.app.SystemServiceRegistry;
 import android.content.Context;
@@ -24,10 +25,9 @@
 /**
  * Class for performing registration for all stats services
  *
- * TODO(b/148225705) Change to @SystemApi(client=MODULE_LIBRARIES) when the build system is ready.
  * @hide
  */
-@SystemApi
+@SystemApi(client = Client.MODULE_LIBRARIES)
 public class StatsFrameworkInitializer {
     private StatsFrameworkInitializer() {
     }
diff --git a/apex/statsd/service/Android.bp b/apex/statsd/service/Android.bp
index 0f8a151..0f325e3 100644
--- a/apex/statsd/service/Android.bp
+++ b/apex/statsd/service/Android.bp
@@ -16,16 +16,21 @@
     srcs: [
         ":service-statsd-sources",
     ],
-    // TODO(b/146209659): Use system_current instead once framework-statsd compiles against
-    // system_current.
+
+    // TODO(b/146757305) should be "module_current" since not allowed to use
+    // @CorePlatformApi's
     sdk_version: "core_platform",
+
     libs: [
         "framework-annotations-lib",
-        "framework-statsd",
-        // TODO(b/146758669): Remove this line after nullability annotations are system APIs.
-        "android_system_stubs_current",
         "services-stubs",
+        "framework-statsd",
+
+        // TODO(b/146757305): should be unnecessary once
+        // sdk_version="module_lib_current" or "module_current"
+        "android_module_lib_stubs_current",
     ],
+
     apex_available: [
         "com.android.os.statsd",
         "test_com.android.os.statsd",
diff --git a/api/current.txt b/api/current.txt
index 826d409..89faaa3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -91,6 +91,7 @@
     field public static final String INTERACT_ACROSS_PROFILES = "android.permission.INTERACT_ACROSS_PROFILES";
     field public static final String INTERNET = "android.permission.INTERNET";
     field public static final String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES";
+    field public static final String LOADER_USAGE_STATS = "android.permission.LOADER_USAGE_STATS";
     field public static final String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
     field public static final String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
     field public static final String MANAGE_EXTERNAL_STORAGE = "android.permission.MANAGE_EXTERNAL_STORAGE";
@@ -7574,7 +7575,9 @@
 
   public class BlobStoreManager {
     method public void acquireLease(@NonNull android.app.blob.BlobHandle, @IdRes int, long) throws java.io.IOException;
+    method public void acquireLease(@NonNull android.app.blob.BlobHandle, @NonNull CharSequence, long) throws java.io.IOException;
     method public void acquireLease(@NonNull android.app.blob.BlobHandle, @IdRes int) throws java.io.IOException;
+    method public void acquireLease(@NonNull android.app.blob.BlobHandle, @NonNull CharSequence) throws java.io.IOException;
     method @IntRange(from=1) public long createSession(@NonNull android.app.blob.BlobHandle) throws java.io.IOException;
     method public void deleteSession(@IntRange(from=1) long) throws java.io.IOException;
     method @NonNull public android.os.ParcelFileDescriptor openBlob(@NonNull android.app.blob.BlobHandle) throws java.io.IOException;
@@ -12575,6 +12578,7 @@
     method public int getLayoutDirection();
     method @NonNull public android.os.LocaleList getLocales();
     method public boolean isLayoutSizeAtLeast(int);
+    method public boolean isNightModeActive();
     method public boolean isScreenHdr();
     method public boolean isScreenRound();
     method public boolean isScreenWideColorGamut();
@@ -17032,6 +17036,7 @@
     field public static final int BIOMETRIC_ERROR_HW_UNAVAILABLE = 1; // 0x1
     field public static final int BIOMETRIC_ERROR_NONE_ENROLLED = 11; // 0xb
     field public static final int BIOMETRIC_ERROR_NO_HARDWARE = 12; // 0xc
+    field public static final int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15; // 0xf
     field public static final int BIOMETRIC_SUCCESS = 0; // 0x0
   }
 
@@ -17066,6 +17071,7 @@
     field public static final int BIOMETRIC_ERROR_NO_BIOMETRICS = 11; // 0xb
     field public static final int BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14; // 0xe
     field public static final int BIOMETRIC_ERROR_NO_SPACE = 4; // 0x4
+    field public static final int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15; // 0xf
     field public static final int BIOMETRIC_ERROR_TIMEOUT = 3; // 0x3
     field public static final int BIOMETRIC_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
     field public static final int BIOMETRIC_ERROR_USER_CANCELED = 10; // 0xa
@@ -23472,6 +23478,69 @@
     method public static boolean isPresent();
   }
 
+  public final class GnssAntennaInfo implements android.os.Parcelable {
+    ctor public GnssAntennaInfo(double, @NonNull android.location.GnssAntennaInfo.PhaseCenterOffsetCoordinates, @Nullable android.location.GnssAntennaInfo.PhaseCenterVariationCorrections, @Nullable android.location.GnssAntennaInfo.SignalGainCorrections);
+    method public int describeContents();
+    method public double getCarrierFrequencyMHz();
+    method @NonNull public android.location.GnssAntennaInfo.PhaseCenterOffsetCoordinates getPhaseCenterOffsetCoordinates();
+    method @Nullable public android.location.GnssAntennaInfo.PhaseCenterVariationCorrections getPhaseCenterVariationCorrections();
+    method @Nullable public android.location.GnssAntennaInfo.SignalGainCorrections getSignalGainCorrections();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAntennaInfo> CREATOR;
+  }
+
+  public abstract static class GnssAntennaInfo.Callback {
+    ctor public GnssAntennaInfo.Callback();
+    method public void onGnssAntennaInfoReceived(@NonNull java.util.List<android.location.GnssAntennaInfo>);
+    method public void onStatusChanged(int);
+    field public static final int STATUS_LOCATION_DISABLED = 2; // 0x2
+    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
+    field public static final int STATUS_READY = 1; // 0x1
+  }
+
+  public static final class GnssAntennaInfo.PhaseCenterOffsetCoordinates implements android.os.Parcelable {
+    ctor public GnssAntennaInfo.PhaseCenterOffsetCoordinates(double, double, double, double, double, double);
+    method public int describeContents();
+    method public double getXCoordMillimeters();
+    method public double getXCoordUncertaintyMillimeters();
+    method public double getYCoordMillimeters();
+    method public double getYCoordUncertaintyMillimeters();
+    method public double getZCoordMillimeters();
+    method public double getZCoordUncertaintyMillimeters();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAntennaInfo.PhaseCenterOffsetCoordinates> CREATOR;
+  }
+
+  public static final class GnssAntennaInfo.PhaseCenterVariationCorrections implements android.os.Parcelable {
+    ctor public GnssAntennaInfo.PhaseCenterVariationCorrections(@NonNull double[][], @NonNull double[][]);
+    method public int describeContents();
+    method public double getDeltaPhi();
+    method public double getDeltaTheta();
+    method public int getNumColumns();
+    method public int getNumRows();
+    method public double getPhaseCenterVariationCorrectionMillimetersAt(int, int);
+    method public double getPhaseCenterVariationCorrectionUncertaintyMillimetersAt(int, int);
+    method @NonNull public double[][] getRawCorrectionUncertaintiesArray();
+    method @NonNull public double[][] getRawCorrectionsArray();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAntennaInfo.PhaseCenterVariationCorrections> CREATOR;
+  }
+
+  public static final class GnssAntennaInfo.SignalGainCorrections implements android.os.Parcelable {
+    ctor public GnssAntennaInfo.SignalGainCorrections(@NonNull double[][], @NonNull double[][]);
+    method public int describeContents();
+    method public double getDeltaPhi();
+    method public double getDeltaTheta();
+    method public int getNumColumns();
+    method public int getNumRows();
+    method @NonNull public double[][] getRawCorrectionUncertaintiesArray();
+    method @NonNull public double[][] getRawCorrectionsArray();
+    method public double getSignalGainCorrectionDbiAt(int, int);
+    method public double getSignalGainCorrectionUncertaintyDbiAt(int, int);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAntennaInfo.SignalGainCorrections> CREATOR;
+  }
+
   public final class GnssClock implements android.os.Parcelable {
     method public int describeContents();
     method public double getBiasNanos();
@@ -23789,6 +23858,7 @@
     method @NonNull public java.util.List<java.lang.String> getProviders(@NonNull android.location.Criteria, boolean);
     method public boolean isLocationEnabled();
     method public boolean isProviderEnabled(@NonNull String);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerAntennaInfoCallback(@NonNull java.util.concurrent.Executor, @NonNull android.location.GnssAntennaInfo.Callback);
     method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssMeasurementsEvent.Callback);
     method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssMeasurementsEvent.Callback, @Nullable android.os.Handler);
     method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback);
@@ -23820,6 +23890,7 @@
     method public void setTestProviderEnabled(@NonNull String, boolean);
     method public void setTestProviderLocation(@NonNull String, @NonNull android.location.Location);
     method @Deprecated public void setTestProviderStatus(@NonNull String, int, @Nullable android.os.Bundle, long);
+    method public void unregisterAntennaInfoCallback(@NonNull android.location.GnssAntennaInfo.Callback);
     method public void unregisterGnssMeasurementsCallback(@NonNull android.location.GnssMeasurementsEvent.Callback);
     method public void unregisterGnssNavigationMessageCallback(@NonNull android.location.GnssNavigationMessage.Callback);
     method public void unregisterGnssStatusCallback(@NonNull android.location.GnssStatus.Callback);
@@ -26811,7 +26882,8 @@
     method public void onDiscoveryPreferenceChanged(@NonNull android.media.RouteDiscoveryPreference);
     method public abstract void onReleaseSession(@NonNull String);
     method public abstract void onSelectRoute(@NonNull String, @NonNull String);
-    method public abstract void onSetVolume(@NonNull String, int);
+    method public abstract void onSetRouteVolume(@NonNull String, int);
+    method public abstract void onSetSessionVolume(@NonNull String, int);
     method public abstract void onTransferToRoute(@NonNull String, @NonNull String);
     field public static final long REQUEST_ID_UNKNOWN = 0L; // 0x0L
     field public static final String SERVICE_INTERFACE = "android.media.MediaRoute2ProviderService";
@@ -26972,9 +27044,13 @@
     method @NonNull public java.util.List<android.media.MediaRoute2Info> getSelectableRoutes();
     method @NonNull public java.util.List<android.media.MediaRoute2Info> getSelectedRoutes();
     method @NonNull public java.util.List<android.media.MediaRoute2Info> getTransferrableRoutes();
+    method public int getVolume();
+    method public int getVolumeHandling();
+    method public int getVolumeMax();
     method public boolean isReleased();
     method public void release();
     method public void selectRoute(@NonNull android.media.MediaRoute2Info);
+    method public void setVolume(int);
     method public void transferToRoute(@NonNull android.media.MediaRoute2Info);
   }
 
@@ -27369,6 +27445,9 @@
     method @NonNull public java.util.List<java.lang.String> getSelectableRoutes();
     method @NonNull public java.util.List<java.lang.String> getSelectedRoutes();
     method @NonNull public java.util.List<java.lang.String> getTransferrableRoutes();
+    method public int getVolume();
+    method public int getVolumeHandling();
+    method public int getVolumeMax();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.media.RoutingSessionInfo> CREATOR;
   }
@@ -27390,6 +27469,9 @@
     method @NonNull public android.media.RoutingSessionInfo.Builder removeSelectedRoute(@NonNull String);
     method @NonNull public android.media.RoutingSessionInfo.Builder removeTransferrableRoute(@NonNull String);
     method @NonNull public android.media.RoutingSessionInfo.Builder setControlHints(@Nullable android.os.Bundle);
+    method @NonNull public android.media.RoutingSessionInfo.Builder setVolume(int);
+    method @NonNull public android.media.RoutingSessionInfo.Builder setVolumeHandling(int);
+    method @NonNull public android.media.RoutingSessionInfo.Builder setVolumeMax(int);
   }
 
   public final class Session2Command implements android.os.Parcelable {
@@ -29718,10 +29800,12 @@
   }
 
   public static final class ConnectivityDiagnosticsManager.DataStallReport implements android.os.Parcelable {
-    ctor public ConnectivityDiagnosticsManager.DataStallReport(@NonNull android.net.Network, long, int, @NonNull android.os.PersistableBundle);
+    ctor public ConnectivityDiagnosticsManager.DataStallReport(@NonNull android.net.Network, long, int, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle);
     method public int describeContents();
     method public int getDetectionMethod();
+    method @NonNull public android.net.LinkProperties getLinkProperties();
     method @NonNull public android.net.Network getNetwork();
+    method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities();
     method public long getReportTimestamp();
     method @NonNull public android.os.PersistableBundle getStallDetails();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -31280,6 +31364,7 @@
     method public int getWifiState();
     method public boolean is5GHzBandSupported();
     method public boolean is6GHzBandSupported();
+    method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isAutoWakeupEnabled();
     method @Deprecated public boolean isDeviceToApRttSupported();
     method public boolean isEasyConnectSupported();
     method public boolean isEnhancedOpenSupported();
@@ -40249,6 +40334,7 @@
     field public static final String VOLUME_NAME = "volume_name";
     field public static final String WIDTH = "width";
     field public static final String WRITER = "writer";
+    field public static final String XMP = "xmp";
     field public static final String YEAR = "year";
   }
 
@@ -42599,6 +42685,7 @@
     method @NonNull public String getKeystoreAlias();
     method public int getPurposes();
     method @NonNull public String[] getSignaturePaddings();
+    method public int getUserAuthenticationType();
     method public int getUserAuthenticationValidityDurationSeconds();
     method @NonNull public boolean isDigestsSpecified();
     method public boolean isInvalidatedByBiometricEnrollment();
@@ -42633,9 +42720,10 @@
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean);
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...);
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUnlockedDeviceRequired(boolean);
+    method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationParameters(@IntRange(from=0xffffffff) int, int);
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationRequired(boolean);
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidWhileOnBody(boolean);
-    method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidityDurationSeconds(@IntRange(from=0xffffffff) int);
+    method @Deprecated @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidityDurationSeconds(@IntRange(from=0xffffffff) int);
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUserConfirmationRequired(boolean);
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUserPresenceRequired(boolean);
   }
@@ -42652,6 +42740,7 @@
     method public int getOrigin();
     method public int getPurposes();
     method @NonNull public String[] getSignaturePaddings();
+    method public int getUserAuthenticationType();
     method public int getUserAuthenticationValidityDurationSeconds();
     method public boolean isInsideSecureHardware();
     method public boolean isInvalidatedByBiometricEnrollment();
@@ -42675,6 +42764,8 @@
   }
 
   public abstract class KeyProperties {
+    field public static final int AUTH_BIOMETRIC_STRONG = 2; // 0x2
+    field public static final int AUTH_DEVICE_CREDENTIAL = 1; // 0x1
     field public static final String BLOCK_MODE_CBC = "CBC";
     field public static final String BLOCK_MODE_CTR = "CTR";
     field public static final String BLOCK_MODE_ECB = "ECB";
@@ -42721,6 +42812,7 @@
     method @Nullable public java.util.Date getKeyValidityStart();
     method public int getPurposes();
     method @NonNull public String[] getSignaturePaddings();
+    method public int getUserAuthenticationType();
     method public int getUserAuthenticationValidityDurationSeconds();
     method public boolean isDigestsSpecified();
     method public boolean isInvalidatedByBiometricEnrollment();
@@ -42746,9 +42838,10 @@
     method @NonNull public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean);
     method @NonNull public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...);
     method @NonNull public android.security.keystore.KeyProtection.Builder setUnlockedDeviceRequired(boolean);
+    method @NonNull public android.security.keystore.KeyProtection.Builder setUserAuthenticationParameters(@IntRange(from=0xffffffff) int, int);
     method @NonNull public android.security.keystore.KeyProtection.Builder setUserAuthenticationRequired(boolean);
     method @NonNull public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidWhileOnBody(boolean);
-    method @NonNull public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidityDurationSeconds(@IntRange(from=0xffffffff) int);
+    method @Deprecated @NonNull public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidityDurationSeconds(@IntRange(from=0xffffffff) int);
     method @NonNull public android.security.keystore.KeyProtection.Builder setUserConfirmationRequired(boolean);
     method @NonNull public android.security.keystore.KeyProtection.Builder setUserPresenceRequired(boolean);
   }
@@ -43292,6 +43385,7 @@
   public abstract class ControlsProviderService extends android.app.Service {
     ctor public ControlsProviderService();
     method public abstract void loadAvailableControls(@NonNull java.util.function.Consumer<java.util.List<android.service.controls.Control>>);
+    method public void loadSuggestedControls(int, @NonNull java.util.function.Consumer<java.util.List<android.service.controls.Control>>);
     method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
     method public abstract void performControlAction(@NonNull String, @NonNull android.service.controls.actions.ControlAction, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @NonNull public abstract java.util.concurrent.Flow.Publisher<android.service.controls.Control> publisherFor(@NonNull java.util.List<java.lang.String>);
@@ -45426,7 +45520,6 @@
     field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2
     field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80
     field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
-    field public static final int PROPERTY_IS_ADHOC_CONFERENCE = 8192; // 0x2000
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40
     field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 2048; // 0x800
     field public static final int PROPERTY_RTT = 1024; // 0x400
@@ -45504,7 +45597,6 @@
   public abstract class Conference extends android.telecom.Conferenceable {
     ctor public Conference(android.telecom.PhoneAccountHandle);
     method public final boolean addConnection(android.telecom.Connection);
-    method @NonNull public static android.telecom.Conference createFailedConference(@NonNull android.telecom.DisconnectCause, @NonNull android.telecom.PhoneAccountHandle);
     method public final void destroy();
     method public final android.telecom.CallAudioState getCallAudioState();
     method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
@@ -45519,8 +45611,6 @@
     method public final android.telecom.StatusHints getStatusHints();
     method public android.telecom.Connection.VideoProvider getVideoProvider();
     method public int getVideoState();
-    method public final boolean isRingbackRequested();
-    method public void onAnswer(int);
     method public void onCallAudioStateChanged(android.telecom.CallAudioState);
     method public void onConnectionAdded(android.telecom.Connection);
     method public void onDisconnect();
@@ -45529,7 +45619,6 @@
     method public void onMerge(android.telecom.Connection);
     method public void onMerge();
     method public void onPlayDtmfTone(char);
-    method public void onReject();
     method public void onSeparate(android.telecom.Connection);
     method public void onStopDtmfTone();
     method public void onSwap();
@@ -45549,8 +45638,6 @@
     method public final void setDisconnected(android.telecom.DisconnectCause);
     method public final void setExtras(@Nullable android.os.Bundle);
     method public final void setOnHold();
-    method public final void setRingbackRequested(boolean);
-    method public final void setRinging();
     method public final void setStatusHints(android.telecom.StatusHints);
     method public final void setVideoProvider(android.telecom.Connection, android.telecom.Connection.VideoProvider);
     method public final void setVideoState(android.telecom.Connection, int);
@@ -45709,7 +45796,6 @@
     field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200
     field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
     field public static final int PROPERTY_HIGH_DEF_AUDIO = 4; // 0x4
-    field public static final int PROPERTY_IS_ADHOC_CONFERENCE = 4096; // 0x1000
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
     field public static final int PROPERTY_IS_RTT = 256; // 0x100
     field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 1024; // 0x400
@@ -46125,7 +46211,6 @@
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ANSWER_PHONE_CALLS, android.Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ANSWER_PHONE_CALLS, android.Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall(int);
     method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
-    method public void addNewIncomingConference(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.os.Bundle);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void cancelMissedCallsNotification();
     method public android.content.Intent createManageBlockedNumbersIntent();
     method @Deprecated @RequiresPermission(android.Manifest.permission.ANSWER_PHONE_CALLS) public boolean endCall();
@@ -46153,7 +46238,6 @@
     method public void registerPhoneAccount(android.telecom.PhoneAccount);
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void showInCallScreen(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void silenceRinger();
-    method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void startConference(@NonNull java.util.List<android.net.Uri>, @NonNull android.os.Bundle);
     method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle);
     field public static final String ACTION_CHANGE_DEFAULT_DIALER = "android.telecom.action.CHANGE_DEFAULT_DIALER";
     field public static final String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
@@ -46680,7 +46764,6 @@
     field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
     field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
     field public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool";
-    field public static final String KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL = "support_adhoc_conference_calls_bool";
     field public static final String KEY_SUPPORT_CLIR_NETWORK_DEFAULT_BOOL = "support_clir_network_default_bool";
     field public static final String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
     field public static final String KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL = "support_emergency_sms_over_ims_bool";
@@ -55140,7 +55223,7 @@
   }
 
   public interface WindowInsetsController {
-    method public void controlWindowInsetsAnimation(int, long, @Nullable android.view.animation.Interpolator, @NonNull android.view.WindowInsetsAnimationControlListener);
+    method @NonNull public android.os.CancellationSignal controlWindowInsetsAnimation(int, long, @Nullable android.view.animation.Interpolator, @NonNull android.view.WindowInsetsAnimationControlListener);
     method public int getSystemBarsAppearance();
     method public int getSystemBarsBehavior();
     method public void hide(int);
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 59aa145..7b66f73 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -1,4 +1,14 @@
 // Signature format: 2.0
+package android.annotation {
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD}) public @interface NonNull {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD}) public @interface Nullable {
+  }
+
+}
+
 package android.net {
 
   public final class TetheredClient implements android.os.Parcelable {
@@ -120,6 +130,30 @@
 
 }
 
+package android.os {
+
+  public class StatsFrameworkInitializer {
+    method public static void registerServiceWrappers();
+    method public static void setStatsServiceManager(@NonNull android.os.StatsServiceManager);
+  }
+
+  public class StatsServiceManager {
+    method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsCompanionServiceRegisterer();
+    method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsManagerServiceRegisterer();
+    method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsdServiceRegisterer();
+  }
+
+  public static class StatsServiceManager.ServiceNotFoundException extends java.lang.Exception {
+    ctor public StatsServiceManager.ServiceNotFoundException(@NonNull String);
+  }
+
+  public static final class StatsServiceManager.ServiceRegisterer {
+    method @Nullable public android.os.IBinder get();
+    method @Nullable public android.os.IBinder getOrThrow() throws android.os.StatsServiceManager.ServiceNotFoundException;
+  }
+
+}
+
 package android.os.ext.test {
 
   @Deprecated public class Test {
diff --git a/api/system-current.txt b/api/system-current.txt
index 24936d5..838d23f 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -18,6 +18,7 @@
     field public static final String ACCESS_SHORTCUTS = "android.permission.ACCESS_SHORTCUTS";
     field public static final String ACCESS_SURFACE_FLINGER = "android.permission.ACCESS_SURFACE_FLINGER";
     field public static final String ACCESS_TV_TUNER = "android.permission.ACCESS_TV_TUNER";
+    field public static final String ACCESS_VIBRATOR_STATE = "android.permission.ACCESS_VIBRATOR_STATE";
     field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
     field public static final String ADJUST_RUNTIME_PERMISSIONS_POLICY = "android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY";
     field public static final String ALLOCATE_AGGRESSIVE = "android.permission.ALLOCATE_AGGRESSIVE";
@@ -391,6 +392,7 @@
     field public static final String OPSTR_INSTANT_APP_START_FOREGROUND = "android:instant_app_start_foreground";
     field public static final String OPSTR_INTERACT_ACROSS_PROFILES = "android:interact_across_profiles";
     field public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage";
+    field public static final String OPSTR_LOADER_USAGE_STATS = "android:loader_usage_stats";
     field public static final String OPSTR_MANAGE_EXTERNAL_STORAGE = "android:manage_external_storage";
     field public static final String OPSTR_MANAGE_IPSEC_TUNNELS = "android:manage_ipsec_tunnels";
     field public static final String OPSTR_MUTE_MICROPHONE = "android:mute_microphone";
@@ -1451,16 +1453,16 @@
 package android.bluetooth {
 
   public final class BluetoothA2dp implements android.bluetooth.BluetoothProfile {
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void disableOptionalCodecs(@Nullable android.bluetooth.BluetoothDevice);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void enableOptionalCodecs(@Nullable android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void disableOptionalCodecs(@NonNull android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void enableOptionalCodecs(@NonNull android.bluetooth.BluetoothDevice);
     method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH) public android.bluetooth.BluetoothDevice getActiveDevice();
-    method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH) public android.bluetooth.BluetoothCodecStatus getCodecStatus(@Nullable android.bluetooth.BluetoothDevice);
+    method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH) public android.bluetooth.BluetoothCodecStatus getCodecStatus(@NonNull android.bluetooth.BluetoothDevice);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public int getOptionalCodecsEnabled(@Nullable android.bluetooth.BluetoothDevice);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void setCodecConfigPreference(@Nullable android.bluetooth.BluetoothDevice, @Nullable android.bluetooth.BluetoothCodecConfig);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public int isOptionalCodecsEnabled(@NonNull android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public int isOptionalCodecsSupported(@NonNull android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void setCodecConfigPreference(@NonNull android.bluetooth.BluetoothDevice, @NonNull android.bluetooth.BluetoothCodecConfig);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void setOptionalCodecsEnabled(@Nullable android.bluetooth.BluetoothDevice, int);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public int supportsOptionalCodecs(@Nullable android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void setOptionalCodecsEnabled(@NonNull android.bluetooth.BluetoothDevice, int);
     field public static final int OPTIONAL_CODECS_NOT_SUPPORTED = 0; // 0x0
     field public static final int OPTIONAL_CODECS_PREF_DISABLED = 0; // 0x0
     field public static final int OPTIONAL_CODECS_PREF_ENABLED = 1; // 0x1
@@ -1471,10 +1473,10 @@
 
   public final class BluetoothA2dpSink implements android.bluetooth.BluetoothProfile {
     method public void finalize();
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isAudioPlaying(@Nullable android.bluetooth.BluetoothDevice);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice, int);
-    field @RequiresPermission(android.Manifest.permission.BLUETOOTH) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED";
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isAudioPlaying(@Nullable android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice, int);
+    field @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED";
   }
 
   public final class BluetoothAdapter {
@@ -1647,9 +1649,9 @@
   }
 
   public class BluetoothPbap implements android.bluetooth.BluetoothProfile {
-    method public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
-    field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+    field @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
   }
 
   public interface BluetoothProfile {
@@ -1922,18 +1924,22 @@
   public abstract class IntegrityFormula {
     method @NonNull public static android.content.integrity.IntegrityFormula all(@NonNull android.content.integrity.IntegrityFormula...);
     method @NonNull public static android.content.integrity.IntegrityFormula any(@NonNull android.content.integrity.IntegrityFormula...);
-    method @NonNull public android.content.integrity.IntegrityFormula equalTo(@NonNull String);
-    method @NonNull public android.content.integrity.IntegrityFormula equalTo(boolean);
-    method @NonNull public android.content.integrity.IntegrityFormula equalTo(long);
-    method @NonNull public android.content.integrity.IntegrityFormula greaterThan(long);
-    method @NonNull public android.content.integrity.IntegrityFormula greaterThanOrEquals(long);
     method @NonNull public static android.content.integrity.IntegrityFormula not(@NonNull android.content.integrity.IntegrityFormula);
-    field @NonNull public static final android.content.integrity.IntegrityFormula APP_CERTIFICATE;
-    field @NonNull public static final android.content.integrity.IntegrityFormula INSTALLER_CERTIFICATE;
-    field @NonNull public static final android.content.integrity.IntegrityFormula INSTALLER_NAME;
-    field @NonNull public static final android.content.integrity.IntegrityFormula PACKAGE_NAME;
-    field @NonNull public static final android.content.integrity.IntegrityFormula PRE_INSTALLED;
-    field @NonNull public static final android.content.integrity.IntegrityFormula VERSION_CODE;
+  }
+
+  public static final class IntegrityFormula.Application {
+    method @NonNull public static android.content.integrity.IntegrityFormula certificatesContain(@NonNull String);
+    method @NonNull public static android.content.integrity.IntegrityFormula isPreInstalled();
+    method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String);
+    method @NonNull public static android.content.integrity.IntegrityFormula versionCodeEquals(@NonNull long);
+    method @NonNull public static android.content.integrity.IntegrityFormula versionCodeGreaterThan(@NonNull long);
+    method @NonNull public static android.content.integrity.IntegrityFormula versionCodeGreaterThanOrEqualTo(@NonNull long);
+  }
+
+  public static final class IntegrityFormula.Installer {
+    method @NonNull public static android.content.integrity.IntegrityFormula certificatesContain(@NonNull String);
+    method @NonNull public static android.content.integrity.IntegrityFormula notAllowedByManifest();
+    method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String);
   }
 
   public final class Rule implements android.os.Parcelable {
@@ -2007,18 +2013,15 @@
   }
 
   public final class InstallationFile implements android.os.Parcelable {
-    ctor public InstallationFile(@NonNull String, long, @Nullable byte[]);
+    ctor public InstallationFile(int, @NonNull String, long, @Nullable byte[], @Nullable byte[]);
     method public int describeContents();
-    method public int getFileType();
+    method public long getLengthBytes();
+    method public int getLocation();
     method @Nullable public byte[] getMetadata();
     method @NonNull public String getName();
-    method public long getSize();
+    method @Nullable public byte[] getSignature();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.InstallationFile> CREATOR;
-    field public static final int FILE_TYPE_APK = 0; // 0x0
-    field public static final int FILE_TYPE_LIB = 1; // 0x1
-    field public static final int FILE_TYPE_OBB = 2; // 0x2
-    field public static final int FILE_TYPE_UNKNOWN = -1; // 0xffffffff
   }
 
   public final class InstantAppInfo implements android.os.Parcelable {
@@ -2213,8 +2216,8 @@
     field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
     field public static final int FLAGS_PERMISSION_RESERVED_PERMISSIONCONTROLLER = -268435456; // 0xf0000000
     field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000
-    field public static final int FLAG_PERMISSION_DONT_AUTO_REVOKE = 131072; // 0x20000
-    field public static final int FLAG_PERMISSION_DONT_AUTO_REVOKE_USER_SET = 262144; // 0x40000
+    field public static final int FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED = 131072; // 0x20000
+    field public static final int FLAG_PERMISSION_AUTO_REVOKE_USER_SET = 262144; // 0x40000
     field public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 32; // 0x20
     field public static final int FLAG_PERMISSION_GRANTED_BY_ROLE = 32768; // 0x8000
     field public static final int FLAG_PERMISSION_ONE_TIME = 65536; // 0x10000
@@ -2298,7 +2301,7 @@
     method public void onPermissionsChanged(int);
   }
 
-  @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME, android.content.pm.PackageManager.FLAG_PERMISSION_DONT_AUTO_REVOKE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags {
+  @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKE_USER_SET}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags {
   }
 
   public class PermissionGroupInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
@@ -3803,6 +3806,7 @@
 
   public final class GnssCapabilities {
     method public boolean hasGeofencing();
+    method public boolean hasGnssAntennaInfo();
     method public boolean hasLowPowerMode();
     method public boolean hasMeasurementCorrections();
     method public boolean hasMeasurementCorrectionsExcessPathLength();
@@ -3905,236 +3909,236 @@
     method @NonNull public android.location.GnssSingleSatCorrection.Builder setSatelliteId(@IntRange(from=0) int);
   }
 
-  public class GpsClock implements android.os.Parcelable {
-    method public int describeContents();
-    method public double getBiasInNs();
-    method public double getBiasUncertaintyInNs();
-    method public double getDriftInNsPerSec();
-    method public double getDriftUncertaintyInNsPerSec();
-    method public long getFullBiasInNs();
-    method public short getLeapSecond();
-    method public long getTimeInNs();
-    method public double getTimeUncertaintyInNs();
-    method public byte getType();
-    method public boolean hasBiasInNs();
-    method public boolean hasBiasUncertaintyInNs();
-    method public boolean hasDriftInNsPerSec();
-    method public boolean hasDriftUncertaintyInNsPerSec();
-    method public boolean hasFullBiasInNs();
-    method public boolean hasLeapSecond();
-    method public boolean hasTimeUncertaintyInNs();
-    method public void reset();
-    method public void resetBiasInNs();
-    method public void resetBiasUncertaintyInNs();
-    method public void resetDriftInNsPerSec();
-    method public void resetDriftUncertaintyInNsPerSec();
-    method public void resetFullBiasInNs();
-    method public void resetLeapSecond();
-    method public void resetTimeUncertaintyInNs();
-    method public void set(android.location.GpsClock);
-    method public void setBiasInNs(double);
-    method public void setBiasUncertaintyInNs(double);
-    method public void setDriftInNsPerSec(double);
-    method public void setDriftUncertaintyInNsPerSec(double);
-    method public void setFullBiasInNs(long);
-    method public void setLeapSecond(short);
-    method public void setTimeInNs(long);
-    method public void setTimeUncertaintyInNs(double);
-    method public void setType(byte);
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.location.GpsClock> CREATOR;
-    field public static final byte TYPE_GPS_TIME = 2; // 0x2
-    field public static final byte TYPE_LOCAL_HW_TIME = 1; // 0x1
-    field public static final byte TYPE_UNKNOWN = 0; // 0x0
+  @Deprecated public class GpsClock implements android.os.Parcelable {
+    method @Deprecated public int describeContents();
+    method @Deprecated public double getBiasInNs();
+    method @Deprecated public double getBiasUncertaintyInNs();
+    method @Deprecated public double getDriftInNsPerSec();
+    method @Deprecated public double getDriftUncertaintyInNsPerSec();
+    method @Deprecated public long getFullBiasInNs();
+    method @Deprecated public short getLeapSecond();
+    method @Deprecated public long getTimeInNs();
+    method @Deprecated public double getTimeUncertaintyInNs();
+    method @Deprecated public byte getType();
+    method @Deprecated public boolean hasBiasInNs();
+    method @Deprecated public boolean hasBiasUncertaintyInNs();
+    method @Deprecated public boolean hasDriftInNsPerSec();
+    method @Deprecated public boolean hasDriftUncertaintyInNsPerSec();
+    method @Deprecated public boolean hasFullBiasInNs();
+    method @Deprecated public boolean hasLeapSecond();
+    method @Deprecated public boolean hasTimeUncertaintyInNs();
+    method @Deprecated public void reset();
+    method @Deprecated public void resetBiasInNs();
+    method @Deprecated public void resetBiasUncertaintyInNs();
+    method @Deprecated public void resetDriftInNsPerSec();
+    method @Deprecated public void resetDriftUncertaintyInNsPerSec();
+    method @Deprecated public void resetFullBiasInNs();
+    method @Deprecated public void resetLeapSecond();
+    method @Deprecated public void resetTimeUncertaintyInNs();
+    method @Deprecated public void set(android.location.GpsClock);
+    method @Deprecated public void setBiasInNs(double);
+    method @Deprecated public void setBiasUncertaintyInNs(double);
+    method @Deprecated public void setDriftInNsPerSec(double);
+    method @Deprecated public void setDriftUncertaintyInNsPerSec(double);
+    method @Deprecated public void setFullBiasInNs(long);
+    method @Deprecated public void setLeapSecond(short);
+    method @Deprecated public void setTimeInNs(long);
+    method @Deprecated public void setTimeUncertaintyInNs(double);
+    method @Deprecated public void setType(byte);
+    method @Deprecated public void writeToParcel(android.os.Parcel, int);
+    field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.location.GpsClock> CREATOR;
+    field @Deprecated public static final byte TYPE_GPS_TIME = 2; // 0x2
+    field @Deprecated public static final byte TYPE_LOCAL_HW_TIME = 1; // 0x1
+    field @Deprecated public static final byte TYPE_UNKNOWN = 0; // 0x0
   }
 
-  public class GpsMeasurement implements android.os.Parcelable {
-    method public int describeContents();
-    method public double getAccumulatedDeltaRangeInMeters();
-    method public short getAccumulatedDeltaRangeState();
-    method public double getAccumulatedDeltaRangeUncertaintyInMeters();
-    method public double getAzimuthInDeg();
-    method public double getAzimuthUncertaintyInDeg();
-    method public int getBitNumber();
-    method public long getCarrierCycles();
-    method public float getCarrierFrequencyInHz();
-    method public double getCarrierPhase();
-    method public double getCarrierPhaseUncertainty();
-    method public double getCn0InDbHz();
-    method public double getCodePhaseInChips();
-    method public double getCodePhaseUncertaintyInChips();
-    method public double getDopplerShiftInHz();
-    method public double getDopplerShiftUncertaintyInHz();
-    method public double getElevationInDeg();
-    method public double getElevationUncertaintyInDeg();
-    method public byte getLossOfLock();
-    method public byte getMultipathIndicator();
-    method public byte getPrn();
-    method public double getPseudorangeInMeters();
-    method public double getPseudorangeRateInMetersPerSec();
-    method public double getPseudorangeRateUncertaintyInMetersPerSec();
-    method public double getPseudorangeUncertaintyInMeters();
-    method public long getReceivedGpsTowInNs();
-    method public long getReceivedGpsTowUncertaintyInNs();
-    method public double getSnrInDb();
-    method public short getState();
-    method public short getTimeFromLastBitInMs();
-    method public double getTimeOffsetInNs();
-    method public boolean hasAzimuthInDeg();
-    method public boolean hasAzimuthUncertaintyInDeg();
-    method public boolean hasBitNumber();
-    method public boolean hasCarrierCycles();
-    method public boolean hasCarrierFrequencyInHz();
-    method public boolean hasCarrierPhase();
-    method public boolean hasCarrierPhaseUncertainty();
-    method public boolean hasCodePhaseInChips();
-    method public boolean hasCodePhaseUncertaintyInChips();
-    method public boolean hasDopplerShiftInHz();
-    method public boolean hasDopplerShiftUncertaintyInHz();
-    method public boolean hasElevationInDeg();
-    method public boolean hasElevationUncertaintyInDeg();
-    method public boolean hasPseudorangeInMeters();
-    method public boolean hasPseudorangeUncertaintyInMeters();
-    method public boolean hasSnrInDb();
-    method public boolean hasTimeFromLastBitInMs();
-    method public boolean isPseudorangeRateCorrected();
-    method public boolean isUsedInFix();
-    method public void reset();
-    method public void resetAzimuthInDeg();
-    method public void resetAzimuthUncertaintyInDeg();
-    method public void resetBitNumber();
-    method public void resetCarrierCycles();
-    method public void resetCarrierFrequencyInHz();
-    method public void resetCarrierPhase();
-    method public void resetCarrierPhaseUncertainty();
-    method public void resetCodePhaseInChips();
-    method public void resetCodePhaseUncertaintyInChips();
-    method public void resetDopplerShiftInHz();
-    method public void resetDopplerShiftUncertaintyInHz();
-    method public void resetElevationInDeg();
-    method public void resetElevationUncertaintyInDeg();
-    method public void resetPseudorangeInMeters();
-    method public void resetPseudorangeUncertaintyInMeters();
-    method public void resetSnrInDb();
-    method public void resetTimeFromLastBitInMs();
-    method public void set(android.location.GpsMeasurement);
-    method public void setAccumulatedDeltaRangeInMeters(double);
-    method public void setAccumulatedDeltaRangeState(short);
-    method public void setAccumulatedDeltaRangeUncertaintyInMeters(double);
-    method public void setAzimuthInDeg(double);
-    method public void setAzimuthUncertaintyInDeg(double);
-    method public void setBitNumber(int);
-    method public void setCarrierCycles(long);
-    method public void setCarrierFrequencyInHz(float);
-    method public void setCarrierPhase(double);
-    method public void setCarrierPhaseUncertainty(double);
-    method public void setCn0InDbHz(double);
-    method public void setCodePhaseInChips(double);
-    method public void setCodePhaseUncertaintyInChips(double);
-    method public void setDopplerShiftInHz(double);
-    method public void setDopplerShiftUncertaintyInHz(double);
-    method public void setElevationInDeg(double);
-    method public void setElevationUncertaintyInDeg(double);
-    method public void setLossOfLock(byte);
-    method public void setMultipathIndicator(byte);
-    method public void setPrn(byte);
-    method public void setPseudorangeInMeters(double);
-    method public void setPseudorangeRateInMetersPerSec(double);
-    method public void setPseudorangeRateUncertaintyInMetersPerSec(double);
-    method public void setPseudorangeUncertaintyInMeters(double);
-    method public void setReceivedGpsTowInNs(long);
-    method public void setReceivedGpsTowUncertaintyInNs(long);
-    method public void setSnrInDb(double);
-    method public void setState(short);
-    method public void setTimeFromLastBitInMs(short);
-    method public void setTimeOffsetInNs(double);
-    method public void setUsedInFix(boolean);
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final short ADR_STATE_CYCLE_SLIP = 4; // 0x4
-    field public static final short ADR_STATE_RESET = 2; // 0x2
-    field public static final short ADR_STATE_UNKNOWN = 0; // 0x0
-    field public static final short ADR_STATE_VALID = 1; // 0x1
-    field @NonNull public static final android.os.Parcelable.Creator<android.location.GpsMeasurement> CREATOR;
-    field public static final byte LOSS_OF_LOCK_CYCLE_SLIP = 2; // 0x2
-    field public static final byte LOSS_OF_LOCK_OK = 1; // 0x1
-    field public static final byte LOSS_OF_LOCK_UNKNOWN = 0; // 0x0
-    field public static final byte MULTIPATH_INDICATOR_DETECTED = 1; // 0x1
-    field public static final byte MULTIPATH_INDICATOR_NOT_USED = 2; // 0x2
-    field public static final byte MULTIPATH_INDICATOR_UNKNOWN = 0; // 0x0
-    field public static final short STATE_BIT_SYNC = 2; // 0x2
-    field public static final short STATE_CODE_LOCK = 1; // 0x1
-    field public static final short STATE_MSEC_AMBIGUOUS = 16; // 0x10
-    field public static final short STATE_SUBFRAME_SYNC = 4; // 0x4
-    field public static final short STATE_TOW_DECODED = 8; // 0x8
-    field public static final short STATE_UNKNOWN = 0; // 0x0
+  @Deprecated public class GpsMeasurement implements android.os.Parcelable {
+    method @Deprecated public int describeContents();
+    method @Deprecated public double getAccumulatedDeltaRangeInMeters();
+    method @Deprecated public short getAccumulatedDeltaRangeState();
+    method @Deprecated public double getAccumulatedDeltaRangeUncertaintyInMeters();
+    method @Deprecated public double getAzimuthInDeg();
+    method @Deprecated public double getAzimuthUncertaintyInDeg();
+    method @Deprecated public int getBitNumber();
+    method @Deprecated public long getCarrierCycles();
+    method @Deprecated public float getCarrierFrequencyInHz();
+    method @Deprecated public double getCarrierPhase();
+    method @Deprecated public double getCarrierPhaseUncertainty();
+    method @Deprecated public double getCn0InDbHz();
+    method @Deprecated public double getCodePhaseInChips();
+    method @Deprecated public double getCodePhaseUncertaintyInChips();
+    method @Deprecated public double getDopplerShiftInHz();
+    method @Deprecated public double getDopplerShiftUncertaintyInHz();
+    method @Deprecated public double getElevationInDeg();
+    method @Deprecated public double getElevationUncertaintyInDeg();
+    method @Deprecated public byte getLossOfLock();
+    method @Deprecated public byte getMultipathIndicator();
+    method @Deprecated public byte getPrn();
+    method @Deprecated public double getPseudorangeInMeters();
+    method @Deprecated public double getPseudorangeRateInMetersPerSec();
+    method @Deprecated public double getPseudorangeRateUncertaintyInMetersPerSec();
+    method @Deprecated public double getPseudorangeUncertaintyInMeters();
+    method @Deprecated public long getReceivedGpsTowInNs();
+    method @Deprecated public long getReceivedGpsTowUncertaintyInNs();
+    method @Deprecated public double getSnrInDb();
+    method @Deprecated public short getState();
+    method @Deprecated public short getTimeFromLastBitInMs();
+    method @Deprecated public double getTimeOffsetInNs();
+    method @Deprecated public boolean hasAzimuthInDeg();
+    method @Deprecated public boolean hasAzimuthUncertaintyInDeg();
+    method @Deprecated public boolean hasBitNumber();
+    method @Deprecated public boolean hasCarrierCycles();
+    method @Deprecated public boolean hasCarrierFrequencyInHz();
+    method @Deprecated public boolean hasCarrierPhase();
+    method @Deprecated public boolean hasCarrierPhaseUncertainty();
+    method @Deprecated public boolean hasCodePhaseInChips();
+    method @Deprecated public boolean hasCodePhaseUncertaintyInChips();
+    method @Deprecated public boolean hasDopplerShiftInHz();
+    method @Deprecated public boolean hasDopplerShiftUncertaintyInHz();
+    method @Deprecated public boolean hasElevationInDeg();
+    method @Deprecated public boolean hasElevationUncertaintyInDeg();
+    method @Deprecated public boolean hasPseudorangeInMeters();
+    method @Deprecated public boolean hasPseudorangeUncertaintyInMeters();
+    method @Deprecated public boolean hasSnrInDb();
+    method @Deprecated public boolean hasTimeFromLastBitInMs();
+    method @Deprecated public boolean isPseudorangeRateCorrected();
+    method @Deprecated public boolean isUsedInFix();
+    method @Deprecated public void reset();
+    method @Deprecated public void resetAzimuthInDeg();
+    method @Deprecated public void resetAzimuthUncertaintyInDeg();
+    method @Deprecated public void resetBitNumber();
+    method @Deprecated public void resetCarrierCycles();
+    method @Deprecated public void resetCarrierFrequencyInHz();
+    method @Deprecated public void resetCarrierPhase();
+    method @Deprecated public void resetCarrierPhaseUncertainty();
+    method @Deprecated public void resetCodePhaseInChips();
+    method @Deprecated public void resetCodePhaseUncertaintyInChips();
+    method @Deprecated public void resetDopplerShiftInHz();
+    method @Deprecated public void resetDopplerShiftUncertaintyInHz();
+    method @Deprecated public void resetElevationInDeg();
+    method @Deprecated public void resetElevationUncertaintyInDeg();
+    method @Deprecated public void resetPseudorangeInMeters();
+    method @Deprecated public void resetPseudorangeUncertaintyInMeters();
+    method @Deprecated public void resetSnrInDb();
+    method @Deprecated public void resetTimeFromLastBitInMs();
+    method @Deprecated public void set(android.location.GpsMeasurement);
+    method @Deprecated public void setAccumulatedDeltaRangeInMeters(double);
+    method @Deprecated public void setAccumulatedDeltaRangeState(short);
+    method @Deprecated public void setAccumulatedDeltaRangeUncertaintyInMeters(double);
+    method @Deprecated public void setAzimuthInDeg(double);
+    method @Deprecated public void setAzimuthUncertaintyInDeg(double);
+    method @Deprecated public void setBitNumber(int);
+    method @Deprecated public void setCarrierCycles(long);
+    method @Deprecated public void setCarrierFrequencyInHz(float);
+    method @Deprecated public void setCarrierPhase(double);
+    method @Deprecated public void setCarrierPhaseUncertainty(double);
+    method @Deprecated public void setCn0InDbHz(double);
+    method @Deprecated public void setCodePhaseInChips(double);
+    method @Deprecated public void setCodePhaseUncertaintyInChips(double);
+    method @Deprecated public void setDopplerShiftInHz(double);
+    method @Deprecated public void setDopplerShiftUncertaintyInHz(double);
+    method @Deprecated public void setElevationInDeg(double);
+    method @Deprecated public void setElevationUncertaintyInDeg(double);
+    method @Deprecated public void setLossOfLock(byte);
+    method @Deprecated public void setMultipathIndicator(byte);
+    method @Deprecated public void setPrn(byte);
+    method @Deprecated public void setPseudorangeInMeters(double);
+    method @Deprecated public void setPseudorangeRateInMetersPerSec(double);
+    method @Deprecated public void setPseudorangeRateUncertaintyInMetersPerSec(double);
+    method @Deprecated public void setPseudorangeUncertaintyInMeters(double);
+    method @Deprecated public void setReceivedGpsTowInNs(long);
+    method @Deprecated public void setReceivedGpsTowUncertaintyInNs(long);
+    method @Deprecated public void setSnrInDb(double);
+    method @Deprecated public void setState(short);
+    method @Deprecated public void setTimeFromLastBitInMs(short);
+    method @Deprecated public void setTimeOffsetInNs(double);
+    method @Deprecated public void setUsedInFix(boolean);
+    method @Deprecated public void writeToParcel(android.os.Parcel, int);
+    field @Deprecated public static final short ADR_STATE_CYCLE_SLIP = 4; // 0x4
+    field @Deprecated public static final short ADR_STATE_RESET = 2; // 0x2
+    field @Deprecated public static final short ADR_STATE_UNKNOWN = 0; // 0x0
+    field @Deprecated public static final short ADR_STATE_VALID = 1; // 0x1
+    field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.location.GpsMeasurement> CREATOR;
+    field @Deprecated public static final byte LOSS_OF_LOCK_CYCLE_SLIP = 2; // 0x2
+    field @Deprecated public static final byte LOSS_OF_LOCK_OK = 1; // 0x1
+    field @Deprecated public static final byte LOSS_OF_LOCK_UNKNOWN = 0; // 0x0
+    field @Deprecated public static final byte MULTIPATH_INDICATOR_DETECTED = 1; // 0x1
+    field @Deprecated public static final byte MULTIPATH_INDICATOR_NOT_USED = 2; // 0x2
+    field @Deprecated public static final byte MULTIPATH_INDICATOR_UNKNOWN = 0; // 0x0
+    field @Deprecated public static final short STATE_BIT_SYNC = 2; // 0x2
+    field @Deprecated public static final short STATE_CODE_LOCK = 1; // 0x1
+    field @Deprecated public static final short STATE_MSEC_AMBIGUOUS = 16; // 0x10
+    field @Deprecated public static final short STATE_SUBFRAME_SYNC = 4; // 0x4
+    field @Deprecated public static final short STATE_TOW_DECODED = 8; // 0x8
+    field @Deprecated public static final short STATE_UNKNOWN = 0; // 0x0
   }
 
-  public class GpsMeasurementsEvent implements android.os.Parcelable {
-    ctor public GpsMeasurementsEvent(android.location.GpsClock, android.location.GpsMeasurement[]);
-    method public int describeContents();
-    method @NonNull public android.location.GpsClock getClock();
-    method @NonNull public java.util.Collection<android.location.GpsMeasurement> getMeasurements();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.location.GpsMeasurementsEvent> CREATOR;
-    field public static final int STATUS_GPS_LOCATION_DISABLED = 2; // 0x2
-    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
-    field public static final int STATUS_READY = 1; // 0x1
+  @Deprecated public class GpsMeasurementsEvent implements android.os.Parcelable {
+    ctor @Deprecated public GpsMeasurementsEvent(android.location.GpsClock, android.location.GpsMeasurement[]);
+    method @Deprecated public int describeContents();
+    method @Deprecated @NonNull public android.location.GpsClock getClock();
+    method @Deprecated @NonNull public java.util.Collection<android.location.GpsMeasurement> getMeasurements();
+    method @Deprecated public void writeToParcel(android.os.Parcel, int);
+    field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.location.GpsMeasurementsEvent> CREATOR;
+    field @Deprecated public static final int STATUS_GPS_LOCATION_DISABLED = 2; // 0x2
+    field @Deprecated public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
+    field @Deprecated public static final int STATUS_READY = 1; // 0x1
   }
 
-  public static interface GpsMeasurementsEvent.Listener {
-    method public void onGpsMeasurementsReceived(android.location.GpsMeasurementsEvent);
-    method public void onStatusChanged(int);
+  @Deprecated public static interface GpsMeasurementsEvent.Listener {
+    method @Deprecated public void onGpsMeasurementsReceived(android.location.GpsMeasurementsEvent);
+    method @Deprecated public void onStatusChanged(int);
   }
 
-  public class GpsNavigationMessage implements android.os.Parcelable {
-    method public int describeContents();
-    method @NonNull public byte[] getData();
-    method public short getMessageId();
-    method public byte getPrn();
-    method public short getStatus();
-    method public short getSubmessageId();
-    method public byte getType();
-    method public void reset();
-    method public void set(android.location.GpsNavigationMessage);
-    method public void setData(byte[]);
-    method public void setMessageId(short);
-    method public void setPrn(byte);
-    method public void setStatus(short);
-    method public void setSubmessageId(short);
-    method public void setType(byte);
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.location.GpsNavigationMessage> CREATOR;
-    field public static final short STATUS_PARITY_PASSED = 1; // 0x1
-    field public static final short STATUS_PARITY_REBUILT = 2; // 0x2
-    field public static final short STATUS_UNKNOWN = 0; // 0x0
-    field public static final byte TYPE_CNAV2 = 4; // 0x4
-    field public static final byte TYPE_L1CA = 1; // 0x1
-    field public static final byte TYPE_L2CNAV = 2; // 0x2
-    field public static final byte TYPE_L5CNAV = 3; // 0x3
-    field public static final byte TYPE_UNKNOWN = 0; // 0x0
+  @Deprecated public class GpsNavigationMessage implements android.os.Parcelable {
+    method @Deprecated public int describeContents();
+    method @Deprecated @NonNull public byte[] getData();
+    method @Deprecated public short getMessageId();
+    method @Deprecated public byte getPrn();
+    method @Deprecated public short getStatus();
+    method @Deprecated public short getSubmessageId();
+    method @Deprecated public byte getType();
+    method @Deprecated public void reset();
+    method @Deprecated public void set(android.location.GpsNavigationMessage);
+    method @Deprecated public void setData(byte[]);
+    method @Deprecated public void setMessageId(short);
+    method @Deprecated public void setPrn(byte);
+    method @Deprecated public void setStatus(short);
+    method @Deprecated public void setSubmessageId(short);
+    method @Deprecated public void setType(byte);
+    method @Deprecated public void writeToParcel(android.os.Parcel, int);
+    field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.location.GpsNavigationMessage> CREATOR;
+    field @Deprecated public static final short STATUS_PARITY_PASSED = 1; // 0x1
+    field @Deprecated public static final short STATUS_PARITY_REBUILT = 2; // 0x2
+    field @Deprecated public static final short STATUS_UNKNOWN = 0; // 0x0
+    field @Deprecated public static final byte TYPE_CNAV2 = 4; // 0x4
+    field @Deprecated public static final byte TYPE_L1CA = 1; // 0x1
+    field @Deprecated public static final byte TYPE_L2CNAV = 2; // 0x2
+    field @Deprecated public static final byte TYPE_L5CNAV = 3; // 0x3
+    field @Deprecated public static final byte TYPE_UNKNOWN = 0; // 0x0
   }
 
-  public class GpsNavigationMessageEvent implements android.os.Parcelable {
-    ctor public GpsNavigationMessageEvent(android.location.GpsNavigationMessage);
-    method public int describeContents();
-    method @NonNull public android.location.GpsNavigationMessage getNavigationMessage();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.location.GpsNavigationMessageEvent> CREATOR;
-    field public static int STATUS_GPS_LOCATION_DISABLED;
-    field public static int STATUS_NOT_SUPPORTED;
-    field public static int STATUS_READY;
+  @Deprecated public class GpsNavigationMessageEvent implements android.os.Parcelable {
+    ctor @Deprecated public GpsNavigationMessageEvent(android.location.GpsNavigationMessage);
+    method @Deprecated public int describeContents();
+    method @Deprecated @NonNull public android.location.GpsNavigationMessage getNavigationMessage();
+    method @Deprecated public void writeToParcel(android.os.Parcel, int);
+    field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.location.GpsNavigationMessageEvent> CREATOR;
+    field @Deprecated public static int STATUS_GPS_LOCATION_DISABLED;
+    field @Deprecated public static int STATUS_NOT_SUPPORTED;
+    field @Deprecated public static int STATUS_READY;
   }
 
-  public static interface GpsNavigationMessageEvent.Listener {
-    method public void onGpsNavigationMessageReceived(android.location.GpsNavigationMessageEvent);
-    method public void onStatusChanged(int);
+  @Deprecated public static interface GpsNavigationMessageEvent.Listener {
+    method @Deprecated public void onGpsNavigationMessageReceived(android.location.GpsNavigationMessageEvent);
+    method @Deprecated public void onStatusChanged(int);
   }
 
   public class Location implements android.os.Parcelable {
     method public boolean isComplete();
     method public void makeComplete();
     method public void setIsFromMockProvider(boolean);
-    field public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
+    field @Deprecated public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
   }
 
   public class LocationManager {
@@ -4267,6 +4271,7 @@
 
   public class AudioManager {
     method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException;
     method public void clearAudioServerStateCallback();
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
     method @IntRange(from=0) public int getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
@@ -4283,6 +4288,7 @@
     method public boolean isHdmiSystemAudioSupported();
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
     method public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean removePreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException;
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy) throws java.lang.IllegalArgumentException;
@@ -4309,6 +4315,10 @@
     method public void onAudioServerUp();
   }
 
+  public static interface AudioManager.OnPreferredDeviceForStrategyChangedListener {
+    method public void onPreferredDeviceForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @Nullable android.media.AudioDevice);
+  }
+
   public abstract static class AudioManager.VolumeGroupCallback {
     ctor public AudioManager.VolumeGroupCallback();
     method public void onAudioVolumeGroupChanged(int, int);
@@ -4854,6 +4864,8 @@
 
   public class Tuner implements java.lang.AutoCloseable {
     ctor @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public Tuner(@NonNull android.content.Context, @NonNull String, int, @Nullable android.media.tv.tuner.Tuner.OnResourceLostListener);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int cancelScanning();
+    method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int cancelTuning();
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void clearOnTuneEventListener();
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void close();
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int connectCiCam(int);
@@ -4874,8 +4886,6 @@
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int setLna(boolean);
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void setOnTuneEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.OnTuneEventListener);
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
-    method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int stopScan();
-    method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int stopTune();
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings);
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void updateResourcePriority(int, int);
   }
@@ -5279,9 +5289,8 @@
     method public int getType();
   }
 
-  public static class TsFilterConfiguration.Builder {
+  public static class TsFilterConfiguration.Builder extends android.media.tv.tuner.filter.FilterConfiguration.Builder<android.media.tv.tuner.filter.TsFilterConfiguration.Builder> {
     method @NonNull public android.media.tv.tuner.filter.TsFilterConfiguration build();
-    method @NonNull public android.media.tv.tuner.filter.TsFilterConfiguration.Builder setSettings(@NonNull android.media.tv.tuner.filter.Settings);
     method @NonNull public android.media.tv.tuner.filter.TsFilterConfiguration.Builder setTpid(int);
   }
 
@@ -7717,6 +7726,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public byte[] retrieveBackupData();
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public byte[] retrieveSoftApBackupData();
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
+    method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setAutoWakeupEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE) public void setDeviceMobilityState(int);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMacRandomizationSettingPasspointEnabled(@NonNull String, boolean);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMeteredOverridePasspoint(@NonNull String, int);
@@ -8242,8 +8252,8 @@
   public final class NativeScanResult implements android.os.Parcelable {
     ctor public NativeScanResult();
     method public int describeContents();
-    method @NonNull public byte[] getBssid();
-    method @NonNull public int getCapabilities();
+    method @Nullable public android.net.MacAddress getBssid();
+    method public int getCapabilities();
     method public int getFrequencyMhz();
     method @NonNull public byte[] getInformationElements();
     method @NonNull public java.util.List<android.net.wifi.wificond.RadioChainInfo> getRadioChainInfos();
@@ -8252,15 +8262,31 @@
     method public long getTsf();
     method public boolean isAssociated();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int BSS_CAPABILITY_APSD = 2048; // 0x800
+    field public static final int BSS_CAPABILITY_CF_POLLABLE = 4; // 0x4
+    field public static final int BSS_CAPABILITY_CF_POLL_REQUEST = 8; // 0x8
+    field public static final int BSS_CAPABILITY_CHANNEL_AGILITY = 128; // 0x80
+    field public static final int BSS_CAPABILITY_DELAYED_BLOCK_ACK = 16384; // 0x4000
+    field public static final int BSS_CAPABILITY_DSSS_OFDM = 8192; // 0x2000
+    field public static final int BSS_CAPABILITY_ESS = 1; // 0x1
+    field public static final int BSS_CAPABILITY_IBSS = 2; // 0x2
+    field public static final int BSS_CAPABILITY_IMMEDIATE_BLOCK_ACK = 32768; // 0x8000
+    field public static final int BSS_CAPABILITY_PBCC = 64; // 0x40
+    field public static final int BSS_CAPABILITY_PRIVACY = 16; // 0x10
+    field public static final int BSS_CAPABILITY_QOS = 512; // 0x200
+    field public static final int BSS_CAPABILITY_RADIO_MANAGEMENT = 4096; // 0x1000
+    field public static final int BSS_CAPABILITY_SHORT_PREAMBLE = 32; // 0x20
+    field public static final int BSS_CAPABILITY_SHORT_SLOT_TIME = 1024; // 0x400
+    field public static final int BSS_CAPABILITY_SPECTRUM_MANAGEMENT = 256; // 0x100
     field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.wificond.NativeScanResult> CREATOR;
   }
 
   public final class NativeWifiClient implements android.os.Parcelable {
-    ctor public NativeWifiClient(@NonNull byte[]);
+    ctor public NativeWifiClient(@Nullable android.net.MacAddress);
     method public int describeContents();
+    method @Nullable public android.net.MacAddress getMacAddress();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.wificond.NativeWifiClient> CREATOR;
-    field @NonNull public final byte[] macAddress;
   }
 
   public final class PnoNetwork implements android.os.Parcelable {
@@ -8305,7 +8331,7 @@
   public class WifiCondManager {
     method public void abortScan(@NonNull String);
     method public void enableVerboseLogging(boolean);
-    method @NonNull public java.util.List<java.lang.Integer> getChannelsMhzForBand(int);
+    method @NonNull public int[] getChannelsMhzForBand(int);
     method @Nullable public android.net.wifi.wificond.DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String);
     method @NonNull public java.util.List<android.net.wifi.wificond.NativeScanResult> getScanResults(@NonNull String, int);
     method @Nullable public android.net.wifi.wificond.WifiCondManager.TxPacketCounters getTxPacketCounters(@NonNull String);
@@ -8874,26 +8900,6 @@
     field public static final int TUPLE_VALUE_TYPE = 7; // 0x7
   }
 
-  public class StatsFrameworkInitializer {
-    method public static void registerServiceWrappers();
-    method public static void setStatsServiceManager(@NonNull android.os.StatsServiceManager);
-  }
-
-  public class StatsServiceManager {
-    method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsCompanionServiceRegisterer();
-    method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsManagerServiceRegisterer();
-    method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsdServiceRegisterer();
-  }
-
-  public static class StatsServiceManager.ServiceNotFoundException extends java.lang.Exception {
-    ctor public StatsServiceManager.ServiceNotFoundException(@NonNull String);
-  }
-
-  public static final class StatsServiceManager.ServiceRegisterer {
-    method @Nullable public android.os.IBinder get();
-    method @Nullable public android.os.IBinder getOrThrow() throws android.os.StatsServiceManager.ServiceNotFoundException;
-  }
-
   public class SystemConfigManager {
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Set<java.lang.String> getDisabledUntilUsedPreinstalledCarrierApps();
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
@@ -9079,6 +9085,17 @@
   @IntDef(flag=true, prefix={"RESTRICTION_"}, value={android.os.UserManager.RESTRICTION_NOT_SET, android.os.UserManager.RESTRICTION_SOURCE_SYSTEM, android.os.UserManager.RESTRICTION_SOURCE_DEVICE_OWNER, android.os.UserManager.RESTRICTION_SOURCE_PROFILE_OWNER}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface UserManager.UserRestrictionSource {
   }
 
+  public abstract class Vibrator {
+    method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void addVibratorStateListener(@NonNull android.os.Vibrator.OnVibratorStateChangedListener);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void addVibratorStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.Vibrator.OnVibratorStateChangedListener);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public boolean isVibrating();
+    method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void removeVibratorStateListener(@NonNull android.os.Vibrator.OnVibratorStateChangedListener);
+  }
+
+  public static interface Vibrator.OnVibratorStateChangedListener {
+    method public void onVibratorStateChanged(boolean);
+  }
+
   public class WorkSource implements android.os.Parcelable {
     ctor public WorkSource(int);
     ctor public WorkSource(int, @NonNull String);
@@ -9680,7 +9697,6 @@
     field public static final String EUICC_UNSUPPORTED_COUNTRIES = "euicc_unsupported_countries";
     field public static final String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT = "install_carrier_app_notification_persistent";
     field public static final String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS = "install_carrier_app_notification_sleep_millis";
-    field public static final String NETWORK_RECOMMENDATIONS_ENABLED = "network_recommendations_enabled";
     field public static final String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
     field public static final String REQUIRE_PASSWORD_TO_DECRYPT = "require_password_to_decrypt";
     field public static final String SOFT_AP_TIMEOUT_ENABLED = "soft_ap_timeout_enabled";
@@ -9695,7 +9711,7 @@
     field public static final String WIFI_SCAN_THROTTLE_ENABLED = "wifi_scan_throttle_enabled";
     field public static final String WIFI_SCORE_PARAMS = "wifi_score_params";
     field public static final String WIFI_VERBOSE_LOGGING_ENABLED = "wifi_verbose_logging_enabled";
-    field public static final String WIFI_WAKEUP_ENABLED = "wifi_wakeup_enabled";
+    field @Deprecated public static final String WIFI_WAKEUP_ENABLED = "wifi_wakeup_enabled";
   }
 
   public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
@@ -10317,7 +10333,7 @@
 
   public abstract class DataLoaderService extends android.app.Service {
     ctor public DataLoaderService();
-    method @Nullable public android.service.dataloader.DataLoaderService.DataLoader onCreateDataLoader();
+    method @Nullable public android.service.dataloader.DataLoaderService.DataLoader onCreateDataLoader(@NonNull android.content.pm.DataLoaderParams);
   }
 
   public static interface DataLoaderService.DataLoader {
@@ -12610,20 +12626,6 @@
     method public static final void setSmsFilterSettings(android.content.Context, android.telecom.PhoneAccountHandle, android.telephony.VisualVoicemailSmsFilterSettings);
   }
 
-  public final class WapPushManagerConnector {
-    ctor public WapPushManagerConnector(@NonNull android.content.Context);
-    method public boolean bindToWapPushManagerService();
-    method @Nullable public String getConnectedWapPushManagerServicePackage();
-    method public int processMessage(@NonNull String, @NonNull String, @NonNull android.content.Intent);
-    method public void unbindWapPushManagerService();
-    field public static final int RESULT_APP_QUERY_FAILED = 2; // 0x2
-    field public static final int RESULT_EXCEPTION_CAUGHT = 16; // 0x10
-    field public static final int RESULT_FURTHER_PROCESSING = 32768; // 0x8000
-    field public static final int RESULT_INVALID_RECEIVER_NAME = 8; // 0x8
-    field public static final int RESULT_MESSAGE_HANDLED = 1; // 0x1
-    field public static final int RESULT_SIGNATURE_NO_MATCH = 4; // 0x4
-  }
-
 }
 
 package android.telephony.cdma {
@@ -13183,19 +13185,7 @@
   }
 
   public class ImsRcsManager implements android.telephony.ims.RegistrationManager {
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(int) throws android.telephony.ims.ImsException;
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(int, int) throws android.telephony.ims.ImsException;
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerRcsAvailabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsRcsManager.AvailabilityCallback) throws android.telephony.ims.ImsException;
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterRcsAvailabilityCallback(@NonNull android.telephony.ims.ImsRcsManager.AvailabilityCallback) throws android.telephony.ims.ImsException;
-  }
-
-  public static class ImsRcsManager.AvailabilityCallback {
-    ctor public ImsRcsManager.AvailabilityCallback();
-    method public void onAvailabilityChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities);
+    method @NonNull public android.telephony.ims.RcsUceAdapter getUceAdapter();
   }
 
   public final class ImsReasonInfo implements android.os.Parcelable {
@@ -13574,34 +13564,8 @@
   }
 
   public class RcsUceAdapter {
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getUcePublishState() throws android.telephony.ims.ImsException;
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException;
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void requestCapabilities(@NonNull java.util.concurrent.Executor, @NonNull java.util.List<android.net.Uri>, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
-    field public static final int ERROR_ALREADY_IN_QUEUE = 13; // 0xd
-    field public static final int ERROR_FORBIDDEN = 6; // 0x6
-    field public static final int ERROR_GENERIC_FAILURE = 1; // 0x1
-    field public static final int ERROR_INSUFFICIENT_MEMORY = 11; // 0xb
-    field public static final int ERROR_LOST_NETWORK = 12; // 0xc
-    field public static final int ERROR_NOT_AUTHORIZED = 5; // 0x5
-    field public static final int ERROR_NOT_AVAILABLE = 3; // 0x3
-    field public static final int ERROR_NOT_ENABLED = 2; // 0x2
-    field public static final int ERROR_NOT_FOUND = 7; // 0x7
-    field public static final int ERROR_NOT_REGISTERED = 4; // 0x4
-    field public static final int ERROR_REQUEST_TIMEOUT = 10; // 0xa
-    field public static final int ERROR_REQUEST_TOO_LARGE = 8; // 0x8
-    field public static final int PUBLISH_STATE_200_OK = 1; // 0x1
-    field public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; // 0x2
-    field public static final int PUBLISH_STATE_OTHER_ERROR = 6; // 0x6
-    field public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; // 0x4
-    field public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; // 0x5
-    field public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3; // 0x3
-  }
-
-  public static class RcsUceAdapter.CapabilitiesCallback {
-    ctor public RcsUceAdapter.CapabilitiesCallback();
-    method public void onCapabilitiesReceived(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>);
-    method public void onError(int);
   }
 
 }
@@ -13687,23 +13651,8 @@
   public class RcsFeature extends android.telephony.ims.feature.ImsFeature {
     ctor public RcsFeature();
     method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
-    method @NonNull public android.telephony.ims.stub.RcsSipOptionsImplBase getOptionsExchangeImpl();
-    method @NonNull public android.telephony.ims.stub.RcsPresenceExchangeImplBase getPresenceExchangeImpl();
-    method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities);
     method public void onFeatureReady();
     method public void onFeatureRemoved();
-    method public boolean queryCapabilityConfiguration(int, int);
-    method @NonNull public final android.telephony.ims.feature.RcsFeature.RcsImsCapabilities queryCapabilityStatus();
-  }
-
-  public static class RcsFeature.RcsImsCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities {
-    ctor public RcsFeature.RcsImsCapabilities(int);
-    method public void addCapabilities(int);
-    method public boolean isCapable(int);
-    method public void removeCapabilities(int);
-    field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0
-    field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1
-    field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
   }
 
 }
@@ -13881,71 +13830,6 @@
     field public static final int INVALID_RESULT = -1; // 0xffffffff
   }
 
-  public class RcsCapabilityExchange {
-    ctor public RcsCapabilityExchange();
-    method public final void onCommandUpdate(int, int) throws android.telephony.ims.ImsException;
-    field public static final int COMMAND_CODE_FETCH_ERROR = 4; // 0x4
-    field public static final int COMMAND_CODE_GENERIC_FAILURE = 2; // 0x2
-    field public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 6; // 0x6
-    field public static final int COMMAND_CODE_INVALID_PARAM = 3; // 0x3
-    field public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 7; // 0x7
-    field public static final int COMMAND_CODE_NOT_FOUND = 9; // 0x9
-    field public static final int COMMAND_CODE_NOT_SUPPORTED = 8; // 0x8
-    field public static final int COMMAND_CODE_NO_CHANGE_IN_CAP = 11; // 0xb
-    field public static final int COMMAND_CODE_REQUEST_TIMEOUT = 5; // 0x5
-    field public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 10; // 0xa
-    field public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0; // 0x0
-    field public static final int COMMAND_CODE_SUCCESS = 1; // 0x1
-  }
-
-  public class RcsPresenceExchangeImplBase extends android.telephony.ims.stub.RcsCapabilityExchange {
-    ctor public RcsPresenceExchangeImplBase();
-    method public final void onCapabilityRequestResponse(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>, int) throws android.telephony.ims.ImsException;
-    method public final void onNetworkResponse(int, @NonNull String, int) throws android.telephony.ims.ImsException;
-    method public final void onNotifyUpdateCapabilites(int) throws android.telephony.ims.ImsException;
-    method public final void onUnpublish() throws android.telephony.ims.ImsException;
-    method public void requestCapabilities(@NonNull java.util.List<android.net.Uri>, int);
-    method public void updateCapabilities(@NonNull android.telephony.ims.RcsContactUceCapability, int);
-    field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 0; // 0x0
-    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 6; // 0x6
-    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 5; // 0x5
-    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 3; // 0x3
-    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 4; // 0x4
-    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 8; // 0x8
-    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 1; // 0x1
-    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 2; // 0x2
-    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10; // 0xa
-    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; // 0xb
-    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 7; // 0x7
-    field public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 9; // 0x9
-    field public static final int RESPONSE_FORBIDDEN = 3; // 0x3
-    field public static final int RESPONSE_NOT_AUTHORIZED_FOR_PRESENCE = 2; // 0x2
-    field public static final int RESPONSE_NOT_FOUND = 4; // 0x4
-    field public static final int RESPONSE_NOT_REGISTERED = 1; // 0x1
-    field public static final int RESPONSE_SIP_INTERVAL_TOO_SHORT = 7; // 0x7
-    field public static final int RESPONSE_SIP_REQUEST_TIMEOUT = 5; // 0x5
-    field public static final int RESPONSE_SIP_SERVICE_UNAVAILABLE = 8; // 0x8
-    field public static final int RESPONSE_SUBSCRIBE_GENERIC_FAILURE = -1; // 0xffffffff
-    field public static final int RESPONSE_SUBSCRIBE_TOO_LARGE = 6; // 0x6
-    field public static final int RESPONSE_SUCCESS = 0; // 0x0
-  }
-
-  public class RcsSipOptionsImplBase extends android.telephony.ims.stub.RcsCapabilityExchange {
-    ctor public RcsSipOptionsImplBase();
-    method public final void onCapabilityRequestResponse(int, @NonNull String, @Nullable android.telephony.ims.RcsContactUceCapability, int) throws android.telephony.ims.ImsException;
-    method public final void onRemoteCapabilityRequest(@NonNull android.net.Uri, @NonNull android.telephony.ims.RcsContactUceCapability, int) throws android.telephony.ims.ImsException;
-    method public void respondToCapabilityRequest(@NonNull String, @NonNull android.telephony.ims.RcsContactUceCapability, int);
-    method public void respondToCapabilityRequestWithError(@NonNull android.net.Uri, int, @NonNull String, int);
-    method public void sendCapabilityRequest(@NonNull android.net.Uri, @NonNull android.telephony.ims.RcsContactUceCapability, int);
-    field public static final int RESPONSE_BAD_REQUEST = 5; // 0x5
-    field public static final int RESPONSE_DOES_NOT_EXIST_ANYWHERE = 4; // 0x4
-    field public static final int RESPONSE_GENERIC_FAILURE = -1; // 0xffffffff
-    field public static final int RESPONSE_NOT_FOUND = 3; // 0x3
-    field public static final int RESPONSE_REQUEST_TIMEOUT = 2; // 0x2
-    field public static final int RESPONSE_SUCCESS = 0; // 0x0
-    field public static final int RESPONSE_TEMPORARILY_UNAVAILABLE = 1; // 0x1
-  }
-
 }
 
 package android.telephony.mbms {
diff --git a/api/test-current.txt b/api/test-current.txt
index e352cb6..7e8eb0c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -812,18 +812,22 @@
   public abstract class IntegrityFormula {
     method @NonNull public static android.content.integrity.IntegrityFormula all(@NonNull android.content.integrity.IntegrityFormula...);
     method @NonNull public static android.content.integrity.IntegrityFormula any(@NonNull android.content.integrity.IntegrityFormula...);
-    method @NonNull public android.content.integrity.IntegrityFormula equalTo(@NonNull String);
-    method @NonNull public android.content.integrity.IntegrityFormula equalTo(boolean);
-    method @NonNull public android.content.integrity.IntegrityFormula equalTo(long);
-    method @NonNull public android.content.integrity.IntegrityFormula greaterThan(long);
-    method @NonNull public android.content.integrity.IntegrityFormula greaterThanOrEquals(long);
     method @NonNull public static android.content.integrity.IntegrityFormula not(@NonNull android.content.integrity.IntegrityFormula);
-    field @NonNull public static final android.content.integrity.IntegrityFormula APP_CERTIFICATE;
-    field @NonNull public static final android.content.integrity.IntegrityFormula INSTALLER_CERTIFICATE;
-    field @NonNull public static final android.content.integrity.IntegrityFormula INSTALLER_NAME;
-    field @NonNull public static final android.content.integrity.IntegrityFormula PACKAGE_NAME;
-    field @NonNull public static final android.content.integrity.IntegrityFormula PRE_INSTALLED;
-    field @NonNull public static final android.content.integrity.IntegrityFormula VERSION_CODE;
+  }
+
+  public static final class IntegrityFormula.Application {
+    method @NonNull public static android.content.integrity.IntegrityFormula certificatesContain(@NonNull String);
+    method @NonNull public static android.content.integrity.IntegrityFormula isPreInstalled();
+    method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String);
+    method @NonNull public static android.content.integrity.IntegrityFormula versionCodeEquals(@NonNull long);
+    method @NonNull public static android.content.integrity.IntegrityFormula versionCodeGreaterThan(@NonNull long);
+    method @NonNull public static android.content.integrity.IntegrityFormula versionCodeGreaterThanOrEqualTo(@NonNull long);
+  }
+
+  public static final class IntegrityFormula.Installer {
+    method @NonNull public static android.content.integrity.IntegrityFormula certificatesContain(@NonNull String);
+    method @NonNull public static android.content.integrity.IntegrityFormula notAllowedByManifest();
+    method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String);
   }
 
   public final class Rule implements android.os.Parcelable {
@@ -1334,7 +1338,7 @@
 
   public class Location implements android.os.Parcelable {
     method public void makeComplete();
-    field public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
+    field @Deprecated public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
   }
 
   public class LocationManager {
@@ -2605,6 +2609,17 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationEffect.Waveform> CREATOR;
   }
 
+  public abstract class Vibrator {
+    method @RequiresPermission("android.permission.ACCESS_VIBRATOR_STATE") public void addVibratorStateListener(@NonNull android.os.Vibrator.OnVibratorStateChangedListener);
+    method @RequiresPermission("android.permission.ACCESS_VIBRATOR_STATE") public void addVibratorStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.Vibrator.OnVibratorStateChangedListener);
+    method @RequiresPermission("android.permission.ACCESS_VIBRATOR_STATE") public boolean isVibrating();
+    method @RequiresPermission("android.permission.ACCESS_VIBRATOR_STATE") public void removeVibratorStateListener(@NonNull android.os.Vibrator.OnVibratorStateChangedListener);
+  }
+
+  public static interface Vibrator.OnVibratorStateChangedListener {
+    method public void onVibratorStateChanged(boolean);
+  }
+
   public class VintfObject {
     method public static String[] getHalNamesAndVersions();
     method public static String getSepolicyVersion();
@@ -3242,6 +3257,7 @@
     method public void onContentCaptureEvent(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.view.contentcapture.ContentCaptureEvent);
     method public void onCreateContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureContext, @NonNull android.view.contentcapture.ContentCaptureSessionId);
     method public void onDataRemovalRequest(@NonNull android.view.contentcapture.DataRemovalRequest);
+    method public void onDataShareRequest(@NonNull android.view.contentcapture.DataShareRequest, @NonNull android.service.contentcapture.DataShareCallback);
     method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId);
     method public void onDisconnected();
     method public final void setContentCaptureConditions(@NonNull String, @Nullable java.util.Set<android.view.contentcapture.ContentCaptureCondition>);
@@ -3250,6 +3266,16 @@
     field public static final String SERVICE_META_DATA = "android.content_capture";
   }
 
+  public interface DataShareCallback {
+    method public void onAccept(@NonNull java.util.concurrent.Executor, @NonNull android.service.contentcapture.DataShareReadAdapter);
+    method public void onReject();
+  }
+
+  public interface DataShareReadAdapter {
+    method public void onError(int);
+    method public void onStart(@NonNull android.os.ParcelFileDescriptor);
+  }
+
   public final class SnapshotData implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public android.app.assist.AssistContent getAssistContent();
@@ -3961,19 +3987,7 @@
   }
 
   public class ImsRcsManager implements android.telephony.ims.RegistrationManager {
-    method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
-    method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
-    method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isAvailable(int) throws android.telephony.ims.ImsException;
-    method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isCapable(int, int) throws android.telephony.ims.ImsException;
-    method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
-    method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerRcsAvailabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsRcsManager.AvailabilityCallback) throws android.telephony.ims.ImsException;
-    method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
-    method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterRcsAvailabilityCallback(@NonNull android.telephony.ims.ImsRcsManager.AvailabilityCallback) throws android.telephony.ims.ImsException;
-  }
-
-  public static class ImsRcsManager.AvailabilityCallback {
-    ctor public ImsRcsManager.AvailabilityCallback();
-    method public void onAvailabilityChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities);
+    method @NonNull public android.telephony.ims.RcsUceAdapter getUceAdapter();
   }
 
   public class ImsService extends android.app.Service {
@@ -4348,34 +4362,8 @@
   }
 
   public class RcsUceAdapter {
-    method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getUcePublishState() throws android.telephony.ims.ImsException;
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException;
-    method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void requestCapabilities(@NonNull java.util.concurrent.Executor, @NonNull java.util.List<android.net.Uri>, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
-    field public static final int ERROR_ALREADY_IN_QUEUE = 13; // 0xd
-    field public static final int ERROR_FORBIDDEN = 6; // 0x6
-    field public static final int ERROR_GENERIC_FAILURE = 1; // 0x1
-    field public static final int ERROR_INSUFFICIENT_MEMORY = 11; // 0xb
-    field public static final int ERROR_LOST_NETWORK = 12; // 0xc
-    field public static final int ERROR_NOT_AUTHORIZED = 5; // 0x5
-    field public static final int ERROR_NOT_AVAILABLE = 3; // 0x3
-    field public static final int ERROR_NOT_ENABLED = 2; // 0x2
-    field public static final int ERROR_NOT_FOUND = 7; // 0x7
-    field public static final int ERROR_NOT_REGISTERED = 4; // 0x4
-    field public static final int ERROR_REQUEST_TIMEOUT = 10; // 0xa
-    field public static final int ERROR_REQUEST_TOO_LARGE = 8; // 0x8
-    field public static final int PUBLISH_STATE_200_OK = 1; // 0x1
-    field public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; // 0x2
-    field public static final int PUBLISH_STATE_OTHER_ERROR = 6; // 0x6
-    field public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; // 0x4
-    field public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; // 0x5
-    field public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3; // 0x3
-  }
-
-  public static class RcsUceAdapter.CapabilitiesCallback {
-    ctor public RcsUceAdapter.CapabilitiesCallback();
-    method public void onCapabilitiesReceived(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>);
-    method public void onError(int);
   }
 
 }
@@ -4461,23 +4449,8 @@
   public class RcsFeature extends android.telephony.ims.feature.ImsFeature {
     ctor public RcsFeature();
     method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
-    method @NonNull public android.telephony.ims.stub.RcsSipOptionsImplBase getOptionsExchangeImpl();
-    method @NonNull public android.telephony.ims.stub.RcsPresenceExchangeImplBase getPresenceExchangeImpl();
-    method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities);
     method public void onFeatureReady();
     method public void onFeatureRemoved();
-    method public boolean queryCapabilityConfiguration(int, int);
-    method @NonNull public final android.telephony.ims.feature.RcsFeature.RcsImsCapabilities queryCapabilityStatus();
-  }
-
-  public static class RcsFeature.RcsImsCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities {
-    ctor public RcsFeature.RcsImsCapabilities(int);
-    method public void addCapabilities(int);
-    method public boolean isCapable(int);
-    method public void removeCapabilities(int);
-    field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0
-    field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1
-    field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
   }
 
 }
@@ -4655,71 +4628,6 @@
     field public static final int INVALID_RESULT = -1; // 0xffffffff
   }
 
-  public class RcsCapabilityExchange {
-    ctor public RcsCapabilityExchange();
-    method public final void onCommandUpdate(int, int) throws android.telephony.ims.ImsException;
-    field public static final int COMMAND_CODE_FETCH_ERROR = 4; // 0x4
-    field public static final int COMMAND_CODE_GENERIC_FAILURE = 2; // 0x2
-    field public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 6; // 0x6
-    field public static final int COMMAND_CODE_INVALID_PARAM = 3; // 0x3
-    field public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 7; // 0x7
-    field public static final int COMMAND_CODE_NOT_FOUND = 9; // 0x9
-    field public static final int COMMAND_CODE_NOT_SUPPORTED = 8; // 0x8
-    field public static final int COMMAND_CODE_NO_CHANGE_IN_CAP = 11; // 0xb
-    field public static final int COMMAND_CODE_REQUEST_TIMEOUT = 5; // 0x5
-    field public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 10; // 0xa
-    field public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0; // 0x0
-    field public static final int COMMAND_CODE_SUCCESS = 1; // 0x1
-  }
-
-  public class RcsPresenceExchangeImplBase extends android.telephony.ims.stub.RcsCapabilityExchange {
-    ctor public RcsPresenceExchangeImplBase();
-    method public final void onCapabilityRequestResponse(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>, int) throws android.telephony.ims.ImsException;
-    method public final void onNetworkResponse(int, @NonNull String, int) throws android.telephony.ims.ImsException;
-    method public final void onNotifyUpdateCapabilites(int) throws android.telephony.ims.ImsException;
-    method public final void onUnpublish() throws android.telephony.ims.ImsException;
-    method public void requestCapabilities(@NonNull java.util.List<android.net.Uri>, int);
-    method public void updateCapabilities(@NonNull android.telephony.ims.RcsContactUceCapability, int);
-    field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 0; // 0x0
-    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 6; // 0x6
-    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 5; // 0x5
-    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 3; // 0x3
-    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 4; // 0x4
-    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 8; // 0x8
-    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 1; // 0x1
-    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 2; // 0x2
-    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10; // 0xa
-    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; // 0xb
-    field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 7; // 0x7
-    field public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 9; // 0x9
-    field public static final int RESPONSE_FORBIDDEN = 3; // 0x3
-    field public static final int RESPONSE_NOT_AUTHORIZED_FOR_PRESENCE = 2; // 0x2
-    field public static final int RESPONSE_NOT_FOUND = 4; // 0x4
-    field public static final int RESPONSE_NOT_REGISTERED = 1; // 0x1
-    field public static final int RESPONSE_SIP_INTERVAL_TOO_SHORT = 7; // 0x7
-    field public static final int RESPONSE_SIP_REQUEST_TIMEOUT = 5; // 0x5
-    field public static final int RESPONSE_SIP_SERVICE_UNAVAILABLE = 8; // 0x8
-    field public static final int RESPONSE_SUBSCRIBE_GENERIC_FAILURE = -1; // 0xffffffff
-    field public static final int RESPONSE_SUBSCRIBE_TOO_LARGE = 6; // 0x6
-    field public static final int RESPONSE_SUCCESS = 0; // 0x0
-  }
-
-  public class RcsSipOptionsImplBase extends android.telephony.ims.stub.RcsCapabilityExchange {
-    ctor public RcsSipOptionsImplBase();
-    method public final void onCapabilityRequestResponse(int, @NonNull String, @Nullable android.telephony.ims.RcsContactUceCapability, int) throws android.telephony.ims.ImsException;
-    method public final void onRemoteCapabilityRequest(@NonNull android.net.Uri, @NonNull android.telephony.ims.RcsContactUceCapability, int) throws android.telephony.ims.ImsException;
-    method public void respondToCapabilityRequest(@NonNull String, @NonNull android.telephony.ims.RcsContactUceCapability, int);
-    method public void respondToCapabilityRequestWithError(@NonNull android.net.Uri, int, @NonNull String, int);
-    method public void sendCapabilityRequest(@NonNull android.net.Uri, @NonNull android.telephony.ims.RcsContactUceCapability, int);
-    field public static final int RESPONSE_BAD_REQUEST = 5; // 0x5
-    field public static final int RESPONSE_DOES_NOT_EXIST_ANYWHERE = 4; // 0x4
-    field public static final int RESPONSE_GENERIC_FAILURE = -1; // 0xffffffff
-    field public static final int RESPONSE_NOT_FOUND = 3; // 0x3
-    field public static final int RESPONSE_REQUEST_TIMEOUT = 2; // 0x2
-    field public static final int RESPONSE_SUCCESS = 0; // 0x0
-    field public static final int RESPONSE_TEMPORARILY_UNAVAILABLE = 1; // 0x1
-  }
-
 }
 
 package android.telephony.mbms {
diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp
index 3e5877b..befb67b 100644
--- a/cmds/bootanimation/Android.bp
+++ b/cmds/bootanimation/Android.bp
@@ -41,6 +41,10 @@
     ],
 
     init_rc: ["bootanim.rc"],
+
+    cflags: [
+        "-Wno-deprecated-declarations",
+    ],
 }
 
 // libbootanimation
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index c5cf980..c441709 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -85,11 +85,42 @@
   return Error("failed to obtain resource id for %s", res.c_str());
 }
 
-Result<std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId resid) {
+void PrintValue(AssetManager2* const am, const Res_value& value, const ApkAssetsCookie& cookie,
+                std::string* const out) {
+  switch (value.dataType) {
+    case Res_value::TYPE_INT_DEC:
+      out->append(StringPrintf("%d", value.data));
+      break;
+    case Res_value::TYPE_INT_HEX:
+      out->append(StringPrintf("0x%08x", value.data));
+      break;
+    case Res_value::TYPE_INT_BOOLEAN:
+      out->append(value.data != 0 ? "true" : "false");
+      break;
+    case Res_value::TYPE_STRING: {
+      const ResStringPool* pool = am->GetStringPoolForCookie(cookie);
+      out->append("\"");
+      size_t len;
+      if (pool->isUTF8()) {
+        const char* str = pool->string8At(value.data, &len);
+        out->append(str, len);
+      } else {
+        const char16_t* str16 = pool->stringAt(value.data, &len);
+        out->append(Utf16ToUtf8(StringPiece16(str16, len)));
+      }
+      out->append("\"");
+    } break;
+    default:
+      out->append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data));
+      break;
+  }
+}
+
+Result<std::string> WARN_UNUSED GetValue(AssetManager2* const am, ResourceId resid) {
   Res_value value;
   ResTable_config config;
   uint32_t flags;
-  ApkAssetsCookie cookie = am.GetResource(resid, false, 0, &value, &config, &flags);
+  ApkAssetsCookie cookie = am->GetResource(resid, true, 0, &value, &config, &flags);
   if (cookie == kInvalidCookie) {
     return Error("no resource 0x%08x in asset manager", resid);
   }
@@ -104,31 +135,37 @@
   out.append(config.toString().c_str());
   out.append("' value=");
 
-  switch (value.dataType) {
-    case Res_value::TYPE_INT_DEC:
-      out.append(StringPrintf("%d", value.data));
-      break;
-    case Res_value::TYPE_INT_HEX:
-      out.append(StringPrintf("0x%08x", value.data));
-      break;
-    case Res_value::TYPE_INT_BOOLEAN:
-      out.append(value.data != 0 ? "true" : "false");
-      break;
-    case Res_value::TYPE_STRING: {
-      const ResStringPool* pool = am.GetStringPoolForCookie(cookie);
-      size_t len;
-      if (pool->isUTF8()) {
-        const char* str = pool->string8At(value.data, &len);
-        out.append(str, len);
-      } else {
-        const char16_t* str16 = pool->stringAt(value.data, &len);
-        out += Utf16ToUtf8(StringPiece16(str16, len));
-      }
-    } break;
-    default:
+  if (value.dataType == Res_value::TYPE_REFERENCE) {
+    const android::ResolvedBag* bag = am->GetBag(static_cast<uint32_t>(value.data));
+    if (bag == nullptr) {
       out.append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data));
-      break;
+      return out;
+    }
+    out.append("[");
+    Res_value bag_val;
+    ResTable_config selected_config;
+    uint32_t flags;
+    uint32_t ref;
+    ApkAssetsCookie bag_cookie;
+    for (size_t i = 0; i < bag->entry_count; ++i) {
+      const android::ResolvedBag::Entry& entry = bag->entries[i];
+      bag_val = entry.value;
+      bag_cookie = am->ResolveReference(entry.cookie, &bag_val, &selected_config, &flags, &ref);
+      if (bag_cookie == kInvalidCookie) {
+        out.append(
+            StringPrintf("Error: dataType=0x%02x data=0x%08x", bag_val.dataType, bag_val.data));
+        continue;
+      }
+      PrintValue(am, bag_val, bag_cookie, &out);
+      if (i != bag->entry_count - 1) {
+        out.append(", ");
+      }
+    }
+    out.append("]");
+  } else {
+    PrintValue(am, value, cookie, &out);
   }
+
   return out;
 }
 
@@ -212,7 +249,7 @@
     return Error(resid.GetError(), "failed to parse resource ID");
   }
 
-  const Result<std::string> value = GetValue(am, *resid);
+  const Result<std::string> value = GetValue(&am, *resid);
   if (!value) {
     return Error(value.GetError(), "resource 0x%08x not found", *resid);
   }
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 89b1798..1bc235a 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -396,7 +396,7 @@
     }
 
     // Pulled events will start at field 10000.
-    // Next: 10071
+    // Next: 10074
     oneof pulled {
         WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"];
         WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"];
@@ -471,6 +471,12 @@
         GraphicsStats graphics_stats = 10068;
         RuntimeAppOpAccess runtime_app_op_access = 10069 [(module) = "framework"];
         IonHeapSize ion_heap_size = 10070 [(module) = "framework"];
+        PackageNotificationPreferences package_notification_preferences =
+                10071 [(module) = "framework"];
+        PackageNotificationChannelPreferences package_notification_channel_preferences =
+                10072 [(module) = "framework"];
+        PackageNotificationChannelGroupPreferences package_notification_channel_group_preferences =
+                10073 [(module) = "framework"];
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -3475,27 +3481,25 @@
     // A small system-assigned identifier for the notification.
     // Locally probably-unique, but expect collisions across users and/or days.
     optional int32 instance_id = 4;
-    // The app-assigned notification ID and tag
-    optional int32 notification_id = 5;
-    optional string notification_tag = 6;
-    optional string channel_id = 7;  // App-assigned channel ID
+    optional int32 notification_id_hash = 5;  // Small hash of the app-assigned notif ID + tag
+    optional int32 channel_id_hash = 6;  // Small hash of app-assigned channel ID
 
     // Grouping information
-    optional string group_id = 8;  // Group the notification currently belongs to
-    optional int32 group_instance_id = 9;  // Instance_id of the group-summary notification
-    optional bool is_group_summary = 10;  // Tags the group-summary notification
+    optional int32 group_id_hash = 7;  // Small hash of the group ID of the notification
+    optional int32 group_instance_id = 8;  // Instance_id of the group-summary notification
+    optional bool is_group_summary = 9;  // Tags the group-summary notification
 
     // Attributes
-    optional string category = 11;   // App-assigned notification category (API-defined strings)
-    optional int32 style = 12;       // App-assigned notification style
-    optional int32 num_people = 13;  // Number of Person records attached to the notification
+    optional string category = 10;   // App-assigned notification category (API-defined strings)
+    optional int32 style = 11;       // App-assigned notification style
+    optional int32 num_people = 12;  // Number of Person records attached to the notification
 
     // Ordering, importance and interruptiveness
 
-    optional int32 position = 14;    // Position in NotificationManager's list
+    optional int32 position = 13;    // Position in NotificationManager's list
 
-    optional android.stats.sysui.NotificationImportance importance = 15;
-    optional int32 alerting = 16;    // Bitfield, 1=buzz 2=beep 4=blink
+    optional android.stats.sysui.NotificationImportance importance = 14;
+    optional int32 alerting = 15;    // Bitfield, 1=buzz 2=beep 4=blink
 
     enum NotificationImportanceExplanation {
         IMPORTANCE_EXPLANATION_UNKNOWN = 0;
@@ -3507,12 +3511,12 @@
         IMPORTANCE_EXPLANATION_APP_PRE_CHANNELS = 5;
     }
 
-    optional NotificationImportanceExplanation importance_source = 17;
-    optional android.stats.sysui.NotificationImportance importance_initial = 18;
-    optional NotificationImportanceExplanation importance_initial_source = 19;
-    optional android.stats.sysui.NotificationImportance importance_asst = 20;
-    optional int32 assistant_hash = 21;
-    optional float assistant_ranking_score = 22;
+    optional NotificationImportanceExplanation importance_source = 16;
+    optional android.stats.sysui.NotificationImportance importance_initial = 17;
+    optional NotificationImportanceExplanation importance_initial_source = 18;
+    optional android.stats.sysui.NotificationImportance importance_asst = 19;
+    optional int32 assistant_hash = 20;
+    optional float assistant_ranking_score = 21;
 }
 
 message Notification {
@@ -5469,6 +5473,65 @@
     optional NotificationRemoteViewsProto notification_remote_views = 1;
 }
 
+/**
+ * Atom that contains a list of a package's preferences, pulled from NotificationManagerService.java
+ */
+message PackageNotificationPreferences {
+    // Uid under which the package is installed.
+    optional int32 uid = 1;
+    // Notification importance, which specifies when and how a notification is displayed.
+    // Specified under core/java/android/app/NotificationManager.java.
+    optional int32 importance = 2;
+    // Lockscreen visibility as set by the user.
+    optional int32 visibility = 3;
+    // Bitfield mask indicating what fields were locked by the user (see LockableAppfields in
+    // PreferencesHelper.java)
+    optional int32 user_locked_fields = 4;
+}
+
+/**
+ * Atom that contains a list of a package's channel preferences, pulled from
+ * NotificationManagerService.java.
+ */
+message PackageNotificationChannelPreferences {
+    // Uid under which the package is installed.
+    optional int32 uid = 1;
+    // Channel's ID. Should always be available.
+    optional string channel_id = 2;
+    // Channel's name. Should always be available.
+    optional string channel_name = 3;
+    // Channel's description. Optionally set by the channel creator.
+    optional string description = 4;
+    // Notification importance, which specifies when and how a notification is displayed. Specified
+    // under core/java/android/app/NotificationManager.java.
+    optional int32 importance = 5;
+    // Bitmask representing which fields have been set by the user. See field bitmask descriptions
+    // at core/java/android/app/NotificationChannel.java
+    optional int32 user_locked_fields = 6;
+    // Indicates if the channel was deleted by the app.
+    optional bool is_deleted = 7;
+}
+
+/**
+ * Atom that contains a list of a package's channel group preferences, pulled from
+ * NotificationManagerService.java.
+ */
+message PackageNotificationChannelGroupPreferences {
+    // Uid under which the package is installed.
+    optional int32 uid = 1;
+    // Channel Group's ID. Should always be available.
+    optional string group_id = 2;
+    // Channel Group's name. Should always be available.
+    optional string group_name = 3;
+    // Channel Group's description. Optionally set by group creator.
+    optional string description = 4;
+    // Indicates if notifications from this channel group are blocked.
+    optional bool is_blocked = 5;
+    // Bitmask representing which fields have been set by the user. See field bitmask descriptions
+    // at core/java/android/app/NotificationChannelGroup.java
+    optional int32 user_locked_fields = 6;
+}
+
 message PowerProfileProto {
     optional double cpu_suspend = 1;
 
@@ -6986,6 +7049,20 @@
     optional string description = 6;
     optional Method method = 7;
     optional string mime_types = 8;
+
+    optional int64 get_constraints_count =  9;
+    optional int64 get_metadata_count = 10;
+    optional int64 can_handle_count = 11;
+    optional int64 process_drm_info_count = 12;
+    optional int64 acquire_drm_info_count = 13;
+    optional int64 save_rights_count = 14;
+    optional int64 get_original_mime_type_count = 15;
+    optional int64 get_drm_object_type_count = 16;
+    optional int64 check_rights_status_count = 17;
+    optional int64 remove_rights_count = 18;
+    optional int64 remove_all_rights_count = 19;
+    optional int64 open_convert_session_count = 20;
+    optional int64 open_decrypt_session_count = 21;
 }
 
 /**
@@ -7919,6 +7996,10 @@
     // Total time that was spent performing animations.
     // This is derived from the present-to-present layer histogram
     optional int64 animation_millis = 5;
+    // Total number of event connections tracked by SurfaceFlinger at the time
+    // of this pull. If this number grows prohibitively large, then this can
+    // cause jank due to resource contention.
+    optional int32 event_connection_count = 6;
 }
 
 /**
@@ -7960,6 +8041,10 @@
     // presentation, until the buffer was ready to be presented.
     optional FrameTimingHistogram post_to_acquire = 9
         [(android.os.statsd.log_mode) = MODE_BYTES];
+    // Frames missed latch because the acquire fence didn't fire
+    optional int64 late_acquire_frames = 10;
+    // Frames latched early because the desired present time was bad
+    optional int64 bad_desired_present_frames = 11;
 }
 
 /**
diff --git a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
index d79740b..9912d2b 100644
--- a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
@@ -76,6 +76,16 @@
     private final int mDescriptionResId;
 
     /**
+     * Resource id of the animated image of the accessibility shortcut target.
+     */
+    private final int mAnimatedImageRes;
+
+    /**
+     * Resource id of the html description of the accessibility shortcut target.
+     */
+    private final int mHtmlDescriptionRes;
+
+    /**
      * Creates a new instance.
      *
      * @param context Context for accessing resources.
@@ -119,6 +129,14 @@
             // Gets summary
             mSummaryResId = asAttributes.getResourceId(
                     com.android.internal.R.styleable.AccessibilityShortcutTarget_summary, 0);
+            // Gets animated image
+            mAnimatedImageRes = asAttributes.getResourceId(
+                    com.android.internal.R.styleable
+                            .AccessibilityShortcutTarget_animatedImageDrawable, 0);
+            // Gets html description
+            mHtmlDescriptionRes = asAttributes.getResourceId(
+                    com.android.internal.R.styleable.AccessibilityShortcutTarget_htmlDescription,
+                    0);
             asAttributes.recycle();
 
             if (mDescriptionResId == 0 || mSummaryResId == 0) {
@@ -172,6 +190,25 @@
     }
 
     /**
+     * The animated image resource id of the accessibility shortcut target.
+     *
+     * @return The animated image resource id.
+     */
+    public int getAnimatedImageRes() {
+        return mAnimatedImageRes;
+    }
+
+    /**
+     * The localized html description of the accessibility shortcut target.
+     *
+     * @return The localized html description.
+     */
+    @Nullable
+    public String loadHtmlDescription(@NonNull PackageManager packageManager) {
+        return loadResourceString(packageManager, mActivityInfo, mHtmlDescriptionRes);
+    }
+
+    /**
      * Gets string resource by the given activity and resource id.
      */
     @Nullable
diff --git a/core/java/android/annotation/NonNull.java b/core/java/android/annotation/NonNull.java
index 927f997..a95bf3b 100644
--- a/core/java/android/annotation/NonNull.java
+++ b/core/java/android/annotation/NonNull.java
@@ -15,14 +15,16 @@
  */
 package android.annotation;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
+import android.annotation.SystemApi.Client;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that a parameter, field or method return value can never be null.
  * <p>
@@ -34,5 +36,6 @@
  */
 @Retention(SOURCE)
 @Target({METHOD, PARAMETER, FIELD})
+@SystemApi(client = Client.MODULE_LIBRARIES)
 public @interface NonNull {
 }
diff --git a/core/java/android/annotation/Nullable.java b/core/java/android/annotation/Nullable.java
index b60170b..2fcddfa 100644
--- a/core/java/android/annotation/Nullable.java
+++ b/core/java/android/annotation/Nullable.java
@@ -15,14 +15,16 @@
  */
 package android.annotation;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
+import android.annotation.SystemApi.Client;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that a parameter, field or method return value can be null.
  * <p>
@@ -41,5 +43,6 @@
  */
 @Retention(SOURCE)
 @Target({METHOD, PARAMETER, FIELD})
+@SystemApi(client = Client.MODULE_LIBRARIES)
 public @interface Nullable {
 }
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index fc37af9..367c2f2 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -924,10 +924,12 @@
      * @hide
      */
     public static final int OP_ACTIVATE_PLATFORM_VPN = 94;
+    /** @hide */
+    public static final int OP_LOADER_USAGE_STATS = 95;
 
     /** @hide */
     @UnsupportedAppUsage
-    public static final int _NUM_OP = 95;
+    public static final int _NUM_OP = 96;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1224,6 +1226,9 @@
     public static final String OPSTR_INTERACT_ACROSS_PROFILES = "android:interact_across_profiles";
     /** @hide Start Platform VPN without user intervention */
     public static final String OPSTR_ACTIVATE_PLATFORM_VPN = "android:activate_platform_vpn";
+    /** @hide */
+    @SystemApi
+    public static final String OPSTR_LOADER_USAGE_STATS = "android:loader_usage_stats";
 
 
     /** {@link #sAppOpsToNote} not initialized yet for this op */
@@ -1302,7 +1307,8 @@
             OP_MANAGE_IPSEC_TUNNELS,
             OP_INSTANT_APP_START_FOREGROUND,
             OP_MANAGE_EXTERNAL_STORAGE,
-            OP_INTERACT_ACROSS_PROFILES
+            OP_INTERACT_ACROSS_PROFILES,
+            OP_LOADER_USAGE_STATS,
     };
 
     /**
@@ -1409,6 +1415,7 @@
             OP_MANAGE_EXTERNAL_STORAGE,         // MANAGE_EXTERNAL_STORAGE
             OP_INTERACT_ACROSS_PROFILES,        //INTERACT_ACROSS_PROFILES
             OP_ACTIVATE_PLATFORM_VPN,           // ACTIVATE_PLATFORM_VPN
+            OP_LOADER_USAGE_STATS,              // LOADER_USAGE_STATS
     };
 
     /**
@@ -1510,6 +1517,7 @@
             OPSTR_MANAGE_EXTERNAL_STORAGE,
             OPSTR_INTERACT_ACROSS_PROFILES,
             OPSTR_ACTIVATE_PLATFORM_VPN,
+            OPSTR_LOADER_USAGE_STATS,
     };
 
     /**
@@ -1612,6 +1620,7 @@
             "MANAGE_EXTERNAL_STORAGE",
             "INTERACT_ACROSS_PROFILES",
             "ACTIVATE_PLATFORM_VPN",
+            "LOADER_USAGE_STATS",
     };
 
     /**
@@ -1715,6 +1724,7 @@
             Manifest.permission.MANAGE_EXTERNAL_STORAGE,
             android.Manifest.permission.INTERACT_ACROSS_PROFILES,
             null, // no permission for OP_ACTIVATE_PLATFORM_VPN
+            android.Manifest.permission.LOADER_USAGE_STATS,
     };
 
     /**
@@ -1818,6 +1828,7 @@
             null, // MANAGE_EXTERNAL_STORAGE
             null, // INTERACT_ACROSS_PROFILES
             null, // ACTIVATE_PLATFORM_VPN
+            null, // LOADER_USAGE_STATS
     };
 
     /**
@@ -1920,6 +1931,7 @@
             false, // MANAGE_EXTERNAL_STORAGE
             false, // INTERACT_ACROSS_PROFILES
             false, // ACTIVATE_PLATFORM_VPN
+            false, // LOADER_USAGE_STATS
     };
 
     /**
@@ -2021,6 +2033,7 @@
             AppOpsManager.MODE_DEFAULT, // MANAGE_EXTERNAL_STORAGE
             AppOpsManager.MODE_DEFAULT, // INTERACT_ACROSS_PROFILES
             AppOpsManager.MODE_IGNORED, // ACTIVATE_PLATFORM_VPN
+            AppOpsManager.MODE_DEFAULT, // LOADER_USAGE_STATS
     };
 
     /**
@@ -2126,6 +2139,7 @@
             false, // MANAGE_EXTERNAL_STORAGE
             false, // INTERACT_ACROSS_PROFILES
             false, // ACTIVATE_PLATFORM_VPN
+            false, // LOADER_USAGE_STATS
     };
 
     /**
diff --git a/core/java/android/app/AsyncNotedAppOp.java b/core/java/android/app/AsyncNotedAppOp.java
index 3febf71..130e2ec 100644
--- a/core/java/android/app/AsyncNotedAppOp.java
+++ b/core/java/android/app/AsyncNotedAppOp.java
@@ -256,10 +256,10 @@
     };
 
     @DataClass.Generated(
-            time = 1578516519372L,
+            time = 1580158740502L,
             codegenVersion = "1.0.14",
             sourceFile = "frameworks/base/core/java/android/app/AsyncNotedAppOp.java",
-            inputSignatures = "private final @android.annotation.IntRange(from=0L, to=94L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)")
+            inputSignatures = "private final @android.annotation.IntRange(from=0L, to=95L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 1d4a1ac..9aa6b87 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -123,7 +123,7 @@
     @UnsupportedAppUsage
     StatusBarNotification[] getActiveNotifications(String callingPkg);
     @UnsupportedAppUsage
-    StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count);
+    StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count, boolean includeSnoozed);
 
     NotificationHistory getNotificationHistory(String callingPkg);
 
diff --git a/core/java/android/app/ITaskOrganizerController.aidl b/core/java/android/app/ITaskOrganizerController.aidl
index 5d5956e..9d6c3d6 100644
--- a/core/java/android/app/ITaskOrganizerController.aidl
+++ b/core/java/android/app/ITaskOrganizerController.aidl
@@ -52,7 +52,11 @@
     boolean deleteRootTask(IWindowContainer task);
 
     /** Gets direct child tasks (ordered from top-to-bottom) */
-    List<ActivityManager.RunningTaskInfo> getChildTasks(in IWindowContainer parent);
+    List<ActivityManager.RunningTaskInfo> getChildTasks(in IWindowContainer parent,
+            in int[] activityTypes);
+
+    /** Gets all root tasks on a display (ordered from top-to-bottom) */
+    List<ActivityManager.RunningTaskInfo> getRootTasks(int displayId, in int[] activityTypes);
 
     /** Get the root task which contains the current ime target */
     IWindowContainer getImeTarget(int display);
diff --git a/core/java/android/app/PictureInPictureParams.java b/core/java/android/app/PictureInPictureParams.java
index bfd966c5..67d94de 100644
--- a/core/java/android/app/PictureInPictureParams.java
+++ b/core/java/android/app/PictureInPictureParams.java
@@ -247,6 +247,14 @@
         return mSourceRectHint != null && !mSourceRectHint.isEmpty();
     }
 
+    /**
+     * @return True if no parameters are set
+     * @hide
+     */
+    public boolean empty() {
+        return !hasSourceBoundsHint() && !hasSetActions() && !hasSetAspectRatio();
+    }
+
     @Override
     public int describeContents() {
         return 0;
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 662ca6e..f7d712d 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -149,6 +149,13 @@
     public IWindowContainer token;
 
     /**
+     * The PictureInPictureParams for the Task, if set.
+     * @hide
+     */
+    @Nullable
+    public PictureInPictureParams pictureInPictureParams;
+
+    /**
      * The activity type of the top activity in this task.
      * @hide
      */
@@ -209,6 +216,9 @@
         configuration.readFromParcel(source);
         token = IWindowContainer.Stub.asInterface(source.readStrongBinder());
         topActivityType = source.readInt();
+        pictureInPictureParams = source.readInt() != 0
+            ? PictureInPictureParams.CREATOR.createFromParcel(source)
+            : null;
     }
 
     /**
@@ -246,6 +256,12 @@
         configuration.writeToParcel(dest, flags);
         dest.writeStrongInterface(token);
         dest.writeInt(topActivityType);
+        if (pictureInPictureParams == null) {
+            dest.writeInt(0);
+        } else {
+            dest.writeInt(1);
+            pictureInPictureParams.writeToParcel(dest, flags);
+        }
     }
 
     @Override
@@ -261,6 +277,7 @@
                 + " supportsSplitScreenMultiWindow=" + supportsSplitScreenMultiWindow
                 + " resizeMode=" + resizeMode
                 + " token=" + token
-                + " topActivityType=" + topActivityType;
+                + " topActivityType=" + topActivityType
+                + " pictureInPictureParams=" + pictureInPictureParams;
     }
 }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 9cec514..48eab92 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -5860,21 +5860,27 @@
      * a secure video output. See {@link android.view.Display#FLAG_SECURE} for more details about
      * secure surfaces and secure displays.
      * <p>
-     * The calling device admin must be a device or profile owner. If it is not, a security
+     * This method can be called on the {@link DevicePolicyManager} instance, returned by
+     * {@link #getParentProfileInstance(ComponentName)}, where the calling device admin must be
+     * the profile owner of an organization-owned managed profile. If it is not, a security
      * exception will be thrown.
      * <p>
+     * If the caller is device owner or called on the parent instance by a profile owner of an
+     * organization-owned managed profile, then the restriction will be applied to all users.
+     * <p>
      * From version {@link android.os.Build.VERSION_CODES#M} disabling screen capture also blocks
      * assist requests for all activities of the relevant user.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param disabled Whether screen capture is disabled or not.
-     * @throws SecurityException if {@code admin} is not a device or profile owner.
+     * @throws SecurityException if {@code admin} is not a device or profile owner or if
+     *                           called on the parent profile and the {@code admin} is not a
+     *                           profile owner of an organization-owned managed profile.
      */
     public void setScreenCaptureDisabled(@NonNull ComponentName admin, boolean disabled) {
-        throwIfParentInstance("setScreenCaptureDisabled");
         if (mService != null) {
             try {
-                mService.setScreenCaptureDisabled(admin, disabled);
+                mService.setScreenCaptureDisabled(admin, disabled, mParentInstance);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -5884,11 +5890,16 @@
     /**
      * Determine whether or not screen capture has been disabled by the calling
      * admin, if specified, or all admins.
-     * @param admin The name of the admin component to check, or {@code null} to check whether any admins
-     * have disabled screen capture.
+     * <p>
+     * This method can be called on the {@link DevicePolicyManager} instance,
+     * returned by {@link #getParentProfileInstance(ComponentName)}, where the caller must be
+     * the profile owner of an organization-owned managed profile (the calling admin must be
+     * specified).
+     *
+     * @param admin The name of the admin component to check, or {@code null} to check whether any
+     *              admins have disabled screen capture.
      */
     public boolean getScreenCaptureDisabled(@Nullable ComponentName admin) {
-        throwIfParentInstance("getScreenCaptureDisabled");
         return getScreenCaptureDisabled(admin, myUserId());
     }
 
@@ -5896,7 +5907,7 @@
     public boolean getScreenCaptureDisabled(@Nullable ComponentName admin, int userHandle) {
         if (mService != null) {
             try {
-                return mService.getScreenCaptureDisabled(admin, userHandle);
+                return mService.getScreenCaptureDisabled(admin, userHandle, mParentInstance);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -8723,6 +8734,7 @@
     @StringDef({
             Settings.System.SCREEN_BRIGHTNESS_MODE,
             Settings.System.SCREEN_BRIGHTNESS,
+            Settings.System.SCREEN_BRIGHTNESS_FLOAT,
             Settings.System.SCREEN_OFF_TIMEOUT
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -8951,7 +8963,8 @@
      *
      * <strong>Note: Starting from Android R, apps should no longer call this method with the
      * setting {@link android.provider.Settings.Secure#LOCATION_MODE}, which is deprecated. Instead,
-     * device owners should call {@link #setLocationEnabled(ComponentName, boolean)}.
+     * device owners should call {@link #setLocationEnabled(ComponentName, boolean)}. This will be
+     * enforced for all apps targeting Android R or above.
      * </strong>
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
@@ -8961,6 +8974,7 @@
      */
     public void setSecureSetting(@NonNull ComponentName admin, String setting, String value) {
         throwIfParentInstance("setSecureSetting");
+
         if (mService != null) {
             try {
                 mService.setSecureSetting(admin, setting, value);
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index e6c89d9..80fa871 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -187,4 +187,22 @@
      * @hide
      */
     public abstract List<String> getAllCrossProfilePackages();
+
+    /**
+     * Sends the {@code intent} to the packages with cross profile capabilities.
+     *
+     * <p>This means the application must have the {@code crossProfile} property and the
+     * corresponding permissions, defined by
+     * {@link
+     * android.content.pm.CrossProfileAppsInternal#verifyPackageHasInteractAcrossProfilePermission}.
+     *
+     * <p>Note: This method doesn't modify {@code intent} but copies it before use.
+     *
+     * @param intent Template for the intent sent to the package.
+     * @param parentHandle Handle of the user that will receive the intents.
+     * @param requiresPermission If false, all packages with the {@code crossProfile} property
+     *                           will receive the intent.
+     */
+    public abstract void broadcastIntentToCrossProfileManifestReceiversAsUser(Intent intent,
+            UserHandle parentHandle, boolean requiresPermission);
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index d2672eb..8ef25bd 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -121,8 +121,8 @@
     void setCameraDisabled(in ComponentName who, boolean disabled, boolean parent);
     boolean getCameraDisabled(in ComponentName who, int userHandle, boolean parent);
 
-    void setScreenCaptureDisabled(in ComponentName who, boolean disabled);
-    boolean getScreenCaptureDisabled(in ComponentName who, int userHandle);
+    void setScreenCaptureDisabled(in ComponentName who, boolean disabled, boolean parent);
+    boolean getScreenCaptureDisabled(in ComponentName who, int userHandle, boolean parent);
 
     void setKeyguardDisabledFeatures(in ComponentName who, int which, boolean parent);
     int getKeyguardDisabledFeatures(in ComponentName who, int userHandle, boolean parent);
diff --git a/core/java/android/app/compat/TEST_MAPPING b/core/java/android/app/compat/TEST_MAPPING
new file mode 100644
index 0000000..c047df5
--- /dev/null
+++ b/core/java/android/app/compat/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+    "imports": [
+        {
+            "path": "frameworks/base/services/core/java/com/android/services/compat"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/core/java/android/app/prediction/AppPredictionContext.java b/core/java/android/app/prediction/AppPredictionContext.java
index 8fc7e8d..d14238b 100644
--- a/core/java/android/app/prediction/AppPredictionContext.java
+++ b/core/java/android/app/prediction/AppPredictionContext.java
@@ -100,16 +100,6 @@
                 && mPackageName.equals(other.mPackageName);
     }
 
-    @NonNull
-    @Override
-    public String toString() {
-        return new StringBuilder(this.getClass().getSimpleName())
-                .append("[mUiSurface=").append(mUiSurface)
-                .append(",mPackageName=").append(mPackageName)
-                .append(",mPredictedTargetCount=").append(mPredictedTargetCount)
-                .append(",mExtras=").append(mExtras.toString()).append("]").toString();
-    }
-
     @Override
     public int describeContents() {
         return 0;
diff --git a/core/java/android/app/prediction/AppPredictionSessionId.java b/core/java/android/app/prediction/AppPredictionSessionId.java
index e5e06f8..876bafd 100644
--- a/core/java/android/app/prediction/AppPredictionSessionId.java
+++ b/core/java/android/app/prediction/AppPredictionSessionId.java
@@ -22,6 +22,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Objects;
+
 /**
  * The id for an app prediction session. See {@link AppPredictor}.
  *
@@ -32,18 +34,28 @@
 public final class AppPredictionSessionId implements Parcelable {
 
     private final String mId;
+    private final int mUserId;
 
     /**
      * Creates a new id for a prediction session.
      *
      * @hide
      */
-    public AppPredictionSessionId(@NonNull String id) {
+    public AppPredictionSessionId(@NonNull final String id, final int userId) {
         mId = id;
+        mUserId = userId;
     }
 
     private AppPredictionSessionId(Parcel p) {
         mId = p.readString();
+        mUserId = p.readInt();
+    }
+
+    /**
+     * @hide
+     */
+    public int getUserId() {
+        return mUserId;
     }
 
     @Override
@@ -51,17 +63,17 @@
         if (!getClass().equals(o != null ? o.getClass() : null)) return false;
 
         AppPredictionSessionId other = (AppPredictionSessionId) o;
-        return mId.equals(other.mId);
+        return mId.equals(other.mId) && mUserId == other.mUserId;
     }
 
     @Override
     public @NonNull String toString() {
-        return mId;
+        return mId + "," + mUserId;
     }
 
     @Override
     public int hashCode() {
-        return mId.hashCode();
+        return Objects.hash(mId, mUserId);
     }
 
     @Override
@@ -72,6 +84,7 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(mId);
+        dest.writeInt(mUserId);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<AppPredictionSessionId> CREATOR =
diff --git a/core/java/android/app/prediction/AppPredictor.java b/core/java/android/app/prediction/AppPredictor.java
index cd635d6..f0eedf3 100644
--- a/core/java/android/app/prediction/AppPredictor.java
+++ b/core/java/android/app/prediction/AppPredictor.java
@@ -96,7 +96,7 @@
         IBinder b = ServiceManager.getService(Context.APP_PREDICTION_SERVICE);
         mPredictionManager = IPredictionManager.Stub.asInterface(b);
         mSessionId = new AppPredictionSessionId(
-                context.getPackageName() + ":" + UUID.randomUUID().toString());
+                context.getPackageName() + ":" + UUID.randomUUID().toString(), context.getUserId());
         try {
             mPredictionManager.createPredictionSession(predictionContext, mSessionId);
         } catch (RemoteException e) {
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index d8c653c6..b672a08 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -643,8 +643,9 @@
     @SystemApi
     @Nullable
     @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public BluetoothCodecStatus getCodecStatus(@Nullable BluetoothDevice device) {
+    public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) {
         if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")");
+        verifyDeviceNotNull(device, "getCodecStatus");
         try {
             final IBluetoothA2dp service = getService();
             if (service != null && isEnabled()) {
@@ -670,9 +671,14 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void setCodecConfigPreference(@Nullable BluetoothDevice device,
-                                         @Nullable BluetoothCodecConfig codecConfig) {
+    public void setCodecConfigPreference(@NonNull BluetoothDevice device,
+                                         @NonNull BluetoothCodecConfig codecConfig) {
         if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")");
+        verifyDeviceNotNull(device, "setCodecConfigPreference");
+        if (codecConfig == null) {
+            Log.e(TAG, "setCodecConfigPreference: Codec config can't be null");
+            throw new IllegalArgumentException("codecConfig cannot be null");
+        }
         try {
             final IBluetoothA2dp service = getService();
             if (service != null && isEnabled()) {
@@ -695,8 +701,9 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void enableOptionalCodecs(@Nullable BluetoothDevice device) {
+    public void enableOptionalCodecs(@NonNull BluetoothDevice device) {
         if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")");
+        verifyDeviceNotNull(device, "enableOptionalCodecs");
         enableDisableOptionalCodecs(device, true);
     }
 
@@ -709,8 +716,9 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void disableOptionalCodecs(@Nullable BluetoothDevice device) {
+    public void disableOptionalCodecs(@NonNull BluetoothDevice device) {
         if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")");
+        verifyDeviceNotNull(device, "disableOptionalCodecs");
         enableDisableOptionalCodecs(device, false);
     }
 
@@ -750,7 +758,8 @@
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     @OptionalCodecsSupportStatus
-    public int supportsOptionalCodecs(@Nullable BluetoothDevice device) {
+    public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) {
+        verifyDeviceNotNull(device, "isOptionalCodecsSupported");
         try {
             final IBluetoothA2dp service = getService();
             if (service != null && isEnabled() && isValidDevice(device)) {
@@ -775,7 +784,8 @@
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     @OptionalCodecsPreferenceStatus
-    public int getOptionalCodecsEnabled(@Nullable BluetoothDevice device) {
+    public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) {
+        verifyDeviceNotNull(device, "isOptionalCodecsEnabled");
         try {
             final IBluetoothA2dp service = getService();
             if (service != null && isEnabled() && isValidDevice(device)) {
@@ -800,8 +810,9 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void setOptionalCodecsEnabled(@Nullable BluetoothDevice device,
+    public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device,
             @OptionalCodecsPreferenceStatus int value) {
+        verifyDeviceNotNull(device, "setOptionalCodecsEnabled");
         try {
             if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN
                     && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED
@@ -854,6 +865,13 @@
         return false;
     }
 
+    private void verifyDeviceNotNull(BluetoothDevice device, String methodName) {
+        if (device == null) {
+            Log.e(TAG, methodName + ": device param is null");
+            throw new IllegalArgumentException("Device cannot be null");
+        }
+    }
+
     private boolean isValidDevice(BluetoothDevice device) {
         if (device == null) return false;
 
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
index ee2cc6d..ab49230 100755
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -66,7 +66,7 @@
      */
     @SystemApi
     @SuppressLint("ActionValue")
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public static final String ACTION_CONNECTION_STATE_CHANGED =
             "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED";
 
@@ -296,7 +296,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean setConnectionPolicy(@Nullable BluetoothDevice device,
             @ConnectionPolicy int connectionPolicy) {
         if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -345,7 +345,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
         if (VDBG) log("getConnectionPolicy(" + device + ")");
         final IBluetoothA2dpSink service = getService();
@@ -370,7 +370,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean isAudioPlaying(@Nullable BluetoothDevice device) {
         final IBluetoothA2dpSink service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 587c92e..66bfcbd 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1970,6 +1970,38 @@
         }
     }
 
+    private static final String BLUETOOTH_FILTERING_CACHE_PROPERTY =
+            "cache_key.bluetooth.is_offloaded_filtering_supported";
+    private final PropertyInvalidatedCache<Void, Boolean> mBluetoothFilteringCache =
+            new PropertyInvalidatedCache<Void, Boolean>(
+                8, BLUETOOTH_FILTERING_CACHE_PROPERTY) {
+                @Override
+                protected Boolean recompute(Void query) {
+                    try {
+                        mServiceLock.readLock().lock();
+                        if (mService != null) {
+                            return mService.isOffloadedFilteringSupported();
+                        }
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "failed to get isOffloadedFilteringSupported, error: ", e);
+                    } finally {
+                        mServiceLock.readLock().unlock();
+                    }
+                    return false;
+
+                }
+            };
+
+    /** @hide */
+    public void disableIsOffloadedFilteringSupportedCache() {
+        mBluetoothFilteringCache.disableLocal();
+    }
+
+    /** @hide */
+    public static void invalidateIsOffloadedFilteringSupportedCache() {
+        PropertyInvalidatedCache.invalidateCache(BLUETOOTH_FILTERING_CACHE_PROPERTY);
+    }
+
     /**
      * Return true if offloaded filters are supported
      *
@@ -1979,17 +2011,7 @@
         if (!getLeAccess()) {
             return false;
         }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.isOffloadedFilteringSupported();
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "failed to get isOffloadedFilteringSupported, error: ", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
+        return mBluetoothFilteringCache.query(null);
     }
 
     /**
@@ -2361,6 +2383,43 @@
         return BluetoothAdapter.STATE_DISCONNECTED;
     }
 
+    private static final String BLUETOOTH_PROFILE_CACHE_PROPERTY =
+            "cache_key.bluetooth.get_profile_connection_state";
+    private final PropertyInvalidatedCache<Integer, Integer>
+            mGetProfileConnectionStateCache =
+            new PropertyInvalidatedCache<Integer, Integer>(
+                8, BLUETOOTH_PROFILE_CACHE_PROPERTY) {
+                @Override
+                protected Integer recompute(Integer query) {
+                    try {
+                        mServiceLock.readLock().lock();
+                        if (mService != null) {
+                            return mService.getProfileConnectionState(query);
+                        }
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "getProfileConnectionState:", e);
+                    } finally {
+                        mServiceLock.readLock().unlock();
+                    }
+                    return BluetoothProfile.STATE_DISCONNECTED;
+                }
+                @Override
+                public String queryToString(Integer query) {
+                    return String.format("getProfileConnectionState(profile=\"%d\")",
+                                         query);
+                }
+            };
+
+    /** @hide */
+    public void disableGetProfileConnectionStateCache() {
+        mGetProfileConnectionStateCache.disableLocal();
+    }
+
+    /** @hide */
+    public static void invalidateGetProfileConnectionStateCache() {
+        PropertyInvalidatedCache.invalidateCache(BLUETOOTH_PROFILE_CACHE_PROPERTY);
+    }
+
     /**
      * Get the current connection state of a profile.
      * This function can be used to check whether the local Bluetooth adapter
@@ -2378,17 +2437,7 @@
         if (getState() != STATE_ON) {
             return BluetoothProfile.STATE_DISCONNECTED;
         }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.getProfileConnectionState(profile);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "getProfileConnectionState:", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return BluetoothProfile.STATE_DISCONNECTED;
+        return mGetProfileConnectionStateCache.query(new Integer(profile));
     }
 
     /**
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index e07ca52..1f89ddf 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -38,9 +38,6 @@
 import java.util.List;
 
 /**
- * The Android Bluetooth API is not finalized, and *will* change. Use at your
- * own risk.
- *
  * Public API for controlling the Bluetooth Pbap Service. This includes
  * Bluetooth Phone book Access profile.
  * BluetoothPbap is a proxy object for controlling the Bluetooth Pbap
@@ -56,6 +53,11 @@
  * notification when it is bound, this is especially important if you wish to
  * immediately call methods on BluetoothPbap after construction.
  *
+ * To get an instance of the BluetoothPbap class, you can call
+ * {@link BluetoothAdapter#getProfileProxy(Context, ServiceListener, int)} with the final param
+ * being {@link BluetoothProfile#PBAP}. The ServiceListener should be able to get the instance of
+ * BluetoothPbap in {@link android.bluetooth.BluetoothProfile.ServiceListener#onServiceConnected}.
+ *
  * Android only supports one connected Bluetooth Pce at a time.
  *
  * @hide
@@ -87,6 +89,7 @@
      */
     @SuppressLint("ActionValue")
     @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_CONNECTION_STATE_CHANGED =
             "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
@@ -235,7 +238,8 @@
      */
     @SystemApi
     @Override
-    public int getConnectionState(@Nullable BluetoothDevice device) {
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public @BtProfileState int getConnectionState(@Nullable BluetoothDevice device) {
         log("getConnectionState: device=" + device);
         try {
             final IBluetoothPbap service = mService;
@@ -287,7 +291,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
             @ConnectionPolicy int connectionPolicy) {
         if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 0e0161f..f32a4ab 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -700,27 +700,6 @@
     /** @hide */
     public static final String REMOTE_CALLBACK_RESULT = "result";
 
-    /**
-     * How long we wait for an attached process to publish its content providers
-     * before we decide it must be hung.
-     * @hide
-     */
-    public static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS = 10 * 1000;
-
-    /**
-     * How long we wait for an provider to be published. Should be longer than
-     * {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS}.
-     * @hide
-     */
-    public static final int CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS =
-            CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS + 10 * 1000;
-
-    // Should be >= {@link #CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS}, because that's how
-    // long ActivityManagerService is giving a content provider to get published if a new process
-    // needs to be started for that.
-    private static final int GET_TYPE_TIMEOUT_MILLIS =
-            CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS + 5 * 1000;
-
     public ContentResolver(@Nullable Context context) {
         this(context, null);
     }
@@ -870,6 +849,8 @@
         }
     }
 
+    private static final int GET_TYPE_TIMEOUT_MILLIS = 3000;
+
     private static class GetTypeResultListener implements RemoteCallback.OnResultListener {
         @GuardedBy("this")
         public boolean done;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 6f8a99f..0f88c90 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4289,7 +4289,9 @@
      * intent filter in their manifests, so that they can be looked up and bound to by
      * {@code DataLoaderManagerService}.
      *
-     * Data loader service providers must be privileged apps. 
+     * <p class="note">This is a protected intent that can only be sent by the system.
+     *
+     * Data loader service providers must be privileged apps.
      * See {@link com.android.server.pm.PackageManagerShellCommandDataLoader} as an example of such
      * data loader service provider.
      *
@@ -4970,11 +4972,14 @@
      * <pre>
      * &lt;accessibility-shortcut-target
      *     android:description="@string/shortcut_target_description"
-     *     android:summary="@string/shortcut_target_summary" /&gt;
+     *     android:summary="@string/shortcut_target_summary"
+     *     android:animatedImageDrawable="@drawable/shortcut_target_animated_image"
+     *     android:htmlDescription="@string/shortcut_target_html_description" /&gt;
      * </pre>
      * <p>
      * Both description and summary are necessary. The system will ignore the accessibility
-     * shortcut target if they are missing.
+     * shortcut target if they are missing. The animated image and html description are supported
+     * to help users understand how to use the shortcut target.
      * </p>
      */
     @SdkConstant(SdkConstantType.INTENT_CATEGORY)
diff --git a/core/java/android/content/integrity/AppInstallMetadata.java b/core/java/android/content/integrity/AppInstallMetadata.java
index cd5117b..4be7e6d 100644
--- a/core/java/android/content/integrity/AppInstallMetadata.java
+++ b/core/java/android/content/integrity/AppInstallMetadata.java
@@ -18,7 +18,9 @@
 
 import android.annotation.NonNull;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -40,6 +42,7 @@
     private final List<String> mInstallerCertificates;
     private final long mVersionCode;
     private final boolean mIsPreInstalled;
+    private final Map<String, String> mAllowedInstallersAndCertificates;
 
     private AppInstallMetadata(Builder builder) {
         this.mPackageName = builder.mPackageName;
@@ -48,6 +51,7 @@
         this.mInstallerCertificates = builder.mInstallerCertificates;
         this.mVersionCode = builder.mVersionCode;
         this.mIsPreInstalled = builder.mIsPreInstalled;
+        this.mAllowedInstallersAndCertificates = builder.mAllowedInstallersAndCertificates;
     }
 
     @NonNull
@@ -80,6 +84,13 @@
         return mIsPreInstalled;
     }
 
+    /**
+     * Get the allowed installers and their corresponding cert.
+     */
+    public Map<String, String> getAllowedInstallersAndCertificates() {
+        return mAllowedInstallersAndCertificates;
+    }
+
     @Override
     public String toString() {
         return String.format(
@@ -101,6 +112,23 @@
         private List<String> mInstallerCertificates;
         private long mVersionCode;
         private boolean mIsPreInstalled;
+        private Map<String, String> mAllowedInstallersAndCertificates;
+
+        public Builder() {
+            mAllowedInstallersAndCertificates = new HashMap<>();
+        }
+
+        /**
+         * Add allowed installers and cert.
+         *
+         * @see AppInstallMetadata#getAllowedInstallersAndCertificates()
+         */
+        @NonNull
+        public Builder setAllowedInstallersAndCert(
+                @NonNull Map<String, String> allowedInstallersAndCertificates) {
+            this.mAllowedInstallersAndCertificates = allowedInstallersAndCertificates;
+            return this;
+        }
 
         /**
          * Set package name of the app to be installed.
diff --git a/core/java/android/content/integrity/AtomicFormula.java b/core/java/android/content/integrity/AtomicFormula.java
index 42459779..d911eab 100644
--- a/core/java/android/content/integrity/AtomicFormula.java
+++ b/core/java/android/content/integrity/AtomicFormula.java
@@ -55,14 +55,12 @@
                     PRE_INSTALLED,
             })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface Key {
-    }
+    public @interface Key {}
 
     /** @hide */
     @IntDef(value = {EQ, GT, GTE})
     @Retention(RetentionPolicy.SOURCE)
-    public @interface Operator {
-    }
+    public @interface Operator {}
 
     /**
      * Package name of the app.
@@ -354,7 +352,8 @@
                             "Key %s cannot be used with StringAtomicFormula", keyToString(key)));
             mValue = hashValue(key, value);
             mIsHashedValue =
-                    key == APP_CERTIFICATE || key == INSTALLER_CERTIFICATE
+                    key == APP_CERTIFICATE
+                            || key == INSTALLER_CERTIFICATE
                             ? true
                             : !mValue.equals(value);
         }
diff --git a/core/java/android/content/integrity/InstallerAllowedByManifestFormula.java b/core/java/android/content/integrity/InstallerAllowedByManifestFormula.java
new file mode 100644
index 0000000..475f019
--- /dev/null
+++ b/core/java/android/content/integrity/InstallerAllowedByManifestFormula.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.integrity;
+
+import java.util.Map;
+
+/**
+ * An atomic formula that evaluates to true if the installer of the current install is specified in
+ * the "allowed installer" field in the android manifest. Note that an empty "allowed installer" by
+ * default means containing all possible installers.
+ *
+ * @hide
+ */
+public class InstallerAllowedByManifestFormula extends IntegrityFormula {
+
+    @Override
+    public int getTag() {
+        return IntegrityFormula.INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG;
+    }
+
+    @Override
+    public boolean matches(AppInstallMetadata appInstallMetadata) {
+        Map<String, String> allowedInstallersAndCertificates =
+                appInstallMetadata.getAllowedInstallersAndCertificates();
+        return allowedInstallersAndCertificates.isEmpty()
+                || installerInAllowedInstallersFromManifest(
+                appInstallMetadata, allowedInstallersAndCertificates);
+    }
+
+    @Override
+    public boolean isAppCertificateFormula() {
+        return false;
+    }
+
+    @Override
+    public boolean isInstallerFormula() {
+        return true;
+    }
+
+    private static boolean installerInAllowedInstallersFromManifest(
+            AppInstallMetadata appInstallMetadata,
+            Map<String, String> allowedInstallersAndCertificates) {
+        return allowedInstallersAndCertificates.containsKey(appInstallMetadata.getInstallerName())
+                && appInstallMetadata.getInstallerCertificates()
+                .contains(
+                        allowedInstallersAndCertificates
+                        .get(appInstallMetadata.getInstallerName()));
+    }
+}
diff --git a/core/java/android/content/integrity/IntegrityFormula.java b/core/java/android/content/integrity/IntegrityFormula.java
index a2d937e..ac4c907 100644
--- a/core/java/android/content/integrity/IntegrityFormula.java
+++ b/core/java/android/content/integrity/IntegrityFormula.java
@@ -42,66 +42,88 @@
 @VisibleForTesting
 public abstract class IntegrityFormula {
 
-    /**
-     * A static formula base for package name formulas.
-     *
-     * This formulation is incomplete and should always be used with {@code equals} formulation.
-     * Evaluates to false when used directly and cannot be written as a parcel.
-     */
-    @NonNull
-    public static final IntegrityFormula PACKAGE_NAME =
-            new StringAtomicFormula(AtomicFormula.PACKAGE_NAME);
+    /** Factory class for creating integrity formulas based on the app being installed. */
+    public static final class Application {
+        /** Returns an integrity formula that checks the equality to a package name. */
+        @NonNull
+        public static IntegrityFormula packageNameEquals(@NonNull String packageName) {
+            return new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, packageName);
+        }
 
-    /**
-     * A static formula base for app certificate formulas.
-     *
-     * This formulation is incomplete and should always be used with {@code equals} formulation.
-     * Evaluates to false when used directly and cannot be written as a parcel.
-     */
-    @NonNull
-    public static final IntegrityFormula APP_CERTIFICATE =
-            new StringAtomicFormula(AtomicFormula.APP_CERTIFICATE);
+        /**
+         * Returns an integrity formula that checks if the app certificates contain {@code
+         * appCertificate}.
+         */
+        @NonNull
+        public static IntegrityFormula certificatesContain(@NonNull String appCertificate) {
+            return new StringAtomicFormula(AtomicFormula.APP_CERTIFICATE, appCertificate);
+        }
 
-    /**
-     * A static formula base for installer name formulas.
-     *
-     * This formulation is incomplete and should always be used with {@code equals} formulation.
-     * Evaluates to false when used directly and cannot be written as a parcel.
-     */
-    @NonNull
-    public static final IntegrityFormula INSTALLER_NAME =
-            new StringAtomicFormula(AtomicFormula.INSTALLER_NAME);
+        /** Returns an integrity formula that checks the equality to a version code. */
+        @NonNull
+        public static IntegrityFormula versionCodeEquals(@NonNull long versionCode) {
+            return new LongAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, versionCode);
+        }
 
-    /**
-     * A static formula base for installer certificate formulas.
-     *
-     * This formulation is incomplete and should always be used with {@code equals} formulation.
-     * Evaluates to false when used directly and cannot be written as a parcel.
-     */
-    @NonNull
-    public static final IntegrityFormula INSTALLER_CERTIFICATE =
-            new StringAtomicFormula(AtomicFormula.INSTALLER_CERTIFICATE);
+        /**
+         * Returns an integrity formula that checks the app's version code is greater than the
+         * provided value.
+         */
+        @NonNull
+        public static IntegrityFormula versionCodeGreaterThan(@NonNull long versionCode) {
+            return new LongAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GT, versionCode);
+        }
 
-    /**
-     * A static formula base for version code name formulas.
-     *
-     * This formulation is incomplete and should always be used with {@code equals},
-     * {@code greaterThan} and {@code greaterThanEquals} formulation. Evaluates to false when used
-     * directly and cannot be written as a parcel.
-     */
-    @NonNull
-    public static final IntegrityFormula VERSION_CODE =
-            new LongAtomicFormula(AtomicFormula.VERSION_CODE);
+        /**
+         * Returns an integrity formula that checks the app's version code is greater than or equal
+         * to the provided value.
+         */
+        @NonNull
+        public static IntegrityFormula versionCodeGreaterThanOrEqualTo(@NonNull long versionCode) {
+            return new LongAtomicFormula(
+                    AtomicFormula.VERSION_CODE, AtomicFormula.GTE, versionCode);
+        }
 
-    /**
-     * A static formula base for pre-installed status formulas.
-     *
-     * This formulation is incomplete and should always be used with {@code equals} formulation.
-     * Evaluates to false when used directly and cannot be written as a parcel.
-     */
-    @NonNull
-    public static final IntegrityFormula PRE_INSTALLED =
-            new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED);
+        /** Returns an integrity formula that is valid when app is pre-installed. */
+        @NonNull
+        public static IntegrityFormula isPreInstalled() {
+            return new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true);
+        }
+
+        private Application() {
+        }
+    }
+
+    /** Factory class for creating integrity formulas based on installer. */
+    public static final class Installer {
+        /** Returns an integrity formula that checks the equality to an installer name. */
+        @NonNull
+        public static IntegrityFormula packageNameEquals(@NonNull String installerName) {
+            return new StringAtomicFormula(AtomicFormula.INSTALLER_NAME, installerName);
+        }
+
+        /**
+         * An static formula that evaluates to true if the installer is NOT allowed according to the
+         * "allowed installer" field in the android manifest.
+         */
+        @NonNull
+        public static IntegrityFormula notAllowedByManifest() {
+            return not(new InstallerAllowedByManifestFormula());
+        }
+
+        /**
+         * Returns an integrity formula that checks if the installer certificates contain {@code
+         * installerCertificate}.
+         */
+        @NonNull
+        public static IntegrityFormula certificatesContain(@NonNull String installerCertificate) {
+            return new StringAtomicFormula(AtomicFormula.INSTALLER_CERTIFICATE,
+                    installerCertificate);
+        }
+
+        private Installer() {
+        }
+    }
 
     /** @hide */
     @IntDef(
@@ -109,10 +131,12 @@
                     COMPOUND_FORMULA_TAG,
                     STRING_ATOMIC_FORMULA_TAG,
                     LONG_ATOMIC_FORMULA_TAG,
-                    BOOLEAN_ATOMIC_FORMULA_TAG
+                    BOOLEAN_ATOMIC_FORMULA_TAG,
+                    INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG
             })
     @Retention(RetentionPolicy.SOURCE)
-    @interface Tag {}
+    @interface Tag {
+    }
 
     /** @hide */
     public static final int COMPOUND_FORMULA_TAG = 0;
@@ -122,6 +146,8 @@
     public static final int LONG_ATOMIC_FORMULA_TAG = 2;
     /** @hide */
     public static final int BOOLEAN_ATOMIC_FORMULA_TAG = 3;
+    /** @hide */
+    public static final int INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG = 4;
 
     /**
      * Returns the tag that identifies the current class.
@@ -135,14 +161,14 @@
      *
      * @hide
      */
-    public abstract @Tag boolean matches(AppInstallMetadata appInstallMetadata);
+    public abstract boolean matches(AppInstallMetadata appInstallMetadata);
 
     /**
      * Returns true when the formula (or one of its atomic formulas) has app certificate as key.
      *
      * @hide
      */
-    public abstract @Tag boolean isAppCertificateFormula();
+    public abstract boolean isAppCertificateFormula();
 
     /**
      * Returns true when the formula (or one of its atomic formulas) has installer package name
@@ -150,7 +176,7 @@
      *
      * @hide
      */
-    public abstract @Tag boolean isInstallerFormula();
+    public abstract boolean isInstallerFormula();
 
     /**
      * Write an {@link IntegrityFormula} to {@link android.os.Parcel}.
@@ -159,7 +185,6 @@
      * {@link Parcelable}.
      *
      * @throws IllegalArgumentException if {@link IntegrityFormula} is not a recognized subclass
-     *
      * @hide
      */
     public static void writeToParcel(
@@ -195,70 +220,6 @@
     }
 
     /**
-     * Returns an integrity formula that evaluates to true when value of the key matches to the
-     * provided string value.
-     *
-     * <p>The value will be hashed with SHA256 and the hex digest will be computed; for
-     * all cases except when the key is PACKAGE_NAME or INSTALLER_NAME and the value is less than
-     * 32 characters.
-     *
-     * <p>Throws an {@link IllegalArgumentException} if the key is not string typed.
-     */
-    @NonNull
-    public IntegrityFormula equalTo(@NonNull String value) {
-        AtomicFormula baseFormula = (AtomicFormula) this;
-        return new AtomicFormula.StringAtomicFormula(baseFormula.getKey(), value);
-    }
-
-    /**
-     * Returns an integrity formula that evaluates to true when the boolean value of the key matches
-     * the provided boolean value. It can only be used with the boolean comparison keys.
-     *
-     * <p>Throws an {@link IllegalArgumentException} if the key is not boolean typed.
-     */
-    @NonNull
-    public IntegrityFormula equalTo(boolean value) {
-        AtomicFormula baseFormula = (AtomicFormula) this;
-        return new AtomicFormula.BooleanAtomicFormula(baseFormula.getKey(), value);
-    }
-
-    /**
-     * Returns a formula that evaluates to true when the value of the key in the package being
-     * installed is equal to {@code value}.
-     *
-     * <p>Throws an {@link IllegalArgumentException} if the key is not long typed.
-     */
-    @NonNull
-    public IntegrityFormula equalTo(long value) {
-        AtomicFormula baseFormula = (AtomicFormula) this;
-        return new AtomicFormula.LongAtomicFormula(baseFormula.getKey(), AtomicFormula.EQ, value);
-    }
-
-    /**
-     * Returns a formula that evaluates to true when the value of the key in the package being
-     * installed is greater than {@code value}.
-     *
-     * <p>Throws an {@link IllegalArgumentException} if the key is not long typed.
-     */
-    @NonNull
-    public IntegrityFormula greaterThan(long value) {
-        AtomicFormula baseFormula = (AtomicFormula) this;
-        return new AtomicFormula.LongAtomicFormula(baseFormula.getKey(), AtomicFormula.GT, value);
-    }
-
-    /**
-     * Returns a formula that evaluates to true when the value of the key in the package being
-     * installed is greater than or equals to the {@code value}.
-     *
-     * <p>Throws an {@link IllegalArgumentException} if the key is not long typed.
-     */
-    @NonNull
-    public IntegrityFormula greaterThanOrEquals(long value) {
-        AtomicFormula baseFormula = (AtomicFormula) this;
-        return new AtomicFormula.LongAtomicFormula(baseFormula.getKey(), AtomicFormula.GTE, value);
-    }
-
-    /**
      * Returns a formula that evaluates to true when any formula in {@code formulae} evaluates to
      * true.
      *
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index b998539..d251ba9 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1389,7 +1389,7 @@
                 pw.println(prefix + "fullBackupContent="
                         + (fullBackupContent < 0 ? "false" : "true"));
             }
-            pw.println("crossProfile=" + (crossProfile ? "true" : "false"));
+            pw.println(prefix + "crossProfile=" + (crossProfile ? "true" : "false"));
             if (networkSecurityConfigRes != 0) {
                 pw.println(prefix + "networkSecurityConfigRes=0x"
                         + Integer.toHexString(networkSecurityConfigRes));
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 2ba2840..50841c3 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -319,14 +319,21 @@
      *
      * <p>If other changes could have affected the app's ability to interact across profiles, as
      * defined by the return value of {@link #canInteractAcrossProfiles()}, such as changes to the
-     * admin or OEM consent whitelists, then {@link
-     * #resetInteractAcrossProfilesAppOpsIfInvalid(List)} should be used.
+     * admin or OEM consent whitelists, then {@link #resetInteractAcrossProfilesAppOps(Collection,
+     * Set)} should be used.
+     *
+     * <p>If the caller does not have the {@link android.Manifest.permission
+     * #CONFIGURE_INTERACT_ACROSS_PROFILES} permission, then they must have the permissions that
+     * would have been required to call {@link android.app.AppOpsManager#setMode(int, int, String,
+     * int)}, which includes {@link android.Manifest.permission#MANAGE_APP_OPS_MODES}.
+     *
+     * <p>Also requires either {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or {@link
+     * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
      *
      * @hide
      */
     @RequiresPermission(
-            allOf={android.Manifest.permission.MANAGE_APP_OPS_MODES,
-                    android.Manifest.permission.UPDATE_APP_OPS_STATS,
+            allOf={android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES,
                     android.Manifest.permission.INTERACT_ACROSS_USERS})
     public void setInteractAcrossProfilesAppOp(@NonNull String packageName, @Mode int newMode) {
         try {
@@ -363,11 +370,18 @@
      * have changed as a result of non-user actions, such as changes to admin or OEM consent
      * whitelists.
      *
+     * <p>If the caller does not have the {@link android.Manifest.permission
+     * #CONFIGURE_INTERACT_ACROSS_PROFILES} permission, then they must have the permissions that
+     * would have been required to call {@link android.app.AppOpsManager#setMode(int, int, String,
+     * int)}, which includes {@link android.Manifest.permission#MANAGE_APP_OPS_MODES}.
+     *
+     * <p>Also requires either {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or {@link
+     * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
+     *
      * @hide
      */
     @RequiresPermission(
-            allOf={android.Manifest.permission.MANAGE_APP_OPS_MODES,
-                    android.Manifest.permission.UPDATE_APP_OPS_STATS,
+            allOf={android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES,
                     android.Manifest.permission.INTERACT_ACROSS_USERS})
     public void resetInteractAcrossProfilesAppOps(
             @NonNull Collection<String> previousCrossProfilePackages,
diff --git a/core/java/android/content/pm/CrossProfileAppsInternal.java b/core/java/android/content/pm/CrossProfileAppsInternal.java
new file mode 100644
index 0000000..9ff4417
--- /dev/null
+++ b/core/java/android/content/pm/CrossProfileAppsInternal.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.annotation.UserIdInt;
+
+/**
+ * Exposes internal methods from {@link com.android.server.pm.CrossProfileAppsServiceImpl} to other
+ * system server classes.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class CrossProfileAppsInternal {
+    /**
+     * Returns whether the package has the necessary permissions to communicate cross-profile.
+     *
+     * <p>This means having at least one of these conditions:
+     * <ul>
+     *     <li>{@code Manifest.permission.INTERACT_ACROSS_USERS_FULL} granted.
+     *     <li>{@code Manifest.permission.INTERACT_ACROSS_USERS} granted.
+     *     <li>{@code Manifest.permission.INTERACT_ACROSS_PROFILES} granted, or the corresponding
+     *     AppOps {@code android:interact_across_profiles} is set to "allow".
+     * </ul>
+     */
+    public abstract boolean verifyPackageHasInteractAcrossProfilePermission(String packageName,
+            @UserIdInt int userId) throws PackageManager.NameNotFoundException;
+
+    /**
+     * Returns whether the package has the necessary permissions to communicate cross-profile.
+     *
+     * <p>This means having at least one of these conditions:
+     * <ul>
+     *     <li>{@code Manifest.permission.INTERACT_ACROSS_USERS_FULL} granted.
+     *     <li>{@code Manifest.permission.INTERACT_ACROSS_USERS} granted.
+     *     <li>{@code Manifest.permission.INTERACT_ACROSS_PROFILES} granted, or the corresponding
+     *     AppOps {@code android:interact_across_profiles} is set to "allow".
+     * </ul>
+     */
+    public abstract boolean verifyUidHasInteractAcrossProfilePermission(String packageName,
+            int uid);
+}
diff --git a/core/java/android/content/pm/InstallationFile.java b/core/java/android/content/pm/InstallationFile.java
index 111ad32..b449945 100644
--- a/core/java/android/content/pm/InstallationFile.java
+++ b/core/java/android/content/pm/InstallationFile.java
@@ -16,82 +16,59 @@
 
 package android.content.pm;
 
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
 /**
  * Defines the properties of a file in an installation session.
- * TODO(b/136132412): update with new APIs.
- *
  * @hide
  */
 @SystemApi
 public final class InstallationFile implements Parcelable {
-    public static final int FILE_TYPE_UNKNOWN = -1;
-    public static final int FILE_TYPE_APK = 0;
-    public static final int FILE_TYPE_LIB = 1;
-    public static final int FILE_TYPE_OBB = 2;
+    private final @PackageInstaller.FileLocation int mLocation;
+    private final @NonNull String mName;
+    private final long mLengthBytes;
+    private final @Nullable byte[] mMetadata;
+    private final @Nullable byte[] mSignature;
 
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"FILE_TYPE_"}, value = {
-            FILE_TYPE_APK,
-            FILE_TYPE_LIB,
-            FILE_TYPE_OBB,
-    })
-    public @interface FileType {
-    }
-
-    private String mFileName;
-    private @FileType int mFileType;
-    private long mFileSize;
-    private byte[] mMetadata;
-
-    public InstallationFile(@NonNull String fileName, long fileSize,
-            @Nullable byte[] metadata) {
-        mFileName = fileName;
-        mFileSize = fileSize;
+    public InstallationFile(@PackageInstaller.FileLocation int location, @NonNull String name,
+            long lengthBytes, @Nullable byte[] metadata, @Nullable byte[] signature) {
+        mLocation = location;
+        mName = name;
+        mLengthBytes = lengthBytes;
         mMetadata = metadata;
-        if (fileName.toLowerCase().endsWith(".apk")) {
-            mFileType = FILE_TYPE_APK;
-        } else if (fileName.toLowerCase().endsWith(".obb")) {
-            mFileType = FILE_TYPE_OBB;
-        } else if (fileName.toLowerCase().endsWith(".so") && fileName.toLowerCase().startsWith(
-                "lib/")) {
-            mFileType = FILE_TYPE_LIB;
-        } else {
-            mFileType = FILE_TYPE_UNKNOWN;
-        }
+        mSignature = signature;
     }
 
-    public @FileType int getFileType() {
-        return mFileType;
+    public @PackageInstaller.FileLocation int getLocation() {
+        return mLocation;
     }
 
     public @NonNull String getName() {
-        return mFileName;
+        return mName;
     }
 
-    public long getSize() {
-        return mFileSize;
+    public long getLengthBytes() {
+        return mLengthBytes;
     }
 
     public @Nullable byte[] getMetadata() {
         return mMetadata;
     }
 
+    public @Nullable byte[] getSignature() {
+        return mSignature;
+    }
+
     private InstallationFile(Parcel source) {
-        mFileName = source.readString();
-        mFileType = source.readInt();
-        mFileSize = source.readLong();
+        mLocation = source.readInt();
+        mName = source.readString();
+        mLengthBytes = source.readLong();
         mMetadata = source.createByteArray();
+        mSignature = source.createByteArray();
     }
 
     @Override
@@ -101,10 +78,11 @@
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeString(mFileName);
-        dest.writeInt(mFileType);
-        dest.writeLong(mFileSize);
+        dest.writeInt(mLocation);
+        dest.writeString(mName);
+        dest.writeLong(mLengthBytes);
         dest.writeByteArray(mMetadata);
+        dest.writeByteArray(mSignature);
     }
 
     public static final @NonNull Creator<InstallationFile> CREATOR =
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 271d5e4..5aa0208 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -16,6 +16,7 @@
 
 package android.content.pm;
 
+import static android.Manifest.permission;
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -601,11 +602,15 @@
     }
 
     /**
-     * Show an error log on logcat, when the calling user is a managed profile, and the target
-     * user is different from the calling user, in order to help developers to detect it.
+     * Show an error log on logcat, when the calling user is a managed profile, the target
+     * user is different from the calling user, and it is not called from a package that has the
+     * {@link permission.INTERACT_ACROSS_USERS_FULL} permission, in order to help
+     * developers to detect it.
      */
     private void logErrorForInvalidProfileAccess(@NonNull UserHandle target) {
-        if (UserHandle.myUserId() != target.getIdentifier() && mUserManager.isManagedProfile()) {
+        if (UserHandle.myUserId() != target.getIdentifier() && mUserManager.isManagedProfile()
+                    && mContext.checkSelfPermission(permission.INTERACT_ACROSS_USERS_FULL)
+                            != PackageManager.PERMISSION_GRANTED) {
             Log.w(TAG, "Accessing other profiles/users from managed profile is no longer allowed.");
         }
     }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e8668f1..c78d30d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3373,15 +3373,16 @@
      * @hide
      */
     @SystemApi
-    public static final int FLAG_PERMISSION_DONT_AUTO_REVOKE = 1 << 17;
+    public static final int FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED = 1 << 17;
 
     /**
-     * Permission flag: Whether {@link #FLAG_PERMISSION_DONT_AUTO_REVOKE} state was set by user.
+     * Permission flag: Whether {@link #FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED} state was set by
+     * user.
      *
      * @hide
      */
     @SystemApi
-    public static final int FLAG_PERMISSION_DONT_AUTO_REVOKE_USER_SET = 1 << 18;
+    public static final int FLAG_PERMISSION_AUTO_REVOKE_USER_SET = 1 << 18;
 
     /**
      * Permission flags: Reserved for use by the permission controller.
@@ -3435,8 +3436,8 @@
             | FLAG_PERMISSION_GRANTED_BY_ROLE
             | FLAG_PERMISSION_REVOKED_COMPAT
             | FLAG_PERMISSION_ONE_TIME
-            | FLAG_PERMISSION_DONT_AUTO_REVOKE
-            | FLAG_PERMISSION_DONT_AUTO_REVOKE_USER_SET;
+            | FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED
+            | FLAG_PERMISSION_AUTO_REVOKE_USER_SET;
 
     /**
      * Injected activity in app that forwards user to setting activity of that app.
@@ -4260,7 +4261,8 @@
             FLAG_PERMISSION_GRANTED_BY_ROLE,
             FLAG_PERMISSION_REVOKED_COMPAT,
             FLAG_PERMISSION_ONE_TIME,
-            FLAG_PERMISSION_DONT_AUTO_REVOKE
+            FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED,
+            FLAG_PERMISSION_AUTO_REVOKE_USER_SET
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface PermissionFlags {}
@@ -7397,8 +7399,8 @@
             case FLAG_PERMISSION_GRANTED_BY_ROLE: return "GRANTED_BY_ROLE";
             case FLAG_PERMISSION_REVOKED_COMPAT: return "REVOKED_COMPAT";
             case FLAG_PERMISSION_ONE_TIME: return "ONE_TIME";
-            case FLAG_PERMISSION_DONT_AUTO_REVOKE: return "DONT_AUTO_REVOKE";
-            case FLAG_PERMISSION_DONT_AUTO_REVOKE_USER_SET: return "DONT_AUTO_REVOKE_USER_SET";
+            case FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED: return "AUTO_REVOKE_IF_UNUSED";
+            case FLAG_PERMISSION_AUTO_REVOKE_USER_SET: return "AUTO_REVOKE_USER_SET";
             default: return Integer.toString(flag);
         }
     }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index b6b27b6..ef3b0c8 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1056,10 +1056,8 @@
      * has changed since the last parse, it's up to callers to do so.
      *
      * @see #parsePackageLite(File, int)
-     * @deprecated use {@link #parseParsedPackage(File, int, boolean)}
      */
     @UnsupportedAppUsage
-    @Deprecated
     public Package parsePackage(File packageFile, int flags, boolean useCaches)
             throws PackageParserException {
         if (packageFile.isDirectory()) {
@@ -1071,10 +1069,8 @@
 
     /**
      * Equivalent to {@link #parsePackage(File, int, boolean)} with {@code useCaches == false}.
-     * @deprecated use {@link #parseParsedPackage(File, int, boolean)}
      */
     @UnsupportedAppUsage
-    @Deprecated
     public Package parsePackage(File packageFile, int flags) throws PackageParserException {
         return parsePackage(packageFile, flags, false /* useCaches */);
     }
@@ -1345,12 +1341,7 @@
      * Note that this <em>does not</em> perform signature verification; that
      * must be done separately in
      * {@link ApkParseUtils#collectCertificates(AndroidPackage, boolean)}.
-     *
-     * @deprecated external callers should move to
-     *             {@link #parseParsedPackage(File, int, boolean)}. Eventually this method will
-     *             be marked private.
      */
-    @Deprecated
     @UnsupportedAppUsage
     public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
         final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
@@ -1547,11 +1538,8 @@
      * Collect certificates from all the APKs described in the given package,
      * populating {@link Package#mSigningDetails}. Also asserts that all APK
      * contents are signed correctly and consistently.
-     *
-     * @deprecated use {@link ApkParseUtils#collectCertificates(AndroidPackage, boolean)}
      */
     @UnsupportedAppUsage
-    @Deprecated
     public static void collectCertificates(Package pkg, boolean skipVerify)
             throws PackageParserException {
         collectCertificatesInternal(pkg, skipVerify);
@@ -6472,9 +6460,8 @@
      * Representation of a full package parsed from APK files on disk. A package
      * consists of a single base APK, and zero or more split APKs.
      *
-     * @deprecated use an {@link AndroidPackage}
+     * Deprecated internally. Use AndroidPackage instead.
      */
-    @Deprecated
     public final static class Package implements Parcelable {
 
         @UnsupportedAppUsage
@@ -7388,10 +7375,6 @@
         };
     }
 
-    /**
-     * @deprecated use a {@link ComponentParseUtils.ParsedComponent}
-     */
-    @Deprecated
     public static abstract class Component<II extends IntentInfo> {
         @UnsupportedAppUsage
         public final ArrayList<II> intents;
@@ -7572,10 +7555,6 @@
         }
     }
 
-    /**
-     * @deprecated use {@link ComponentParseUtils.ParsedPermission}
-     */
-    @Deprecated
     public final static class Permission extends Component<IntentInfo> implements Parcelable {
         @UnsupportedAppUsage
         public final PermissionInfo info;
@@ -7650,10 +7629,6 @@
         };
     }
 
-    /**
-     * @deprecated use {@link ComponentParseUtils.ParsedPermissionGroup}
-     */
-    @Deprecated
     public final static class PermissionGroup extends Component<IntentInfo> implements Parcelable {
         @UnsupportedAppUsage
         public final PermissionGroupInfo info;
@@ -7753,12 +7728,7 @@
         return false;
     }
 
-    /**
-     * @deprecated use {@link PackageInfoUtils#generateApplicationInfo(
-     *      AndroidPackage, int, PackageUserState, int)}
-     */
     @UnsupportedAppUsage
-    @Deprecated
     public static ApplicationInfo generateApplicationInfo(Package p, int flags,
             PackageUserState state) {
         return generateApplicationInfo(p, flags, state, UserHandle.getCallingUserId());
@@ -7815,12 +7785,7 @@
         ai.icon = (sUseRoundIcon && ai.roundIconRes != 0) ? ai.roundIconRes : ai.iconRes;
     }
 
-    /**
-     * @deprecated use {@link PackageInfoUtils#generateApplicationInfo(
-     *      AndroidPackage, int, PackageUserState, int)}
-     */
     @UnsupportedAppUsage
-    @Deprecated
     public static ApplicationInfo generateApplicationInfo(Package p, int flags,
             PackageUserState state, int userId) {
         if (p == null) return null;
@@ -7860,11 +7825,6 @@
         return ai;
     }
 
-    /**
-     * @deprecated use {@link PackageInfoUtils#generateApplicationInfo(
-     *      AndroidPackage, int, PackageUserState, int)}
-     */
-    @Deprecated
     public static ApplicationInfo generateApplicationInfo(ApplicationInfo ai, int flags,
             PackageUserState state, int userId) {
         if (ai == null) return null;
@@ -7884,12 +7844,7 @@
         return ai;
     }
 
-    /**
-     * @deprecated use {@link PackageInfoUtils#generatePermissionInfo(
-     *      ComponentParseUtils.ParsedPermission, int)}
-     */
     @UnsupportedAppUsage
-    @Deprecated
     public static final PermissionInfo generatePermissionInfo(
             Permission p, int flags) {
         if (p == null) return null;
@@ -7901,12 +7856,7 @@
         return pi;
     }
 
-    /**
-     * @deprecated use {@link PackageInfoUtils#generatePermissionGroupInfo(
-     *      ComponentParseUtils.ParsedPermissionGroup, int)}
-     */
     @UnsupportedAppUsage
-    @Deprecated
     public static final PermissionGroupInfo generatePermissionGroupInfo(
             PermissionGroup pg, int flags) {
         if (pg == null) return null;
@@ -7918,10 +7868,6 @@
         return pgi;
     }
 
-    /**
-     * @deprecated use {@link ComponentParseUtils.ParsedActivity}
-     */
-    @Deprecated
     public final static class Activity extends Component<ActivityIntentInfo> implements Parcelable {
         @UnsupportedAppUsage
         public final ActivityInfo info;
@@ -8037,12 +7983,7 @@
         };
     }
 
-    /**
-     * @deprecated use {@link PackageInfoUtils#generateActivityInfo(
-     *      AndroidPackage, ComponentParseUtils.ParsedActivity, int, PackageUserState, int)}
-     */
     @UnsupportedAppUsage
-    @Deprecated
     public static final ActivityInfo generateActivityInfo(Activity a, int flags,
             PackageUserState state, int userId) {
         if (a == null) return null;
@@ -8060,11 +8001,6 @@
         return ai;
     }
 
-    /**
-     * @deprecated use {@link PackageInfoUtils#generateActivityInfo(
-     *      AndroidPackage, ComponentParseUtils.ParsedActivity, int, PackageUserState, int)}
-     */
-    @Deprecated
     public static final ActivityInfo generateActivityInfo(ActivityInfo ai, int flags,
             PackageUserState state, int userId) {
         if (ai == null) return null;
@@ -8078,10 +8014,6 @@
         return ai;
     }
 
-    /**
-     * @deprecated use {@link ComponentParseUtils.ParsedService}
-     */
-    @Deprecated
     public final static class Service extends Component<ServiceIntentInfo> implements Parcelable {
         @UnsupportedAppUsage
         public final ServiceInfo info;
@@ -8143,12 +8075,7 @@
         };
     }
 
-    /**
-     * @deprecated use {@link PackageInfoUtils#generateServiceInfo(
-     * AndroidPackage, ComponentParseUtils.ParsedService, int, PackageUserState, int)}
-     */
     @UnsupportedAppUsage
-    @Deprecated
     public static final ServiceInfo generateServiceInfo(Service s, int flags,
             PackageUserState state, int userId) {
         if (s == null) return null;
@@ -8166,10 +8093,6 @@
         return si;
     }
 
-    /**
-     * @deprecated use {@link ComponentParseUtils.ParsedProvider}
-     */
-    @Deprecated
     public final static class Provider extends Component<ProviderIntentInfo> implements Parcelable {
         @UnsupportedAppUsage
         public final ProviderInfo info;
@@ -8250,12 +8173,7 @@
         };
     }
 
-    /**
-     * @deprecated use {@link PackageInfoUtils#generateProviderInfo(
-     *      AndroidPackage, ComponentParseUtils.ParsedProvider, int, PackageUserState, int)}
-     */
     @UnsupportedAppUsage
-    @Deprecated
     public static final ProviderInfo generateProviderInfo(Provider p, int flags,
             PackageUserState state, int userId) {
         if (p == null) return null;
@@ -8278,10 +8196,6 @@
         return pi;
     }
 
-    /**
-     * @deprecated use {@link ComponentParseUtils.ParsedInstrumentation}
-     */
-    @Deprecated
     public final static class Instrumentation extends Component<IntentInfo> implements
             Parcelable {
         @UnsupportedAppUsage
@@ -8342,12 +8256,7 @@
         };
     }
 
-    /**
-     * @deprecated use {@link PackageInfoUtils#generateInstrumentationInfo(
-     *      ComponentParseUtils.ParsedInstrumentation, int)}
-     */
     @UnsupportedAppUsage
-    @Deprecated
     public static final InstrumentationInfo generateInstrumentationInfo(
             Instrumentation i, int flags) {
         if (i == null) return null;
@@ -8359,10 +8268,6 @@
         return ii;
     }
 
-    /**
-     * @deprecated use {@link ComponentParseUtils.ParsedIntentInfo}
-     */
-    @Deprecated
     public static abstract class IntentInfo extends IntentFilter {
         @UnsupportedAppUsage
         public boolean hasDefault;
@@ -8406,10 +8311,6 @@
         }
     }
 
-    /**
-     * @deprecated use {@link ComponentParseUtils.ParsedActivityIntentInfo}
-     */
-    @Deprecated
     public final static class ActivityIntentInfo extends IntentInfo {
         @UnsupportedAppUsage
         public Activity activity;
@@ -8433,10 +8334,6 @@
         }
     }
 
-    /**
-     * @deprecated use {@link ComponentParseUtils.ParsedServiceIntentInfo}
-     */
-    @Deprecated
     public final static class ServiceIntentInfo extends IntentInfo {
         @UnsupportedAppUsage
         public Service service;
@@ -8460,10 +8357,6 @@
         }
     }
 
-    /**
-     * @deprecated use {@link ComponentParseUtils.ParsedProviderIntentInfo}
-     */
-    @Deprecated
     public static final class ProviderIntentInfo extends IntentInfo {
         @UnsupportedAppUsage
         public Provider provider;
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 8c358cc..6a9e0aa 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -45,6 +45,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
+import android.app.UiModeManager;
 import android.app.WindowConfiguration;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.LocaleProto;
@@ -1975,6 +1976,15 @@
         readFromParcel(source);
     }
 
+
+    /**
+     * Retuns whether the configuration is in night mode
+     * @return true if night mode is active and false otherwise
+     */
+    public boolean isNightModeActive() {
+        return (uiMode & UI_MODE_NIGHT_MASK) == UI_MODE_NIGHT_YES;
+    }
+
     public int compareTo(Configuration that) {
         int n;
         float a = this.fontScale;
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index 5a13651..add67aa 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -132,6 +132,14 @@
     int BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14;
 
     /**
+     * A security vulnerability has been discovered and the sensor is unavailable until a
+     * security update has addressed this issue. This error can be received if for example,
+     * authentication was requested with {@link Authenticators#BIOMETRIC_STRONG}, but the
+     * sensor's strength can currently only meet {@link Authenticators#BIOMETRIC_WEAK}.
+     */
+    int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15;
+
+    /**
      * This constant is only used by SystemUI. It notifies SystemUI that authentication was paused
      * because the authentication attempt was unsuccessful.
      * @hide
diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
index bae0fd3..eafcf52 100644
--- a/core/java/android/hardware/biometrics/BiometricFaceConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
@@ -17,6 +17,7 @@
 package android.hardware.biometrics;
 
 import android.app.KeyguardManager;
+import android.hardware.biometrics.BiometricManager.Authenticators;
 import android.hardware.face.FaceManager;
 
 /**
@@ -36,12 +37,12 @@
      * authentication. Note this is to accommodate people who have limited
      * vision.
      */
-    public static final int FEATURE_REQUIRE_ATTENTION = 1;
+    int FEATURE_REQUIRE_ATTENTION = 1;
     /**
      * Require a diverse set of poses during enrollment. Note this is to
      * accommodate people with limited mobility.
      */
-    public static final int FEATURE_REQUIRE_REQUIRE_DIVERSITY = 2;
+    int FEATURE_REQUIRE_REQUIRE_DIVERSITY = 2;
 
     //
     // Error messages from face authentication hardware during initialization, enrollment,
@@ -50,32 +51,32 @@
     /**
      * The hardware is unavailable. Try again later.
      */
-    public static final int FACE_ERROR_HW_UNAVAILABLE = 1;
+    int FACE_ERROR_HW_UNAVAILABLE = 1;
 
     /**
      * Error state returned when the sensor was unable to process the current image.
      */
-    public static final int FACE_ERROR_UNABLE_TO_PROCESS = 2;
+    int FACE_ERROR_UNABLE_TO_PROCESS = 2;
 
     /**
      * Error state returned when the current request has been running too long. This is intended to
      * prevent programs from waiting for the face authentication sensor indefinitely. The timeout is
      * platform and sensor-specific, but is generally on the order of 30 seconds.
      */
-    public static final int FACE_ERROR_TIMEOUT = 3;
+    int FACE_ERROR_TIMEOUT = 3;
 
     /**
      * Error state returned for operations like enrollment; the operation cannot be completed
      * because there's not enough storage remaining to complete the operation.
      */
-    public static final int FACE_ERROR_NO_SPACE = 4;
+    int FACE_ERROR_NO_SPACE = 4;
 
     /**
      * The operation was canceled because the face authentication sensor is unavailable. For
      * example, this may happen when the user is switched, the device is locked or another pending
      * operation prevents or disables it.
      */
-    public static final int FACE_ERROR_CANCELED = 5;
+    int FACE_ERROR_CANCELED = 5;
 
     /**
      * The {@link FaceManager#remove} call failed. Typically this will happen when the
@@ -83,13 +84,13 @@
      *
      * @hide
      */
-    public static final int FACE_ERROR_UNABLE_TO_REMOVE = 6;
+    int FACE_ERROR_UNABLE_TO_REMOVE = 6;
 
     /**
      * The operation was canceled because the API is locked out due to too many attempts.
      * This occurs after 5 failed attempts, and lasts for 30 seconds.
      */
-    public static final int FACE_ERROR_LOCKOUT = 7;
+    int FACE_ERROR_LOCKOUT = 7;
 
     /**
      * Hardware vendors may extend this list if there are conditions that do not fall under one of
@@ -99,52 +100,62 @@
      * expected to show the error message string if they happen, but are advised not to rely on the
      * message id since they will be device and vendor-specific
      */
-    public static final int FACE_ERROR_VENDOR = 8;
+    int FACE_ERROR_VENDOR = 8;
 
     /**
      * The operation was canceled because FACE_ERROR_LOCKOUT occurred too many times.
      * Face authentication is disabled until the user unlocks with strong authentication
      * (PIN/Pattern/Password)
      */
-    public static final int FACE_ERROR_LOCKOUT_PERMANENT = 9;
+    int FACE_ERROR_LOCKOUT_PERMANENT = 9;
 
     /**
      * The user canceled the operation. Upon receiving this, applications should use alternate
      * authentication (e.g. a password). The application should also provide the means to return
      * to face authentication, such as a "use face authentication" button.
      */
-    public static final int FACE_ERROR_USER_CANCELED = 10;
+    int FACE_ERROR_USER_CANCELED = 10;
 
     /**
      * The user does not have a face enrolled.
      */
-    public static final int FACE_ERROR_NOT_ENROLLED = 11;
+    int FACE_ERROR_NOT_ENROLLED = 11;
 
     /**
      * The device does not have a face sensor. This message will propagate if the calling app
      * ignores the result from PackageManager.hasFeature(FEATURE_FACE) and calls
      * this API anyway. Apps should always check for the feature before calling this API.
      */
-    public static final int FACE_ERROR_HW_NOT_PRESENT = 12;
+    int FACE_ERROR_HW_NOT_PRESENT = 12;
 
     /**
      * The user pressed the negative button. This is a placeholder that is currently only used
      * by the support library.
+     *
      * @hide
      */
-    public static final int FACE_ERROR_NEGATIVE_BUTTON = 13;
+    int FACE_ERROR_NEGATIVE_BUTTON = 13;
 
     /**
      * The device does not have pin, pattern, or password set up. See
      * {@link BiometricPrompt.Builder#setDeviceCredentialAllowed(boolean)} and
      * {@link KeyguardManager#isDeviceSecure()}
      */
-    public static final int BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14;
+    int BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14;
+
+    /**
+     * A security vulnerability has been discovered and the sensor is unavailable until a
+     * security update has addressed this issue. This error can be received if for example,
+     * authentication was requested with {@link Authenticators#BIOMETRIC_STRONG}, but the
+     * sensor's strength can currently only meet {@link Authenticators#BIOMETRIC_WEAK}.
+     * @hide
+     */
+    int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15;
 
     /**
      * @hide
      */
-    public static final int FACE_ERROR_VENDOR_BASE = 1000;
+    int FACE_ERROR_VENDOR_BASE = 1000;
 
     //
     // Image acquisition messages. These will not be sent to the user, since they conflict with
@@ -154,13 +165,13 @@
     /**
      * The image acquired was good.
      */
-    public static final int FACE_ACQUIRED_GOOD = 0;
+    int FACE_ACQUIRED_GOOD = 0;
 
     /**
      * The face image was not good enough to process due to a detected condition.
      * (See {@link #FACE_ACQUIRED_TOO_BRIGHT or @link #FACE_ACQUIRED_TOO_DARK}).
      */
-    public static final int FACE_ACQUIRED_INSUFFICIENT = 1;
+    int FACE_ACQUIRED_INSUFFICIENT = 1;
 
     /**
      * The face image was too bright due to too much ambient light.
@@ -169,7 +180,7 @@
      * The user is expected to take action to retry in better lighting conditions
      * when this is returned.
      */
-    public static final int FACE_ACQUIRED_TOO_BRIGHT = 2;
+    int FACE_ACQUIRED_TOO_BRIGHT = 2;
 
     /**
      * The face image was too dark due to illumination light obscured.
@@ -178,65 +189,65 @@
      * The user is expected to take action to retry in better lighting conditions
      * when this is returned.
      */
-    public static final int FACE_ACQUIRED_TOO_DARK = 3;
+    int FACE_ACQUIRED_TOO_DARK = 3;
 
     /**
      * The detected face is too close to the sensor, and the image can't be processed.
      * The user should be informed to move farther from the sensor when this is returned.
      */
-    public static final int FACE_ACQUIRED_TOO_CLOSE = 4;
+    int FACE_ACQUIRED_TOO_CLOSE = 4;
 
     /**
      * The detected face is too small, as the user might be too far from the sensor.
      * The user should be informed to move closer to the sensor when this is returned.
      */
-    public static final int FACE_ACQUIRED_TOO_FAR = 5;
+    int FACE_ACQUIRED_TOO_FAR = 5;
 
     /**
      * Only the upper part of the face was detected. The sensor field of view is too high.
      * The user should be informed to move up with respect to the sensor when this is returned.
      */
-    public static final int FACE_ACQUIRED_TOO_HIGH = 6;
+    int FACE_ACQUIRED_TOO_HIGH = 6;
 
     /**
      * Only the lower part of the face was detected. The sensor field of view is too low.
      * The user should be informed to move down with respect to the sensor when this is returned.
      */
-    public static final int FACE_ACQUIRED_TOO_LOW = 7;
+    int FACE_ACQUIRED_TOO_LOW = 7;
 
     /**
      * Only the right part of the face was detected. The sensor field of view is too far right.
      * The user should be informed to move to the right with respect to the sensor
      * when this is returned.
      */
-    public static final int FACE_ACQUIRED_TOO_RIGHT = 8;
+    int FACE_ACQUIRED_TOO_RIGHT = 8;
 
     /**
      * Only the left part of the face was detected. The sensor field of view is too far left.
      * The user should be informed to move to the left with respect to the sensor
      * when this is returned.
      */
-    public static final int FACE_ACQUIRED_TOO_LEFT = 9;
+    int FACE_ACQUIRED_TOO_LEFT = 9;
 
     /**
      * The user's eyes have strayed away from the sensor. If this message is sent, the user should
      * be informed to look at the device. If the user can't be found in the frame, one of the other
      * acquisition messages should be sent, e.g. FACE_ACQUIRED_NOT_DETECTED.
      */
-    public static final int FACE_ACQUIRED_POOR_GAZE = 10;
+    int FACE_ACQUIRED_POOR_GAZE = 10;
 
     /**
      * No face was detected in front of the sensor.
      * The user should be informed to point the sensor to a face when this is returned.
      */
-    public static final int FACE_ACQUIRED_NOT_DETECTED = 11;
+    int FACE_ACQUIRED_NOT_DETECTED = 11;
 
     /**
      * Too much motion was detected.
      * The user should be informed to keep their face steady relative to the
      * sensor.
      */
-    public static final int FACE_ACQUIRED_TOO_MUCH_MOTION = 12;
+    int FACE_ACQUIRED_TOO_MUCH_MOTION = 12;
 
     /**
      * The sensor needs to be re-calibrated. This is an unexpected condition, and should only be
@@ -244,20 +255,20 @@
      * requires user intervention, e.g. re-enrolling. The expected response to this message is to
      * direct the user to re-enroll.
      */
-    public static final int FACE_ACQUIRED_RECALIBRATE = 13;
+    int FACE_ACQUIRED_RECALIBRATE = 13;
 
     /**
      * The face is too different from a previous acquisition. This condition
      * only applies to enrollment. This can happen if the user passes the
      * device to someone else in the middle of enrollment.
      */
-    public static final int FACE_ACQUIRED_TOO_DIFFERENT = 14;
+    int FACE_ACQUIRED_TOO_DIFFERENT = 14;
 
     /**
      * The face is too similar to a previous acquisition. This condition only
      * applies to enrollment. The user should change their pose.
      */
-    public static final int FACE_ACQUIRED_TOO_SIMILAR = 15;
+    int FACE_ACQUIRED_TOO_SIMILAR = 15;
 
     /**
      * The magnitude of the pan angle of the user’s face with respect to the sensor’s
@@ -269,7 +280,7 @@
      *
      * The user should be informed to look more directly at the camera.
      */
-    public static final int FACE_ACQUIRED_PAN_TOO_EXTREME = 16;
+    int FACE_ACQUIRED_PAN_TOO_EXTREME = 16;
 
     /**
      * The magnitude of the tilt angle of the user’s face with respect to the sensor’s
@@ -280,7 +291,7 @@
      *
      * The user should be informed to look more directly at the camera.
      */
-    public static final int FACE_ACQUIRED_TILT_TOO_EXTREME = 17;
+    int FACE_ACQUIRED_TILT_TOO_EXTREME = 17;
 
     /**
      * The magnitude of the roll angle of the user’s face with respect to the sensor’s
@@ -292,7 +303,7 @@
      *
      * The user should be informed to look more directly at the camera.
      */
-    public static final int FACE_ACQUIRED_ROLL_TOO_EXTREME = 18;
+    int FACE_ACQUIRED_ROLL_TOO_EXTREME = 18;
 
     /**
      * The user’s face has been obscured by some object.
@@ -300,7 +311,7 @@
      * The user should be informed to remove any objects from the line of sight from
      * the sensor to the user’s face.
      */
-    public static final int FACE_ACQUIRED_FACE_OBSCURED = 19;
+    int FACE_ACQUIRED_FACE_OBSCURED = 19;
 
     /**
      * This message represents the earliest message sent at the beginning of the authentication
@@ -310,12 +321,12 @@
      * The framework will measure latency based on the time between the last START message and the
      * onAuthenticated callback.
      */
-    public static final int FACE_ACQUIRED_START = 20;
+    int FACE_ACQUIRED_START = 20;
 
     /**
      * The sensor is dirty. The user should be informed to clean the sensor.
      */
-    public static final int FACE_ACQUIRED_SENSOR_DIRTY = 21;
+    int FACE_ACQUIRED_SENSOR_DIRTY = 21;
 
     /**
      * Hardware vendors may extend this list if there are conditions that do not fall under one of
@@ -323,10 +334,10 @@
      *
      * @hide
      */
-    public static final int FACE_ACQUIRED_VENDOR = 22;
+    int FACE_ACQUIRED_VENDOR = 22;
 
     /**
      * @hide
      */
-    public static final int FACE_ACQUIRED_VENDOR_BASE = 1000;
+    int FACE_ACQUIRED_VENDOR_BASE = 1000;
 }
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
index 5c74456..46e8cc0 100644
--- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -18,6 +18,7 @@
 
 import android.app.KeyguardManager;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.hardware.biometrics.BiometricManager.Authenticators;
 import android.hardware.fingerprint.FingerprintManager;
 
 /**
@@ -37,32 +38,32 @@
     /**
      * The hardware is unavailable. Try again later.
      */
-    public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
+    int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
 
     /**
      * Error state returned when the sensor was unable to process the current image.
      */
-    public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2;
+    int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2;
 
     /**
      * Error state returned when the current request has been running too long. This is intended to
      * prevent programs from waiting for the fingerprint sensor indefinitely. The timeout is
      * platform and sensor-specific, but is generally on the order of 30 seconds.
      */
-    public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
+    int FINGERPRINT_ERROR_TIMEOUT = 3;
 
     /**
      * Error state returned for operations like enrollment; the operation cannot be completed
      * because there's not enough storage remaining to complete the operation.
      */
-    public static final int FINGERPRINT_ERROR_NO_SPACE = 4;
+    int FINGERPRINT_ERROR_NO_SPACE = 4;
 
     /**
      * The operation was canceled because the fingerprint sensor is unavailable. For example,
      * this may happen when the user is switched, the device is locked or another pending operation
      * prevents or disables it.
      */
-    public static final int FINGERPRINT_ERROR_CANCELED = 5;
+    int FINGERPRINT_ERROR_CANCELED = 5;
 
     /**
      * The {@link FingerprintManager#remove} call failed. Typically this will happen when the
@@ -70,13 +71,13 @@
      *
      * @hide
      */
-    public static final int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6;
+    int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6;
 
     /**
      * The operation was canceled because the API is locked out due to too many attempts.
      * This occurs after 5 failed attempts, and lasts for 30 seconds.
      */
-    public static final int FINGERPRINT_ERROR_LOCKOUT = 7;
+    int FINGERPRINT_ERROR_LOCKOUT = 7;
 
     /**
      * Hardware vendors may extend this list if there are conditions that do not fall under one of
@@ -86,52 +87,63 @@
      * expected to show the error message string if they happen, but are advised not to rely on the
      * message id since they will be device and vendor-specific
      */
-    public static final int FINGERPRINT_ERROR_VENDOR = 8;
+    int FINGERPRINT_ERROR_VENDOR = 8;
 
     /**
      * The operation was canceled because FINGERPRINT_ERROR_LOCKOUT occurred too many times.
      * Fingerprint authentication is disabled until the user unlocks with strong authentication
      * (PIN/Pattern/Password)
      */
-    public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9;
+    int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9;
 
     /**
      * The user canceled the operation. Upon receiving this, applications should use alternate
      * authentication (e.g. a password). The application should also provide the means to return
      * to fingerprint authentication, such as a "use fingerprint" button.
      */
-    public static final int FINGERPRINT_ERROR_USER_CANCELED = 10;
+    int FINGERPRINT_ERROR_USER_CANCELED = 10;
 
     /**
      * The user does not have any fingerprints enrolled.
      */
-    public static final int FINGERPRINT_ERROR_NO_FINGERPRINTS = 11;
+    int FINGERPRINT_ERROR_NO_FINGERPRINTS = 11;
 
     /**
      * The device does not have a fingerprint sensor.
      */
-    public static final int FINGERPRINT_ERROR_HW_NOT_PRESENT = 12;
+    int FINGERPRINT_ERROR_HW_NOT_PRESENT = 12;
 
     /**
      * The user pressed the negative button. This is a placeholder that is currently only used
      * by the support library.
+     *
      * @hide
      */
-    public static final int FINGERPRINT_ERROR_NEGATIVE_BUTTON = 13;
+    int FINGERPRINT_ERROR_NEGATIVE_BUTTON = 13;
 
     /**
      * The device does not have pin, pattern, or password set up. See
      * {@link BiometricPrompt.Builder#setDeviceCredentialAllowed(boolean)} and
      * {@link KeyguardManager#isDeviceSecure()}
+     *
      * @hide
      */
-    public static final int BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14;
+    int BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14;
+
+    /**
+     * A security vulnerability has been discovered and the sensor is unavailable until a
+     * security update has addressed this issue. This error can be received if for example,
+     * authentication was requested with {@link Authenticators#BIOMETRIC_STRONG}, but the
+     * sensor's strength can currently only meet {@link Authenticators#BIOMETRIC_WEAK}.
+     * @hide
+     */
+    public static final int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15;
 
     /**
      * @hide
      */
     @UnsupportedAppUsage
-    public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000;
+    int FINGERPRINT_ERROR_VENDOR_BASE = 1000;
 
     //
     // Image acquisition messages. Must agree with those in fingerprint.h
@@ -140,19 +152,19 @@
     /**
      * The image acquired was good.
      */
-    public static final int FINGERPRINT_ACQUIRED_GOOD = 0;
+    int FINGERPRINT_ACQUIRED_GOOD = 0;
 
     /**
      * Only a partial fingerprint image was detected. During enrollment, the user should be
      * informed on what needs to happen to resolve this problem, e.g. "press firmly on sensor."
      */
-    public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1;
+    int FINGERPRINT_ACQUIRED_PARTIAL = 1;
 
     /**
      * The fingerprint image was too noisy to process due to a detected condition (i.e. dry skin) or
      * a possibly dirty sensor (See {@link #FINGERPRINT_ACQUIRED_IMAGER_DIRTY}).
      */
-    public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2;
+    int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2;
 
     /**
      * The fingerprint image was too noisy due to suspected or detected dirt on the sensor.
@@ -161,13 +173,13 @@
      * (stuck pixels, swaths, etc.). The user is expected to take action to clean the sensor
      * when this is returned.
      */
-    public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3;
+    int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3;
 
     /**
      * The fingerprint image was unreadable due to lack of motion. This is most appropriate for
      * linear array sensors that require a swipe motion.
      */
-    public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4;
+    int FINGERPRINT_ACQUIRED_TOO_SLOW = 4;
 
     /**
      * The fingerprint image was incomplete due to quick motion. While mostly appropriate for
@@ -175,16 +187,29 @@
      * The user should be asked to move the finger slower (linear) or leave the finger on the sensor
      * longer.
      */
-    public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5;
+    int FINGERPRINT_ACQUIRED_TOO_FAST = 5;
 
     /**
      * Hardware vendors may extend this list if there are conditions that do not fall under one of
      * the above categories. Vendors are responsible for providing error strings for these errors.
+     *
      * @hide
      */
-    public static final int FINGERPRINT_ACQUIRED_VENDOR = 6;
+    int FINGERPRINT_ACQUIRED_VENDOR = 6;
+
+    /**
+     * This message represents the earliest message sent at the beginning of the authentication
+     * pipeline. It is expected to be used to measure latency. Note this should be sent whenever
+     * authentication is restarted.
+     * The framework will measure latency based on the time between the last START message and the
+     * onAuthenticated callback.
+     *
+     * @hide
+     */
+    int FINGERPRINT_ACQUIRED_START = 7;
+
     /**
      * @hide
      */
-    public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
+    int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
 }
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 125b676..7d66cae 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -61,10 +61,20 @@
     public static final int BIOMETRIC_ERROR_NO_HARDWARE =
             BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT;
 
+    /**
+     * A security vulnerability has been discovered and the sensor is unavailable until a
+     * security update has addressed this issue. This error can be received if for example,
+     * authentication was requested with {@link Authenticators#BIOMETRIC_STRONG}, but the
+     * sensor's strength can currently only meet {@link Authenticators#BIOMETRIC_WEAK}.
+     */
+    public static final int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED =
+            BiometricConstants.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED;
+
     @IntDef({BIOMETRIC_SUCCESS,
             BIOMETRIC_ERROR_HW_UNAVAILABLE,
             BIOMETRIC_ERROR_NONE_ENROLLED,
-            BIOMETRIC_ERROR_NO_HARDWARE})
+            BIOMETRIC_ERROR_NO_HARDWARE,
+            BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED})
     @interface BiometricError {}
 
     /**
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index c686624..75d4fa7 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -25,7 +25,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
-import android.app.KeyguardManager;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.hardware.face.FaceManager;
@@ -280,12 +279,12 @@
         }
 
         /**
-         * Optional: If enabled, the user will first be prompted to authenticate with biometrics,
-         * but also given the option to authenticate with their device PIN, pattern, or password.
-         * Developers should first check {@link KeyguardManager#isDeviceSecure()} before enabling.
-         * If the device is not secure, {@link BiometricPrompt#BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL}
-         * will be given to {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
-         * Defaults to false.
+         * Optional: If enabled, the user will be given the option to authenticate with their device
+         * PIN, pattern, or password. Developers should first check {@link
+         * BiometricManager#canAuthenticate(int)} for {@link Authenticators#DEVICE_CREDENTIAL}
+         * before enabling. If the device is not secured with a credential,
+         * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} will be invoked
+         * with {@link BiometricPrompt#BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL}. Defaults to false.
          *
          * <p>Note that enabling this option replaces the negative button on the prompt with one
          * that allows the user to authenticate with their device credential, making it an error to
@@ -751,13 +750,13 @@
      * <p>Per the Android CDD, only biometric authenticators that meet or exceed the requirements
      * for <strong>Strong</strong> are permitted to integrate with Keystore to perform related
      * cryptographic operations. Therefore, it is an error to call this method after explicitly
-     * calling {@link Builder#setAllowedAuthenticators(int)} with any value other than
+     * calling {@link Builder#setAllowedAuthenticators(int)} with any biometric strength other than
      * {@link Authenticators#BIOMETRIC_STRONG}.
      *
-     * @throws IllegalArgumentException If any of the arguments are null, if
-     * {@link Builder#setDeviceCredentialAllowed(boolean)} was explicitly set to true, or if
-     * {@link Builder#setAllowedAuthenticators(int)} was explicitly called with any value other than
-     * {@link Authenticators#BIOMETRIC_STRONG}.
+     * @throws IllegalArgumentException If any argument is null, or if the allowed biometric
+     * authenticator strength is explicitly set to {@link Authenticators#BIOMETRIC_WEAK}. Prior to
+     * {@link android.os.Build.VERSION_CODES#R}, this exception is also thrown if
+     * {@link Builder#setDeviceCredentialAllowed(boolean)} was explicitly set to true.
      *
      * @param crypto A cryptographic operation to be unlocked after successful authentication.
      * @param cancel An object that can be used to cancel authentication.
@@ -782,16 +781,11 @@
             throw new IllegalArgumentException("Must supply a callback");
         }
 
-        @Authenticators.Types int authenticators = mBundle.getInt(
+        // Disallow explicitly setting any non-Strong biometric authenticator types.
+        final @Authenticators.Types int authenticators = mBundle.getInt(
                 KEY_AUTHENTICATORS_ALLOWED, Authenticators.BIOMETRIC_STRONG);
-
-        if (mBundle.getBoolean(KEY_ALLOW_DEVICE_CREDENTIAL)
-                || (authenticators & Authenticators.DEVICE_CREDENTIAL) != 0) {
-            throw new IllegalArgumentException("Device credential not supported with crypto");
-        }
-
-        // Disallow any non-Strong biometric authenticator types.
-        if ((authenticators & ~Authenticators.BIOMETRIC_STRONG) != 0) {
+        final int biometricStrength = authenticators & Authenticators.BIOMETRIC_WEAK;
+        if ((biometricStrength & ~Authenticators.BIOMETRIC_STRONG) != 0) {
             throw new IllegalArgumentException("Only Strong biometrics supported with crypto");
         }
 
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index c5b9a43..17c83f3 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -400,7 +400,8 @@
                     try {
                         info.setCameraId(Integer.parseInt(cameraId));
                     } catch (NumberFormatException e) {
-                        Log.e(TAG, "Failed to parse camera Id " + cameraId + " to integer");
+                        // For external camera, reaching here is expected.
+                        Log.v(TAG, "Failed to parse camera Id " + cameraId + " to integer");
                     }
                     boolean hasConcurrentStreams =
                             CameraManagerGlobal.get().cameraIdHasConcurrentStreamsLocked(cameraId);
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 9bef2e1..8f3cb93 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -95,7 +95,7 @@
     }
 
     /**
-     * Retrieves the native CameraMetadata* as a Java long.
+     * Retrieves the native std::shared_ptr<CameraMetadata*>* as a Java long.
      * Returns 0 if mNativeInstance is null.
      *
      * @hide
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index aefe66f..df77f52 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -1680,7 +1680,7 @@
     }
 
     @UnsupportedAppUsage
-    private long mMetadataPtr; // native CameraMetadata*
+    private long mMetadataPtr; // native std::shared_ptr<CameraMetadata>*
 
     private native long nativeAllocate();
     private native long nativeAllocateCopy(CameraMetadataNative other)
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index d67de09..25b84c5 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -766,11 +766,11 @@
      * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission.
      * </p>
      *
-     * @param brightness The brightness value from 0 to 255.
+     * @param brightness The brightness value from 0.0f to 1.0f.
      *
      * @hide Requires signature permission.
      */
-    public void setTemporaryBrightness(int brightness) {
+    public void setTemporaryBrightness(float brightness) {
         mGlobal.setTemporaryBrightness(brightness);
     }
 
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index fd539e8..9d92c89 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -616,7 +616,7 @@
      *
      * @hide Requires signature permission.
      */
-    public void setTemporaryBrightness(int brightness) {
+    public void setTemporaryBrightness(float brightness) {
         try {
             mDm.setTemporaryBrightness(brightness);
         } catch (RemoteException ex) {
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 79a339f..ea2b9e7 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -268,8 +268,9 @@
         // nearby, turning it off temporarily until the object is moved away.
         public boolean useProximitySensor;
 
-        // An override of the screen brightness. Set to -1 is used if there's no override.
-        public int screenBrightnessOverride;
+        // An override of the screen brightness.
+        // Set to PowerManager.BRIGHTNESS_INVALID if there's no override.
+        public float screenBrightnessOverride;
 
         // An override of the screen auto-brightness adjustment factor in the range -1 (dimmer) to
         // 1 (brighter). Set to Float.NaN if there's no override.
@@ -300,18 +301,18 @@
         public boolean blockScreenOn;
 
         // Overrides the policy for adjusting screen brightness and state while dozing.
-        public int dozeScreenBrightness;
         public int dozeScreenState;
+        public float dozeScreenBrightness;
 
         public DisplayPowerRequest() {
             policy = POLICY_BRIGHT;
             useProximitySensor = false;
-            screenBrightnessOverride = -1;
+            screenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT;
             useAutoBrightness = false;
             screenAutoBrightnessAdjustmentOverride = Float.NaN;
             screenLowPowerBrightnessFactor = 0.5f;
             blockScreenOn = false;
-            dozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+            dozeScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
             dozeScreenState = Display.STATE_UNKNOWN;
         }
 
@@ -351,7 +352,8 @@
             return other != null
                     && policy == other.policy
                     && useProximitySensor == other.useProximitySensor
-                    && screenBrightnessOverride == other.screenBrightnessOverride
+                    && floatEquals(screenBrightnessOverride,
+                            other.screenBrightnessOverride)
                     && useAutoBrightness == other.useAutoBrightness
                     && floatEquals(screenAutoBrightnessAdjustmentOverride,
                             other.screenAutoBrightnessAdjustmentOverride)
@@ -360,7 +362,7 @@
                     && blockScreenOn == other.blockScreenOn
                     && lowPowerMode == other.lowPowerMode
                     && boostScreenBrightness == other.boostScreenBrightness
-                    && dozeScreenBrightness == other.dozeScreenBrightness
+                    && floatEquals(dozeScreenBrightness, other.dozeScreenBrightness)
                     && dozeScreenState == other.dozeScreenState;
         }
 
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index ccf221b..d22188e 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -115,7 +115,7 @@
     boolean isMinimalPostProcessingRequested(int displayId);
 
     // Temporarily sets the display brightness.
-    void setTemporaryBrightness(int brightness);
+    void setTemporaryBrightness(float brightness);
 
     // Temporarily sets the auto brightness adjustment factor.
     void setTemporaryAutoBrightnessAdjustment(float adjustment);
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index f6717c7..ea576bc 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -918,7 +918,9 @@
         if (mEnrollmentCallback != null) {
             mEnrollmentCallback.onEnrollmentHelp(clientInfo, msg);
         } else if (mAuthenticationCallback != null) {
-            mAuthenticationCallback.onAuthenticationHelp(clientInfo, msg);
+            if (acquireInfo != BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START) {
+                mAuthenticationCallback.onAuthenticationHelp(clientInfo, msg);
+            }
         }
     }
 
@@ -1050,6 +1052,9 @@
                         return msgArray[vendorCode];
                     }
                 }
+                break;
+            case FINGERPRINT_ACQUIRED_START:
+                return null;
         }
         Slog.w(TAG, "Invalid acquired message: " + acquireInfo + ", " + vendorCode);
         return null;
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 83f01a5..a7b6516 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -16,6 +16,7 @@
 
 package android.hardware.input;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SdkConstant;
@@ -51,6 +52,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.concurrent.Executor;
 import java.util.List;
 
 /**
@@ -1236,6 +1238,32 @@
         }
 
         @Override
+        public boolean isVibrating() {
+            throw new UnsupportedOperationException(
+                "isVibrating not supported in InputDeviceVibrator");
+        }
+
+        @Override
+        public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
+            throw new UnsupportedOperationException(
+                "addVibratorStateListener not supported in InputDeviceVibrator");
+        }
+
+        @Override
+        public void addVibratorStateListener(
+                @NonNull @CallbackExecutor Executor executor,
+                @NonNull OnVibratorStateChangedListener listener) {
+            throw new UnsupportedOperationException(
+                "addVibratorStateListener not supported in InputDeviceVibrator");
+        }
+
+        @Override
+        public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
+            throw new UnsupportedOperationException(
+                "removeVibratorStateListener not supported in InputDeviceVibrator");
+        }
+
+        @Override
         public boolean hasAmplitudeControl() {
             return false;
         }
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
index 3c39d15..d009144 100644
--- a/core/java/android/net/ConnectivityDiagnosticsManager.java
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -252,8 +252,8 @@
                 @NonNull PersistableBundle additionalInfo) {
             mNetwork = network;
             mReportTimestamp = reportTimestamp;
-            mLinkProperties = linkProperties;
-            mNetworkCapabilities = networkCapabilities;
+            mLinkProperties = new LinkProperties(linkProperties);
+            mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
             mAdditionalInfo = additionalInfo;
         }
 
@@ -433,6 +433,12 @@
         /** The detection method used to identify the suspected data stall */
         @DetectionMethod private final int mDetectionMethod;
 
+        /** LinkProperties available on the Network at the reported timestamp */
+        @NonNull private final LinkProperties mLinkProperties;
+
+        /** NetworkCapabilities available on the Network at the reported timestamp */
+        @NonNull private final NetworkCapabilities mNetworkCapabilities;
+
         /** PersistableBundle that may contain additional information on the suspected data stall */
         @NonNull private final PersistableBundle mStallDetails;
 
@@ -446,16 +452,23 @@
          * @param network The Network for which this DataStallReport applies
          * @param reportTimestamp The timestamp for the report
          * @param detectionMethod The detection method used to identify this data stall
+         * @param linkProperties The LinkProperties available on network at reportTimestamp
+         * @param networkCapabilities The NetworkCapabilities available on network at
+         *     reportTimestamp
          * @param stallDetails A PersistableBundle that may contain additional info about the report
          */
         public DataStallReport(
                 @NonNull Network network,
                 long reportTimestamp,
                 @DetectionMethod int detectionMethod,
+                @NonNull LinkProperties linkProperties,
+                @NonNull NetworkCapabilities networkCapabilities,
                 @NonNull PersistableBundle stallDetails) {
             mNetwork = network;
             mReportTimestamp = reportTimestamp;
             mDetectionMethod = detectionMethod;
+            mLinkProperties = new LinkProperties(linkProperties);
+            mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
             mStallDetails = stallDetails;
         }
 
@@ -488,6 +501,26 @@
         }
 
         /**
+         * Returns the LinkProperties available when this report was taken.
+         *
+         * @return LinkProperties available on the Network at the reported timestamp
+         */
+        @NonNull
+        public LinkProperties getLinkProperties() {
+            return new LinkProperties(mLinkProperties);
+        }
+
+        /**
+         * Returns the NetworkCapabilities when this report was taken.
+         *
+         * @return NetworkCapabilities available on the Network at the reported timestamp
+         */
+        @NonNull
+        public NetworkCapabilities getNetworkCapabilities() {
+            return new NetworkCapabilities(mNetworkCapabilities);
+        }
+
+        /**
          * Returns a PersistableBundle with additional info for this report.
          *
          * <p>Gets a bundle with details about the suspected data stall including information
@@ -513,12 +546,20 @@
             return mReportTimestamp == that.mReportTimestamp
                     && mDetectionMethod == that.mDetectionMethod
                     && mNetwork.equals(that.mNetwork)
+                    && mLinkProperties.equals(that.mLinkProperties)
+                    && mNetworkCapabilities.equals(that.mNetworkCapabilities)
                     && persistableBundleEquals(mStallDetails, that.mStallDetails);
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(mNetwork, mReportTimestamp, mDetectionMethod, mStallDetails);
+            return Objects.hash(
+                    mNetwork,
+                    mReportTimestamp,
+                    mDetectionMethod,
+                    mLinkProperties,
+                    mNetworkCapabilities,
+                    mStallDetails);
         }
 
         /** {@inheritDoc} */
@@ -533,6 +574,8 @@
             dest.writeParcelable(mNetwork, flags);
             dest.writeLong(mReportTimestamp);
             dest.writeInt(mDetectionMethod);
+            dest.writeParcelable(mLinkProperties, flags);
+            dest.writeParcelable(mNetworkCapabilities, flags);
             dest.writeParcelable(mStallDetails, flags);
         }
 
@@ -544,6 +587,8 @@
                                 in.readParcelable(null),
                                 in.readLong(),
                                 in.readInt(),
+                                in.readParcelable(null),
+                                in.readParcelable(null),
                                 in.readParcelable(null));
                     }
 
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 96d7a80..2f536ff 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -18,6 +18,7 @@
 
 import static android.os.Process.CLAT_UID;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
@@ -35,6 +36,8 @@
 
 import java.io.CharArrayWriter;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Map;
@@ -100,6 +103,19 @@
      */
     public static final int SET_DBG_VPN_OUT = 1002;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "SET_" }, value = {
+            SET_ALL,
+            SET_DEFAULT,
+            SET_FOREGROUND,
+            SET_DEBUG_START,
+            SET_DBG_VPN_IN,
+            SET_DBG_VPN_OUT
+    })
+    public @interface State {
+    }
+
     /**
      * Include all interfaces when filtering
      * @hide
@@ -120,6 +136,17 @@
     /** {@link #metered} value where metered data is accounted. */
     public static final int METERED_YES = 1;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "METERED_" }, value = {
+            METERED_ALL,
+            METERED_NO,
+            METERED_YES
+    })
+    public @interface Meteredness {
+    }
+
+
     /**
      * {@link #roaming} value to account for all roaming states.
      * @hide
@@ -130,6 +157,16 @@
     /** {@link #roaming} value where roaming data is accounted. */
     public static final int ROAMING_YES = 1;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "ROAMING_" }, value = {
+            ROAMING_ALL,
+            ROAMING_NO,
+            ROAMING_YES
+    })
+    public @interface Roaming {
+    }
+
     /**
      * {@link #onDefaultNetwork} value to account for all default network states.
      * @hide
@@ -140,6 +177,16 @@
     /** {@link #onDefaultNetwork} value to account for usage while the default network. */
     public static final int DEFAULT_NETWORK_YES = 1;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "DEFAULT_NETWORK_" }, value = {
+            DEFAULT_NETWORK_ALL,
+            DEFAULT_NETWORK_NO,
+            DEFAULT_NETWORK_YES
+    })
+    public @interface DefaultNetwork {
+    }
+
     /**
      * Denotes a request for stats at the interface level.
      * @hide
@@ -151,6 +198,15 @@
      */
     public static final int STATS_PER_UID = 1;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "STATS_PER_" }, value = {
+            STATS_PER_IFACE,
+            STATS_PER_UID
+    })
+    public @interface StatsType {
+    }
+
     private static final String CLATD_INTERFACE_PREFIX = "v4-";
     // Delta between IPv4 header (20b) and IPv6 header (40b).
     // Used for correct stats accounting on clatd interfaces.
@@ -263,9 +319,9 @@
                     rxBytes, rxPackets, txBytes, txPackets, operations);
         }
 
-        public Entry(@Nullable String iface, int uid, int set, int tag, int metered, int roaming,
-                 int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets,
-                 long operations) {
+        public Entry(@Nullable String iface, int uid, @State int set, int tag,
+                @Meteredness int metered, @Roaming int roaming, @DefaultNetwork int defaultNetwork,
+                long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
             this.iface = iface;
             this.uid = uid;
             this.set = set;
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 3ae5700..1cefbd9 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -49,6 +49,7 @@
     void goToSleep(long time, int reason, int flags);
     @UnsupportedAppUsage(maxTargetSdk = 28)
     void nap(long time);
+    float getBrightnessConstraint(int constraint);
     @UnsupportedAppUsage
     boolean isInteractive();
     boolean isPowerSaveMode();
diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl
index e201e43..84013e7 100644
--- a/core/java/android/os/IVibratorService.aidl
+++ b/core/java/android/os/IVibratorService.aidl
@@ -18,11 +18,15 @@
 
 import android.os.VibrationEffect;
 import android.os.VibrationAttributes;
+import android.os.IVibratorStateListener;
 
 /** {@hide} */
 interface IVibratorService
 {
     boolean hasVibrator();
+    boolean isVibrating();
+    boolean registerVibratorStateListener(in IVibratorStateListener listener);
+    boolean unregisterVibratorStateListener(in IVibratorStateListener listener);
     boolean hasAmplitudeControl();
     boolean[] areEffectsSupported(in int[] effectIds);
     boolean[] arePrimitivesSupported(in int[] primitiveIds);
diff --git a/core/java/android/os/IVibratorStateListener.aidl b/core/java/android/os/IVibratorStateListener.aidl
new file mode 100644
index 0000000..5ff18a3
--- /dev/null
+++ b/core/java/android/os/IVibratorStateListener.aidl
@@ -0,0 +1,29 @@
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.os;
+
+/**
+ * Listener for vibrator state.
+ * {@hide}
+ */
+oneway interface IVibratorStateListener {
+    /**
+     * Called when a vibrator start/stop vibrating.
+     * @param state the vibrator state.
+     */
+    void onVibrating(in boolean vibrating);
+}
\ No newline at end of file
diff --git a/core/java/android/os/NullVibrator.java b/core/java/android/os/NullVibrator.java
index 1d0f9d3..6d8ab6d 100644
--- a/core/java/android/os/NullVibrator.java
+++ b/core/java/android/os/NullVibrator.java
@@ -39,6 +39,11 @@
     }
 
     @Override
+    public boolean isVibrating() {
+        return false;
+    }
+
+    @Override
     public boolean hasAmplitudeControl() {
         return false;
     }
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index f3d3837..267613f 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -248,8 +248,27 @@
     public static final int BRIGHTNESS_DEFAULT = -1;
 
     /**
+     * Brightness value for an invalid value having been stored.
+     * @hide
+     */
+    public static final int BRIGHTNESS_INVALID = -1;
+
+    //Brightness values for new float implementation:
+    /**
+     * Brightness value for fully on as float.
+     * @hide
+     */
+    public static final float BRIGHTNESS_MAX = 1.0f;
+
+    /**
+     * Brightness value for minimum valid brightness as float.
+     * @hide
+     */
+    public static final float BRIGHTNESS_MIN = 0.0f;
+
+    /**
      * Brightness value for fully off in float.
-     * TODO: rename this to BRIGHTNES_OFF and remove the integer-based constant.
+     * TODO(brightnessfloat): rename this to BRIGHTNES_OFF and remove the integer-based constant.
      * @hide
      */
     public static final float BRIGHTNESS_OFF_FLOAT = -1.0f;
@@ -424,6 +443,69 @@
     /**
      * @hide
      */
+    @IntDef(prefix = { "BRIGHTNESS_CONSTRAINT_TYPE" }, value = {
+            BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM,
+            BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM,
+            BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT,
+            BRIGHTNESS_CONSTRAINT_TYPE_DIM,
+            BRIGHTNESS_CONSTRAINT_TYPE_DOZE,
+            BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR,
+            BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM_VR,
+            BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_VR
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BrightnessConstraint{}
+
+    /**
+     * Brightness constraint type: minimum allowed value.
+     * @hide
+     */
+    public static final int BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM = 0;
+    /**
+     * Brightness constraint type: minimum allowed value.
+     * @hide
+     */
+    public static final int BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM = 1;
+
+    /**
+     * Brightness constraint type: minimum allowed value.
+     * @hide
+     */
+    public static final int BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT = 2;
+
+    /**
+     * Brightness constraint type: minimum allowed value.
+     * @hide
+     */
+    public static final int BRIGHTNESS_CONSTRAINT_TYPE_DIM = 3;
+
+    /**
+     * Brightness constraint type: minimum allowed value.
+     * @hide
+     */
+    public static final int BRIGHTNESS_CONSTRAINT_TYPE_DOZE = 4;
+
+    /**
+     * Brightness constraint type: minimum allowed value.
+     * @hide
+     */
+    public static final int BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR = 5;
+
+    /**
+     * Brightness constraint type: minimum allowed value.
+     * @hide
+     */
+    public static final int BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM_VR = 6;
+
+    /**
+     * Brightness constraint type: minimum allowed value.
+     * @hide
+     */
+    public static final int BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_VR = 7;
+
+    /**
+     * @hide
+     */
     @IntDef(prefix = { "WAKE_REASON_" }, value = {
             WAKE_REASON_UNKNOWN,
             WAKE_REASON_POWER_BUTTON,
@@ -889,6 +971,19 @@
     }
 
     /**
+     * Gets a float screen brightness setting.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public float getBrightnessConstraint(int constraint) {
+        try {
+            return mService.getBrightnessConstraint(constraint);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Creates a new wake lock with the specified level and flags.
      * <p>
      * The {@code levelAndFlags} parameter specifies a wake lock level and optional flags
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 8d09461..dbe3b7b 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -214,6 +214,20 @@
      */
     public static final int FSVERITY_CERT_UID = 1075;
 
+    /**
+     * GID that gives write access to app-private data directories on external
+     * storage (used on devices without sdcardfs only).
+     * @hide
+     */
+    public static final int EXT_DATA_RW_GID = 1078;
+
+    /**
+     * GID that gives write access to app-private OBB directories on external
+     * storage (used on devices without sdcardfs only).
+     * @hide
+     */
+    public static final int EXT_OBB_RW_GID = 1079;
+
     /** {@hide} */
     public static final int NOBODY_UID = 9999;
 
diff --git a/core/java/android/os/StatsServiceManager.java b/core/java/android/os/StatsServiceManager.java
index d032e98..de07e92 100644
--- a/core/java/android/os/StatsServiceManager.java
+++ b/core/java/android/os/StatsServiceManager.java
@@ -18,17 +18,16 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.SystemApi.Client;
 
 /**
  * Provides a way to register and obtain the system service binder objects managed by the stats
  * service.
  *
  * <p> Only the statsd mainline module will be able to access an instance of this class.
- *
- * TODO(b/148225705) Change to @SystemApi(client=MODULE_LIBRARIES) when the build system is ready.
  * @hide
  */
-@SystemApi
+@SystemApi(client = Client.MODULE_LIBRARIES)
 public class StatsServiceManager {
     /**
      * @hide
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index faf4a36..da20c7f 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -16,11 +16,17 @@
 
 package android.os;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.media.AudioAttributes;
+import android.os.IVibratorStateListener;
+import android.util.ArrayMap;
 import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+import java.util.concurrent.Executor;
+import java.util.Objects;
 
 /**
  * Vibrator implementation that controls the main system vibrator.
@@ -32,15 +38,22 @@
 
     private final IVibratorService mService;
     private final Binder mToken = new Binder();
+    private final Context mContext;
+
+    @GuardedBy("mDelegates")
+    private final ArrayMap<OnVibratorStateChangedListener,
+            OnVibratorStateChangedListenerDelegate> mDelegates = new ArrayMap<>();
 
     @UnsupportedAppUsage
     public SystemVibrator() {
+        mContext = null;
         mService = IVibratorService.Stub.asInterface(ServiceManager.getService("vibrator"));
     }
 
     @UnsupportedAppUsage
     public SystemVibrator(Context context) {
         super(context);
+        mContext = context;
         mService = IVibratorService.Stub.asInterface(ServiceManager.getService("vibrator"));
     }
 
@@ -57,6 +70,126 @@
         return false;
     }
 
+    /**
+     * Check whether the vibrator is vibrating.
+     *
+     * @return True if the hardware is vibrating, otherwise false.
+     */
+    @Override
+    public boolean isVibrating() {
+        if (mService == null) {
+            Log.w(TAG, "Failed to vibrate; no vibrator service.");
+            return false;
+        }
+        try {
+            return mService.isVibrating();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return false;
+    }
+
+    private class OnVibratorStateChangedListenerDelegate extends
+            IVibratorStateListener.Stub {
+        private final Executor mExecutor;
+        private final OnVibratorStateChangedListener mListener;
+
+        OnVibratorStateChangedListenerDelegate(@NonNull OnVibratorStateChangedListener listener,
+                @NonNull Executor executor) {
+            mExecutor = executor;
+            mListener = listener;
+        }
+
+        @Override
+        public void onVibrating(boolean isVibrating) {
+            mExecutor.execute(() -> mListener.onVibratorStateChanged(isVibrating));
+        }
+    }
+
+    /**
+     * Adds a listener for vibrator state change. If the listener was previously added and not
+     * removed, this call will be ignored.
+     *
+     * @param listener Listener to be added.
+     * @param executor The {@link Executor} on which the listener's callbacks will be executed on.
+     */
+    @Override
+    public void addVibratorStateListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnVibratorStateChangedListener listener) {
+        Objects.requireNonNull(listener);
+        Objects.requireNonNull(executor);
+        if (mService == null) {
+            Log.w(TAG, "Failed to add vibrate state listener; no vibrator service.");
+            return;
+        }
+
+        synchronized (mDelegates) {
+            // If listener is already registered, reject and return.
+            if (mDelegates.containsKey(listener)) {
+                Log.w(TAG, "Listener already registered.");
+                return;
+            }
+            try {
+                final OnVibratorStateChangedListenerDelegate delegate =
+                        new OnVibratorStateChangedListenerDelegate(listener, executor);
+                if (!mService.registerVibratorStateListener(delegate)) {
+                    Log.w(TAG, "Failed to register vibrate state listener");
+                    return;
+                }
+                mDelegates.put(listener, delegate);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Adds a listener for vibrator state changes. Callbacks will be executed on the main thread.
+     * If the listener was previously added and not removed, this call will be ignored.
+     *
+     * @param listener listener to be added
+     */
+    @Override
+    public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
+        Objects.requireNonNull(listener);
+        if (mContext == null) {
+            Log.w(TAG, "Failed to add vibrate state listener; no vibrator context.");
+            return;
+        }
+        addVibratorStateListener(mContext.getMainExecutor(), listener);
+    }
+
+    /**
+     * Removes the listener for vibrator state changes. If the listener was not previously
+     * registered, this call will do nothing.
+     *
+     * @param listener Listener to be removed.
+     */
+    @Override
+    public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
+        Objects.requireNonNull(listener);
+        if (mService == null) {
+            Log.w(TAG, "Failed to remove vibrate state listener; no vibrator service.");
+            return;
+        }
+        synchronized (mDelegates) {
+            // Check if the listener is registered, otherwise will return.
+            if (mDelegates.containsKey(listener)) {
+                final OnVibratorStateChangedListenerDelegate delegate = mDelegates.get(listener);
+                try {
+                    if (!mService.unregisterVibratorStateListener(delegate)) {
+                        Log.w(TAG, "Failed to unregister vibrate state listener");
+                        return;
+                    }
+                    mDelegates.remove(listener);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+        }
+    }
+
     @Override
     public boolean hasAmplitudeControl() {
         if (mService == null) {
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 3508b70..50cc764 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -177,11 +177,7 @@
     }
 
     /**
-     * Set whether tracing is enabled in this process.  Tracing is disabled shortly after Zygote
-     * initializes and re-enabled after processes fork from Zygote.  This is done because Zygote
-     * has no way to be notified about changes to the tracing tags, and if Zygote ever reads and
-     * caches the tracing tags, forked processes will inherit those stale tags.
-     *
+     * Set whether tracing is enabled in this process.
      * @hide
      */
     public static void setTracingEnabled(boolean enabled, int debugFlags) {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index b202053..6d1f646 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -34,6 +34,7 @@
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.admin.DevicePolicyManager;
+import android.app.PropertyInvalidatedCache;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -2150,16 +2151,38 @@
         return isUserUnlocked(user.getIdentifier());
     }
 
+    private static final String CACHE_KEY_IS_USER_UNLOCKED_PROPERTY =
+            "cache_key.is_user_unlocked";
+
+    private final PropertyInvalidatedCache<Integer, Boolean> mIsUserUnlockedCache =
+            new PropertyInvalidatedCache<Integer, Boolean>(
+                32, CACHE_KEY_IS_USER_UNLOCKED_PROPERTY) {
+                @Override
+                protected Boolean recompute(Integer query) {
+                    try {
+                        return mService.isUserUnlocked(query);
+                    } catch (RemoteException re) {
+                        throw re.rethrowFromSystemServer();
+                    }
+                }
+            };
+
     /** {@hide} */
     @UnsupportedAppUsage
     @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
             Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
     public boolean isUserUnlocked(@UserIdInt int userId) {
-        try {
-            return mService.isUserUnlocked(userId);
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
+        return mIsUserUnlockedCache.query(userId);
+    }
+
+    /** {@hide} */
+    public void disableIsUserUnlockedCache() {
+        mIsUserUnlockedCache.disableLocal();
+    }
+
+    /** {@hide} */
+    public static final void invalidateIsUserUnlockedCache() {
+        PropertyInvalidatedCache.invalidateCache(CACHE_KEY_IS_USER_UNLOCKED_PROPERTY);
     }
 
     /**
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index f055c60..d4da7a8 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -16,11 +16,14 @@
 
 package android.os;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.app.ActivityThread;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -29,6 +32,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
 
 /**
  * Class that operates the vibrator on the device.
@@ -395,4 +399,78 @@
      */
     @RequiresPermission(android.Manifest.permission.VIBRATE)
     public abstract void cancel();
+
+    /**
+     * Check whether the vibrator is vibrating.
+     *
+     * @return True if the hardware is vibrating, otherwise false.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)
+    public boolean isVibrating() {
+        return false;
+    }
+
+    /**
+    * Listener for when the vibrator state has changed.
+    *
+    * @see #addVibratorStateListener
+    * @see #removeVibratorStateListener
+    * @hide
+    */
+    @SystemApi
+    @TestApi
+    public interface OnVibratorStateChangedListener  {
+        /**
+         * Called when the vibrator state has changed.
+         *
+         * @param isVibrating If true, the vibrator has started vibrating. If false,
+         *                    it's stopped vibrating.
+         */
+        void onVibratorStateChanged(boolean isVibrating);
+    }
+
+    /**
+     * Adds a listener for vibrator state changes. Callbacks will be executed on the main thread.
+     * If the listener was previously added and not removed, this call will be ignored.
+     *
+     * @param listener listener to be added
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)
+    public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
+    }
+
+    /**
+     * Adds a listener for vibrator state change. If the listener was previously added and not
+     * removed, this call will be ignored.
+     *
+     * @param listener listener to be added
+     * @param executor executor of listener
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)
+    public void addVibratorStateListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnVibratorStateChangedListener listener) {
+    }
+
+    /**
+     * Removes the listener for vibrator state changes. If the listener was not previously
+     * registered, this call will do nothing.
+     *
+     * @param listener listener to be removed
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)
+    public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
+    }
 }
diff --git a/core/java/android/os/incremental/IIncrementalManager.aidl b/core/java/android/os/incremental/IIncrementalManager.aidl
index b415bc0..be83aae 100644
--- a/core/java/android/os/incremental/IIncrementalManager.aidl
+++ b/core/java/android/os/incremental/IIncrementalManager.aidl
@@ -33,7 +33,4 @@
     boolean startDataLoader(int mountId);
     void showHealthBlockedUI(int mountId);
     void destroyDataLoader(int mountId);
-
-    // fileId is a 16 byte long identifier.
-    void newFileForDataLoader(int mountId, in byte[] fileId, in byte[] metadata);
 }
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl
index 9d98b3b..2dbaea8 100644
--- a/core/java/android/os/incremental/IIncrementalService.aidl
+++ b/core/java/android/os/incremental/IIncrementalService.aidl
@@ -17,6 +17,7 @@
 package android.os.incremental;
 
 import android.content.pm.DataLoaderParamsParcel;
+import android.content.pm.IDataLoaderStatusListener;
 import android.os.incremental.IncrementalNewFileParams;
 
 /** @hide */
@@ -33,7 +34,7 @@
      * Opens or creates a storage given a target path and data loader params. Returns the storage ID.
      */
     int openStorage(in @utf8InCpp String path);
-    int createStorage(in @utf8InCpp String path, in DataLoaderParamsParcel params, int createMode);
+    int createStorage(in @utf8InCpp String path, in DataLoaderParamsParcel params, in IDataLoaderStatusListener listener, int createMode);
     int createLinkedStorage(in @utf8InCpp String path, int otherStorageId, int createMode);
 
     /**
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index 987a53e..3f8c0fe 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -29,10 +29,13 @@
  * @throws IllegalStateException the session is not an Incremental installation session.
  */
 
+import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.DataLoaderParams;
+import android.content.pm.IDataLoaderStatusListener;
 import android.content.pm.InstallationFile;
 import android.text.TextUtils;
 import android.util.Slog;
@@ -72,6 +75,7 @@
     public static IncrementalFileStorages initialize(Context context,
             @NonNull File stageDir,
             @NonNull DataLoaderParams dataLoaderParams,
+            @Nullable IDataLoaderStatusListener dataLoaderStatusListener,
             List<InstallationFile> addedFiles) throws IOException {
         // TODO(b/136132412): sanity check if session should not be incremental
         IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService(
@@ -83,22 +87,29 @@
 
         IncrementalFileStorages result = null;
         try {
-            result = new IncrementalFileStorages(stageDir, incrementalManager, dataLoaderParams);
+            result = new IncrementalFileStorages(stageDir, incrementalManager, dataLoaderParams,
+                    dataLoaderStatusListener);
+
+            if (!addedFiles.isEmpty()) {
+                result.mDefaultStorage.bind(stageDir.getAbsolutePath());
+            }
+
             for (InstallationFile file : addedFiles) {
-                if (file.getFileType() == InstallationFile.FILE_TYPE_APK) {
+                if (file.getLocation() == LOCATION_DATA_APP) {
                     try {
                         result.addApkFile(file);
                     } catch (IOException e) {
                         // TODO(b/146080380): add incremental-specific error code
                         throw new IOException(
-                                "Failed to add and configure Incremental File: " + file.getName(),
-                                e);
+                                "Failed to add file to IncFS: " + file.getName() + ", reason: "
+                                        + e.getMessage(), e.getCause());
                     }
                 } else {
-                    throw new IOException("Unknown file type: " + file.getFileType());
+                    throw new IOException("Unknown file location: " + file.getLocation());
                 }
             }
 
+            // TODO(b/146080380): remove 5 secs wait in startLoading
             if (!result.mDefaultStorage.startLoading()) {
                 // TODO(b/146080380): add incremental-specific error code
                 throw new IOException("Failed to start loading data for Incremental installation.");
@@ -115,7 +126,8 @@
 
     private IncrementalFileStorages(@NonNull File stageDir,
             @NonNull IncrementalManager incrementalManager,
-            @NonNull DataLoaderParams dataLoaderParams) throws IOException {
+            @NonNull DataLoaderParams dataLoaderParams,
+            @Nullable IDataLoaderStatusListener dataLoaderStatusListener) throws IOException {
         mStageDir = stageDir;
         mIncrementalManager = incrementalManager;
         if (dataLoaderParams.getComponentName().getPackageName().equals("local")) {
@@ -132,6 +144,7 @@
             }
             mDefaultStorage = mIncrementalManager.createStorage(mDefaultDir,
                     dataLoaderParams,
+                    dataLoaderStatusListener,
                     IncrementalManager.CREATE_MODE_CREATE
                             | IncrementalManager.CREATE_MODE_TEMPORARY_BIND, false);
         }
@@ -142,13 +155,11 @@
     }
 
     private void addApkFile(@NonNull InstallationFile apk) throws IOException {
-        final String stageDirPath = mStageDir.getAbsolutePath();
-        mDefaultStorage.bind(stageDirPath);
-        String apkName = apk.getName();
-        File targetFile = Paths.get(stageDirPath, apkName).toFile();
+        final String apkName = apk.getName();
+        final File targetFile = new File(mStageDir, apkName);
         if (!targetFile.exists()) {
-            mDefaultStorage.makeFile(apkName, apk.getSize(), null,
-                    apk.getMetadata(), 0, null, null, null);
+            mDefaultStorage.makeFile(apkName, apk.getLengthBytes(), null, apk.getMetadata(),
+                    apk.getSignature());
         }
     }
 
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index ba38268..74a4215 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -22,6 +22,7 @@
 import android.annotation.SystemService;
 import android.content.Context;
 import android.content.pm.DataLoaderParams;
+import android.content.pm.IDataLoaderStatusListener;
 import android.os.RemoteException;
 import android.util.SparseArray;
 
@@ -103,10 +104,11 @@
      */
     @Nullable
     public IncrementalStorage createStorage(@NonNull String path,
-            @NonNull DataLoaderParams params, @CreateMode int createMode,
+            @NonNull DataLoaderParams params, @Nullable IDataLoaderStatusListener listener,
+            @CreateMode int createMode,
             boolean autoStartDataLoader) {
         try {
-            final int id = mService.createStorage(path, params.getData(), createMode);
+            final int id = mService.createStorage(path, params.getData(), listener, createMode);
             if (id < 0) {
                 return null;
             }
@@ -299,7 +301,16 @@
         return nativeIsIncrementalPath(path);
     }
 
+    /**
+     * Returns raw signature for file if it's on Incremental File System.
+     * Unsafe, use only if you are sure what you are doing.
+     */
+    public static @Nullable byte[] unsafeGetFileSignature(@NonNull String path) {
+        return nativeUnsafeGetFileSignature(path);
+    }
+
     /* Native methods */
     private static native boolean nativeIsEnabled();
     private static native boolean nativeIsIncrementalPath(@NonNull String path);
+    private static native byte[] nativeUnsafeGetFileSignature(@NonNull String path);
 }
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index 5df44ff..f4e1f96 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -20,6 +20,8 @@
 import android.annotation.Nullable;
 import android.os.RemoteException;
 
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
 import java.io.File;
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -169,10 +171,11 @@
      * @param path             Relative path of the new file.
      * @param size             Size of the new file in bytes.
      * @param metadata         Metadata bytes.
+     * @param v4signatureBytes Serialized V4SignatureProto.
      */
     public void makeFile(@NonNull String path, long size, @Nullable UUID id,
-            @Nullable byte[] metadata, int hashAlgorithm, @Nullable byte[] rootHash,
-            @Nullable byte[] additionalData, @Nullable byte[] signature) throws IOException {
+            @Nullable byte[] metadata, @Nullable byte[] v4signatureBytes)
+            throws IOException {
         try {
             if (id == null && metadata == null) {
                 throw new IOException("File ID and metadata cannot both be null");
@@ -181,13 +184,7 @@
             params.size = size;
             params.metadata = (metadata == null ? new byte[0] : metadata);
             params.fileId = idToBytes(id);
-            if (hashAlgorithm != 0 || signature != null) {
-                params.signature = new IncrementalSignature();
-                params.signature.hashAlgorithm = hashAlgorithm;
-                params.signature.rootHash = rootHash;
-                params.signature.additionalData = additionalData;
-                params.signature.signature = signature;
-            }
+            params.signature = parseV4Signature(v4signatureBytes);
             int res = mService.makeFile(mId, path, params);
             if (res != 0) {
                 throw new IOException("makeFile() failed with errno " + -res);
@@ -197,6 +194,7 @@
         }
     }
 
+
     /**
      * Creates a file in Incremental storage. The content of the file is mapped from a range inside
      * a source file in the same storage.
@@ -349,6 +347,37 @@
         }
     }
 
+    /**
+     * Returns the metadata object of an IncFs File.
+     *
+     * @param id The file id.
+     * @return Byte array that contains metadata bytes.
+     */
+    @Nullable
+    public byte[] getFileMetadata(@NonNull UUID id) {
+        try {
+            final byte[] rawId = idToBytes(id);
+            return mService.getMetadataById(mId, rawId);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return null;
+        }
+    }
+
+    /**
+     * Informs the data loader service associated with the current storage to start data loader
+     *
+     * @return True if data loader is successfully started.
+     */
+    public boolean startLoading() {
+        try {
+            return mService.startLoading(mId);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return false;
+        }
+    }
+
     private static final int UUID_BYTE_SIZE = 16;
 
     /**
@@ -386,35 +415,44 @@
         return new UUID(msb, lsb);
     }
 
-    /**
-     * Returns the metadata object of an IncFs File.
-     *
-     * @param id The file id.
-     * @return Byte array that contains metadata bytes.
-     */
-    @Nullable
-    public byte[] getFileMetadata(@NonNull UUID id) {
-        try {
-            final byte[] rawId = idToBytes(id);
-            return mService.getMetadataById(mId, rawId);
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-            return null;
-        }
-    }
+    private static final int INCFS_HASH_SHA256 = 1;
+    private static final int INCFS_MAX_HASH_SIZE = 32; // SHA256
+    private static final int INCFS_MAX_ADD_DATA_SIZE = 128;
 
     /**
-     * Informs the data loader service associated with the current storage to start data loader
-     *
-     * @return True if data loader is successfully started.
+     * Deserialize and validate v4 signature bytes.
      */
-    public boolean startLoading() {
-        try {
-            return mService.startLoading(mId);
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-            return false;
+    private static IncrementalSignature parseV4Signature(@Nullable byte[] v4signatureBytes)
+            throws IOException {
+        if (v4signatureBytes == null) {
+            return null;
         }
+
+        final V4Signature signature;
+        try (DataInputStream input = new DataInputStream(
+                new ByteArrayInputStream(v4signatureBytes))) {
+            signature = V4Signature.readFrom(input);
+        }
+
+        final byte[] rootHash = signature.verityRootHash;
+        final byte[] additionalData = signature.v3Digest;
+        final byte[] pkcs7Signature = signature.pkcs7SignatureBlock;
+
+        if (rootHash.length != INCFS_MAX_HASH_SIZE) {
+            throw new IOException("rootHash has to be " + INCFS_MAX_HASH_SIZE + " bytes");
+        }
+        if (additionalData.length > INCFS_MAX_ADD_DATA_SIZE) {
+            throw new IOException(
+                    "additionalData has to be at most " + INCFS_MAX_ADD_DATA_SIZE + " bytes");
+        }
+
+        IncrementalSignature result = new IncrementalSignature();
+        result.hashAlgorithm = INCFS_HASH_SHA256;
+        result.rootHash = rootHash;
+        result.additionalData = additionalData;
+        result.signature = pkcs7Signature;
+
+        return result;
     }
 
     /**
diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java
new file mode 100644
index 0000000..5fadee4
--- /dev/null
+++ b/core/java/android/os/incremental/V4Signature.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.incremental;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+/**
+ * V4 signature fields.
+ * Keep in sync with APKSig master copy.
+ * @hide
+ */
+public class V4Signature {
+    public final byte[] verityRootHash;
+    public final byte[] v3Digest;
+    public final byte[] pkcs7SignatureBlock;
+
+    V4Signature(byte[] verityRootHash, byte[] v3Digest, byte[] pkcs7SignatureBlock) {
+        this.verityRootHash = verityRootHash;
+        this.v3Digest = v3Digest;
+        this.pkcs7SignatureBlock = pkcs7SignatureBlock;
+    }
+
+    static byte[] readBytes(DataInputStream stream) throws IOException {
+        byte[] result = new byte[stream.readInt()];
+        stream.read(result);
+        return result;
+    }
+
+    static V4Signature readFrom(DataInputStream stream) throws IOException {
+        byte[] verityRootHash = readBytes(stream);
+        byte[] v3Digest = readBytes(stream);
+        byte[] pkcs7SignatureBlock = readBytes(stream);
+        return new V4Signature(verityRootHash, v3Digest, pkcs7SignatureBlock);
+    }
+
+    static void writeBytes(DataOutputStream stream, byte[] bytes) throws IOException {
+        stream.writeInt(bytes.length);
+        stream.write(bytes);
+    }
+
+    void writeTo(DataOutputStream stream) throws IOException {
+        writeBytes(stream, this.verityRootHash);
+        writeBytes(stream, this.v3Digest);
+        writeBytes(stream, this.pkcs7SignatureBlock);
+    }
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b9207e5..7c4ec8e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1771,6 +1771,15 @@
             = "android.settings.NOTIFICATION_SETTINGS";
 
     /**
+     * Activity Action: Show notification history screen.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_NOTIFICATION_HISTORY
+            = "android.settings.NOTIFICATION_HISTORY";
+
+    /**
      * Activity Action: Show app listing settings, filtered by those that send notifications.
      *
      * @hide
@@ -2997,6 +3006,7 @@
         private static final HashSet<String> MOVED_TO_SECURE;
         static {
             MOVED_TO_SECURE = new HashSet<>(30);
+            MOVED_TO_SECURE.add(Secure.ADAPTIVE_SLEEP);
             MOVED_TO_SECURE.add(Secure.ANDROID_ID);
             MOVED_TO_SECURE.add(Secure.HTTP_PROXY);
             MOVED_TO_SECURE.add(Secure.LOCATION_PROVIDERS_ALLOWED);
@@ -3915,6 +3925,19 @@
         public static final String SCREEN_BRIGHTNESS_FOR_VR = "screen_brightness_for_vr";
 
         /**
+         * The screen backlight brightness between 0.0f and 1.0f.
+         * @hide
+         */
+        public static final String SCREEN_BRIGHTNESS_FOR_VR_FLOAT =
+                "screen_brightness_for_vr_float";
+
+        /**
+         * The screen backlight brightness between 0.0f and 1.0f.
+         * @hide
+         */
+        public static final String SCREEN_BRIGHTNESS_FLOAT = "screen_brightness_float";
+
+        /**
          * Control whether to enable automatic brightness mode.
          */
         public static final String SCREEN_BRIGHTNESS_MODE = "screen_brightness_mode";
@@ -3939,6 +3962,7 @@
 
         /**
          * Control whether to enable adaptive sleep mode.
+         * @deprecated Use {@link android.provider.Settings.Secure#ADAPTIVE_SLEEP} instead.
          * @hide
          */
         public static final String ADAPTIVE_SLEEP = "adaptive_sleep";
@@ -4727,9 +4751,10 @@
             PUBLIC_SETTINGS.add(DIM_SCREEN);
             PUBLIC_SETTINGS.add(SCREEN_OFF_TIMEOUT);
             PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS);
+            PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS_FLOAT);
             PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS_FOR_VR);
+            PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS_FOR_VR_FLOAT);
             PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS_MODE);
-            PUBLIC_SETTINGS.add(ADAPTIVE_SLEEP);
             PUBLIC_SETTINGS.add(MODE_RINGER_STREAMS_AFFECTED);
             PUBLIC_SETTINGS.add(MUTE_STREAMS_AFFECTED);
             PUBLIC_SETTINGS.add(VIBRATE_ON);
@@ -5783,6 +5808,12 @@
         }
 
         /**
+         * Control whether to enable adaptive sleep mode.
+         * @hide
+         */
+        public static final String ADAPTIVE_SLEEP = "adaptive_sleep";
+
+        /**
          * @deprecated Use {@link android.provider.Settings.Global#DEVELOPMENT_SETTINGS_ENABLED}
          * instead
          */
@@ -6242,16 +6273,19 @@
          * @hide
          */
         public static final String LOCATION_CHANGER = "location_changer";
+
         /**
          * The location changer is unknown or unable to detect.
          * @hide
          */
         public static final int LOCATION_CHANGER_UNKNOWN = 0;
+
         /**
          * Location settings in system settings.
          * @hide
          */
         public static final int LOCATION_CHANGER_SYSTEM_SETTINGS = 1;
+
         /**
          * The location icon in drop down notification drawer.
          * @hide
@@ -6299,6 +6333,14 @@
         public static final int LOCATION_MODE_ON = LOCATION_MODE_HIGH_ACCURACY;
 
         /**
+         * The accuracy in meters used for coarsening location for clients with only the coarse
+         * location permission.
+         *
+         * @hide
+         */
+        public static final String LOCATION_COARSE_ACCURACY_M = "locationCoarseAccuracy";
+
+        /**
          * A flag containing settings used for biometric weak
          * @hide
          */
@@ -10207,7 +10249,9 @@
          *
          * Type: int (0 for false, 1 for true)
          * @hide
+         * @deprecated Use {@link WifiManager#isAutoWakeupEnabled()} instead.
          */
+        @Deprecated
         @SystemApi
         public static final String WIFI_WAKEUP_ENABLED = "wifi_wakeup_enabled";
 
@@ -10243,7 +10287,6 @@
          * enabled state.
          * @hide
          */
-        @SystemApi
         public static final String NETWORK_RECOMMENDATIONS_ENABLED =
                 "network_recommendations_enabled";
 
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 36f3a78..7032825 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -87,7 +87,8 @@
     private final @Nullable UserData mUserData;
     private final @Nullable int[] mCancelIds;
     private final boolean mSupportsInlineSuggestions;
-    private final @Nullable ParceledListSlice<InlinePresentation> mInlineActions;
+    // TODO(b/149240554): revert back to use ParceledListSlice after the bug is resolved.
+    private final @Nullable ArrayList<InlinePresentation> mInlineActions;
 
     private FillResponse(@NonNull Builder builder) {
         mDatasets = (builder.mDatasets != null) ? new ParceledListSlice<>(builder.mDatasets) : null;
@@ -106,8 +107,7 @@
         mUserData = builder.mUserData;
         mCancelIds = builder.mCancelIds;
         mSupportsInlineSuggestions = builder.mSupportsInlineSuggestions;
-        mInlineActions = (builder.mInlineActions != null) ? new ParceledListSlice<>(
-                builder.mInlineActions) : null;
+        mInlineActions = builder.mInlineActions;
     }
 
     /** @hide */
@@ -207,7 +207,7 @@
 
     /** @hide */
     public @Nullable List<InlinePresentation> getInlineActions() {
-        return (mInlineActions != null) ? mInlineActions.getList() : null;
+        return mInlineActions;
     }
 
     /**
@@ -718,7 +718,7 @@
         }
         builder.append(", mSupportInlinePresentations=").append(mSupportsInlineSuggestions);
         if (mInlineActions != null) {
-            builder.append(", mInlineActions=" + mInlineActions.getList());
+            builder.append(", mInlineActions=" + mInlineActions);
         }
         return builder.append("]").toString();
     }
@@ -748,7 +748,7 @@
         parcel.writeParcelableArray(mFieldClassificationIds, flags);
         parcel.writeInt(mFlags);
         parcel.writeIntArray(mCancelIds);
-        parcel.writeParcelable(mInlineActions, flags);
+        parcel.writeTypedList(mInlineActions, flags);
         parcel.writeInt(mRequestId);
     }
 
@@ -804,13 +804,12 @@
             final int[] cancelIds = parcel.createIntArray();
             builder.setPresentationCancelIds(cancelIds);
 
-            final ParceledListSlice<InlinePresentation> inlineActionsSlice = parcel.readParcelable(
-                    null);
-            final List<InlinePresentation> inlineActions =
-                    (inlineActionsSlice != null) ? inlineActionsSlice.getList() : null;
-            final int inlineActionsCount = (inlineActions != null) ? inlineActions.size() : 0;
-            for (int i = 0; i < inlineActionsCount; i++) {
-                builder.addInlineAction(inlineActions.get(i));
+            final List<InlinePresentation> inlineActions = parcel.createTypedArrayList(
+                    InlinePresentation.CREATOR);
+            if (inlineActions != null) {
+                for (InlinePresentation inlineAction : inlineActions) {
+                    builder.addInlineAction(inlineAction);
+                }
             }
 
             final FillResponse response = builder.build();
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index 0edd013..cecfe24 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -339,6 +339,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     public void onDataShareRequest(@NonNull DataShareRequest request,
             @NonNull DataShareCallback callback) {
         if (sVerbose) Log.v(TAG, "onDataShareRequest()");
diff --git a/core/java/android/service/contentcapture/DataShareCallback.java b/core/java/android/service/contentcapture/DataShareCallback.java
index e3c7bb3..5df8a4b 100644
--- a/core/java/android/service/contentcapture/DataShareCallback.java
+++ b/core/java/android/service/contentcapture/DataShareCallback.java
@@ -19,6 +19,7 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 
 import java.util.concurrent.Executor;
 
@@ -32,6 +33,7 @@
  * @hide
  **/
 @SystemApi
+@TestApi
 public interface DataShareCallback {
 
     /** Accept the data share.
diff --git a/core/java/android/service/contentcapture/DataShareReadAdapter.java b/core/java/android/service/contentcapture/DataShareReadAdapter.java
index b9fce68..a481ec8 100644
--- a/core/java/android/service/contentcapture/DataShareReadAdapter.java
+++ b/core/java/android/service/contentcapture/DataShareReadAdapter.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.ParcelFileDescriptor;
 
 /**
@@ -27,6 +28,7 @@
  * @hide
  **/
 @SystemApi
+@TestApi
 public interface DataShareReadAdapter {
 
     /**
diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java
index bc65818..de4c056 100644
--- a/core/java/android/service/controls/ControlsProviderService.java
+++ b/core/java/android/service/controls/ControlsProviderService.java
@@ -35,6 +35,7 @@
 import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.Flow.Publisher;
 import java.util.concurrent.Flow.Subscriber;
@@ -73,6 +74,18 @@
     public abstract void loadAvailableControls(@NonNull Consumer<List<Control>> consumer);
 
     /**
+     * (Optional) The service may be asked to provide a small number of recommended controls, in
+     * order to suggest some controls to the user for favoriting. The controls shall be built using
+     * the stateless builder {@link Control.StatelessBuilder}, followed by an invocation to the
+     * provided consumer to callback to the call originator. If the number of controls
+     * is greater than maxNumber, the list will be truncated.
+     */
+    public void loadSuggestedControls(int maxNumber, @NonNull Consumer<List<Control>> consumer) {
+        // Override to change the default behavior
+        consumer.accept(Collections.emptyList());
+    }
+
+    /**
      * Return a valid Publisher for the given controlIds. This publisher will be asked
      * to provide updates for the given list of controlIds as long as the Subscription
      * is valid.
@@ -104,6 +117,11 @@
                 mHandler.obtainMessage(RequestHandler.MSG_LOAD, cb).sendToTarget();
             }
 
+            public void loadSuggested(int maxNumber, IControlsLoadCallback cb) {
+                LoadMessage msg = new LoadMessage(maxNumber, cb);
+                mHandler.obtainMessage(RequestHandler.MSG_LOAD_SUGGESTED, msg).sendToTarget();
+            }
+
             public void subscribe(List<String> controlIds,
                     IControlsSubscriber subscriber) {
                 SubscribeMessage msg = new SubscribeMessage(controlIds, subscriber);
@@ -128,6 +146,14 @@
         private static final int MSG_LOAD = 1;
         private static final int MSG_SUBSCRIBE = 2;
         private static final int MSG_ACTION = 3;
+        private static final int MSG_LOAD_SUGGESTED = 4;
+
+        /**
+         * This the maximum number of controls that can be loaded via
+         * {@link ControlsProviderService#loadAvailablecontrols}. Anything over this number
+         * will be truncated.
+         */
+        private static final int MAX_NUMBER_OF_CONTROLS_ALLOWED = 1000;
 
         RequestHandler(Looper looper) {
             super(looper);
@@ -137,7 +163,14 @@
             switch(msg.what) {
                 case MSG_LOAD:
                     final IControlsLoadCallback cb = (IControlsLoadCallback) msg.obj;
-                    ControlsProviderService.this.loadAvailableControls(consumerFor(cb));
+                    ControlsProviderService.this.loadAvailableControls(consumerFor(
+                            MAX_NUMBER_OF_CONTROLS_ALLOWED, cb));
+                    break;
+
+                case MSG_LOAD_SUGGESTED:
+                    final LoadMessage lMsg = (LoadMessage) msg.obj;
+                    ControlsProviderService.this.loadSuggestedControls(lMsg.mMaxNumber,
+                            consumerFor(lMsg.mMaxNumber, lMsg.mCb));
                     break;
 
                 case MSG_SUBSCRIBE:
@@ -201,9 +234,15 @@
             };
         }
 
-        private Consumer<List<Control>> consumerFor(IControlsLoadCallback cb) {
+        private Consumer<List<Control>> consumerFor(int maxNumber, IControlsLoadCallback cb) {
             return (@NonNull List<Control> controls) -> {
                 Preconditions.checkNotNull(controls);
+                if (controls.size() > maxNumber) {
+                    Log.w(TAG, "Too many controls. Provided: " + controls.size() + ", Max allowed: "
+                            + maxNumber + ". Truncating the list.");
+                    controls = controls.subList(0, maxNumber);
+                }
+
                 List<Control> list = new ArrayList<>();
                 for (Control control: controls) {
                     if (control == null) {
@@ -268,4 +307,14 @@
             this.mSubscriber = subscriber;
         }
     }
+
+    private static class LoadMessage {
+        final int mMaxNumber;
+        final IControlsLoadCallback mCb;
+
+        LoadMessage(int maxNumber, IControlsLoadCallback cb) {
+            this.mMaxNumber = maxNumber;
+            this.mCb = cb;
+        }
+    }
 }
diff --git a/core/java/android/service/controls/IControlsProvider.aidl b/core/java/android/service/controls/IControlsProvider.aidl
index 4ce658e..4375fbb 100644
--- a/core/java/android/service/controls/IControlsProvider.aidl
+++ b/core/java/android/service/controls/IControlsProvider.aidl
@@ -27,6 +27,8 @@
 oneway interface IControlsProvider {
     void load(IControlsLoadCallback cb);
 
+    void loadSuggested(int maxNumber, IControlsLoadCallback cb);
+
     void subscribe(in List<String> controlIds,
              IControlsSubscriber subscriber);
 
diff --git a/core/java/android/service/dataloader/DataLoaderService.java b/core/java/android/service/dataloader/DataLoaderService.java
index c215778..4190001 100644
--- a/core/java/android/service/dataloader/DataLoaderService.java
+++ b/core/java/android/service/dataloader/DataLoaderService.java
@@ -90,7 +90,7 @@
      * @hide
      */
     @SystemApi
-    public @Nullable DataLoader onCreateDataLoader() {
+    public @Nullable DataLoader onCreateDataLoader(@NonNull DataLoaderParams dataLoaderParams) {
         return null;
     }
 
@@ -240,7 +240,7 @@
     private native boolean nativeDestroyDataLoader(int storageId);
 
     private native boolean nativePrepareImage(int storageId,
-            Collection<InstallationFile> addedFiles, Collection<String> removedFiles);
+            List<InstallationFile> addedFiles, List<String> removedFiles);
 
     private static native void nativeWriteData(long nativeInstance, String name, long offsetBytes,
             long lengthBytes, ParcelFileDescriptor incomingFd);
diff --git a/core/java/android/telephony/WapPushManagerConnector.java b/core/java/android/telephony/WapPushManagerConnector.java
deleted file mode 100644
index a9df506..0000000
--- a/core/java/android/telephony/WapPushManagerConnector.java
+++ /dev/null
@@ -1,178 +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 android.telephony;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SdkConstant;
-import android.annotation.SystemApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.IBinder;
-import android.os.RemoteException;
-
-import com.android.internal.telephony.IWapPushManager;
-import com.android.internal.util.Preconditions;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * APIs for platform to connect to the WAP push manager service.
- *
- * <p>To start connection, {@link #bindToWapPushManagerService} should be called.
- *
- * <p>Upon completion {@link #unbindWapPushManagerService} should be called to unbind the service.
- *
- * @hide
- */
-@SystemApi
-public final class WapPushManagerConnector {
-    private final Context mContext;
-
-    private volatile WapPushManagerConnection mConnection;
-    private volatile IWapPushManager mWapPushManager;
-    private String mWapPushManagerPackage;
-
-    /**
-     * The {@link android.content.Intent} that must be declared as handled by the
-     * WAP push manager service.
-     * @hide
-     */
-    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
-    public static final String SERVICE_INTERFACE =
-            "com.android.internal.telephony.IWapPushManager";
-
-    /** @hide */
-    @IntDef(flag = true, prefix = {"RESULT_"}, value = {
-            RESULT_MESSAGE_HANDLED,
-            RESULT_APP_QUERY_FAILED,
-            RESULT_SIGNATURE_NO_MATCH,
-            RESULT_INVALID_RECEIVER_NAME,
-            RESULT_EXCEPTION_CAUGHT,
-            RESULT_FURTHER_PROCESSING,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ProcessMessageResult{}
-
-    /** {@link #processMessage} return value: Message is handled. */
-    public static final int RESULT_MESSAGE_HANDLED = 0x1;
-    /** {@link #processMessage} return value: Application ID or content type was not found. */
-    public static final int RESULT_APP_QUERY_FAILED = 0x2;
-    /** {@link #processMessage} return value: Receiver application signature check failed. */
-    public static final int RESULT_SIGNATURE_NO_MATCH = 0x4;
-    /** {@link #processMessage} return value: Receiver application was not found. */
-    public static final int RESULT_INVALID_RECEIVER_NAME = 0x8;
-    /** {@link #processMessage} return value: Unknown exception. */
-    public static final int RESULT_EXCEPTION_CAUGHT = 0x10;
-    /** {@link #processMessage} return value: further processing needed. */
-    public static final int RESULT_FURTHER_PROCESSING = 0x8000;
-
-    /** The application package name of the WAP push manager service. */
-    private static final String SERVICE_PACKAGE = "com.android.smspush";
-
-    public WapPushManagerConnector(@NonNull Context context) {
-        mContext = context;
-    }
-
-    /**
-     * Binds to the WAP push manager service. This method should be called exactly once.
-     *
-     * @return {@code true} upon successfully binding to a service, {@code false} otherwise
-     */
-    public boolean bindToWapPushManagerService() {
-        Preconditions.checkState(mConnection == null);
-
-        Intent intent = new Intent(SERVICE_INTERFACE);
-        ComponentName component = intent.resolveSystemService(mContext.getPackageManager(), 0);
-        intent.setComponent(component);
-        mConnection = new WapPushManagerConnection();
-        if (component != null
-                && mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
-            mWapPushManagerPackage = component.getPackageName();
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Returns the package name of WAP push manager service application connected to,
-     * or {@code null} if not connected.
-     */
-    @Nullable
-    public String getConnectedWapPushManagerServicePackage() {
-        return mWapPushManagerPackage;
-    }
-
-    /**
-     * Processes WAP push message and triggers the {@code intent}.
-     *
-     * @see RESULT_MESSAGE_HANDLED
-     * @see RESULT_APP_QUERY_FAILED
-     * @see RESULT_SIGNATURE_NO_MATCH
-     * @see RESULT_INVALID_RECEIVER_NAME
-     * @see RESULT_EXCEPTION_CAUGHT
-     * @see RESULT_FURTHER_PROCESSING
-     */
-    @ProcessMessageResult
-    public int processMessage(
-            @NonNull String applicationId, @NonNull String contentType, @NonNull Intent intent) {
-        try {
-            return mWapPushManager.processMessage(applicationId, contentType, intent);
-        } catch (NullPointerException | RemoteException e) {
-            return RESULT_EXCEPTION_CAUGHT;
-        }
-    }
-
-    /**
-     * Unbinds the WAP push manager service. This method should be called exactly once.
-     */
-    public void unbindWapPushManagerService() {
-        Preconditions.checkNotNull(mConnection);
-
-        mContext.unbindService(mConnection);
-        mConnection = null;
-    }
-
-    private class WapPushManagerConnection implements ServiceConnection {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            // Because we have bound to an explicit
-            // service that is running in our own process, we can
-            // cast its IBinder to a concrete class and directly access it.
-            mWapPushManager = IWapPushManager.Stub.asInterface(service);
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            mWapPushManager = null;
-        }
-
-        @Override
-        public void onNullBinding(ComponentName name) {
-            onServiceDisconnected(name);
-        }
-
-        @Override
-        public void onBindingDied(ComponentName name) {
-            onServiceDisconnected(name);
-        }
-    }
-}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 5fdac81..afeb6c3 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -50,6 +50,8 @@
     /** @hide */
     public static final String SETTINGS_DO_NOT_RESTORE_PRESERVED =
             "settings_do_not_restore_preserved";
+    /** @hide */
+    public static final String SETTINGS_SCHEDULES_FLAG = "settings_schedules";
 
     private static final Map<String, String> DEFAULT_FLAGS;
 
@@ -76,6 +78,7 @@
         DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "false");
 
         DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
+        DEFAULT_FLAGS.put(SETTINGS_SCHEDULES_FLAG, "false");
     }
 
     /**
diff --git a/core/java/android/view/AccessibilityEmbeddedConnection.java b/core/java/android/view/AccessibilityEmbeddedConnection.java
new file mode 100644
index 0000000..cc1e501
--- /dev/null
+++ b/core/java/android/view/AccessibilityEmbeddedConnection.java
@@ -0,0 +1,81 @@
+/*
+ * 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 android.view;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Matrix;
+import android.os.IBinder;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityEmbeddedConnection;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * This class is an interface this ViewRootImpl provides to the host view to the latter
+ * can interact with the view hierarchy in SurfaceControlViewHost.
+ *
+ * @hide
+ */
+final class AccessibilityEmbeddedConnection extends IAccessibilityEmbeddedConnection.Stub {
+    private final WeakReference<ViewRootImpl> mViewRootImpl;
+
+    AccessibilityEmbeddedConnection(ViewRootImpl viewRootImpl) {
+        mViewRootImpl = new WeakReference<>(viewRootImpl);
+    }
+
+    @Override
+    public @Nullable IBinder associateEmbeddedHierarchy(@NonNull IBinder host, int hostViewId) {
+        final ViewRootImpl viewRootImpl = mViewRootImpl.get();
+        if (viewRootImpl != null) {
+            final AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(
+                    viewRootImpl.mContext);
+            viewRootImpl.mAttachInfo.mLeashedParentToken = host;
+            viewRootImpl.mAttachInfo.mLeashedParentAccessibilityViewId = hostViewId;
+            if (accessibilityManager.isEnabled()) {
+                accessibilityManager.associateEmbeddedHierarchy(host, viewRootImpl.mLeashToken);
+            }
+            return viewRootImpl.mLeashToken;
+        }
+        return null;
+    }
+
+    @Override
+    public void disassociateEmbeddedHierarchy() {
+        final ViewRootImpl viewRootImpl = mViewRootImpl.get();
+        if (viewRootImpl != null) {
+            final AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(
+                    viewRootImpl.mContext);
+            viewRootImpl.mAttachInfo.mLeashedParentToken = null;
+            viewRootImpl.mAttachInfo.mLeashedParentAccessibilityViewId = View.NO_ID;
+            viewRootImpl.mAttachInfo.mLocationInParentDisplay.set(0, 0);
+            if (accessibilityManager.isEnabled()) {
+                accessibilityManager.disassociateEmbeddedHierarchy(viewRootImpl.mLeashToken);
+            }
+        }
+    }
+
+    @Override
+    public void setScreenMatrix(float[] matrixValues) {
+        final ViewRootImpl viewRootImpl = mViewRootImpl.get();
+        if (viewRootImpl != null) {
+            // TODO(b/148821260): Implement the rest of matrix values.
+            viewRootImpl.mAttachInfo.mLocationInParentDisplay.set(
+                    (int) matrixValues[Matrix.MTRANS_X], (int) matrixValues[Matrix.MTRANS_Y]);
+        }
+    }
+}
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 203b087..3ca84c1 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -855,6 +855,36 @@
         return mViewRootImpl.mAttachInfo.mLocationInParentDisplay.equals(0, 0);
     }
 
+    private void associateLeashedParentIfNeeded(List<AccessibilityNodeInfo> infos) {
+        if (infos == null || shouldBypassAssociateLeashedParent()) {
+            return;
+        }
+        final int infoCount = infos.size();
+        for (int i = 0; i < infoCount; i++) {
+            final AccessibilityNodeInfo info = infos.get(i);
+            associateLeashedParentIfNeeded(info);
+        }
+    }
+
+    private void associateLeashedParentIfNeeded(AccessibilityNodeInfo info) {
+        if (info == null || shouldBypassAssociateLeashedParent()) {
+            return;
+        }
+        // The node id of root node in embedded maybe not be ROOT_NODE_ID so we compare the id
+        // with root view.
+        if (mViewRootImpl.mView.getAccessibilityViewId()
+                != AccessibilityNodeInfo.getAccessibilityViewId(info.getSourceNodeId())) {
+            return;
+        }
+        info.setLeashedParent(mViewRootImpl.mAttachInfo.mLeashedParentToken,
+                mViewRootImpl.mAttachInfo.mLeashedParentAccessibilityViewId);
+    }
+
+    private boolean shouldBypassAssociateLeashedParent() {
+        return (mViewRootImpl.mAttachInfo.mLeashedParentToken == null
+                && mViewRootImpl.mAttachInfo.mLeashedParentAccessibilityViewId == View.NO_ID);
+    }
+
     private void applyAppScaleAndMagnificationSpecIfNeeded(AccessibilityNodeInfo info,
             MagnificationSpec spec) {
         if (info == null) {
@@ -914,6 +944,7 @@
             MagnificationSpec spec, Region interactiveRegion) {
         try {
             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+            associateLeashedParentIfNeeded(infos);
             adjustBoundsInScreenIfNeeded(infos);
             // To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node,
             // then impact the visibility result, we need to adjust visibility before apply scale.
@@ -935,6 +966,7 @@
             MagnificationSpec spec, Region interactiveRegion) {
         try {
             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+            associateLeashedParentIfNeeded(info);
             adjustBoundsInScreenIfNeeded(info);
             // To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node,
             // then impact the visibility result, we need to adjust visibility before apply scale.
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index f9a023f..1730347 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -97,6 +97,8 @@
 
     IWindowSession openSession(in IWindowSessionCallback callback);
 
+    boolean useBLAST();
+
     @UnsupportedAppUsage
     void getInitialDisplaySize(int displayId, out Point size);
     @UnsupportedAppUsage
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 54de1bb..2548068 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -32,6 +32,7 @@
 import android.annotation.Nullable;
 import android.graphics.Insets;
 import android.graphics.Rect;
+import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.util.ArraySet;
@@ -39,7 +40,6 @@
 import android.util.Pair;
 import android.util.Property;
 import android.util.SparseArray;
-import android.view.InputDevice.MotionRange;
 import android.view.InsetsSourceConsumer.ShowResult;
 import android.view.InsetsState.InternalInsetsType;
 import android.view.SurfaceControl.Transaction;
@@ -242,13 +242,15 @@
 
         PendingControlRequest(@InsetsType int types, WindowInsetsAnimationControlListener listener,
                 long durationMs, Interpolator interpolator, @AnimationType int animationType,
-                @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
+                @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
+                CancellationSignal cancellationSignal) {
             this.types = types;
             this.listener = listener;
             this.durationMs = durationMs;
             this.interpolator = interpolator;
             this.animationType = animationType;
             this.layoutInsetsDuringAnimation = layoutInsetsDuringAnimation;
+            this.cancellationSignal = cancellationSignal;
         }
 
         final @InsetsType int types;
@@ -257,6 +259,7 @@
         final Interpolator interpolator;
         final @AnimationType int animationType;
         final @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation;
+        final CancellationSignal cancellationSignal;
     }
 
     private final String TAG = "InsetsControllerImpl";
@@ -455,10 +458,13 @@
             PendingControlRequest pendingRequest = mPendingImeControlRequest;
             mPendingImeControlRequest = null;
             mHandler.removeCallbacks(mPendingControlTimeout);
-            controlAnimationUnchecked(pendingRequest.types, pendingRequest.listener, mFrame,
+            CancellationSignal cancellationSignal = controlAnimationUnchecked(
+                    pendingRequest.types,
+                    pendingRequest.listener, mFrame,
                     true /* fromIme */, pendingRequest.durationMs, pendingRequest.interpolator,
                     false /* fade */, pendingRequest.animationType,
                     pendingRequest.layoutInsetsDuringAnimation);
+            pendingRequest.cancellationSignal.setOnCancelListener(cancellationSignal::cancel);
             return;
         }
 
@@ -504,35 +510,39 @@
     }
 
     @Override
-    public void controlWindowInsetsAnimation(@InsetsType int types, long durationMs,
+    public CancellationSignal controlWindowInsetsAnimation(@InsetsType int types, long durationMs,
             @Nullable Interpolator interpolator,
             @NonNull WindowInsetsAnimationControlListener listener) {
-        controlWindowInsetsAnimation(types, listener, false /* fromIme */, durationMs, interpolator,
-                ANIMATION_TYPE_USER);
+        return controlWindowInsetsAnimation(types, listener, false /* fromIme */, durationMs,
+                interpolator, ANIMATION_TYPE_USER);
     }
 
-    private void controlWindowInsetsAnimation(@InsetsType int types,
+    private CancellationSignal controlWindowInsetsAnimation(@InsetsType int types,
             WindowInsetsAnimationControlListener listener, boolean fromIme, long durationMs,
             @Nullable Interpolator interpolator, @AnimationType int animationType) {
         // If the frame of our window doesn't span the entire display, the control API makes very
         // little sense, as we don't deal with negative insets. So just cancel immediately.
         if (!mState.getDisplayFrame().equals(mFrame)) {
             listener.onCancelled();
-            return;
+            CancellationSignal cancellationSignal = new CancellationSignal();
+            cancellationSignal.cancel();
+            return cancellationSignal;
         }
-        controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, interpolator,
+        return controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, interpolator,
                 false /* fade */, animationType, getLayoutInsetsDuringAnimationMode(types));
     }
 
-    private void controlAnimationUnchecked(@InsetsType int types,
+    private CancellationSignal controlAnimationUnchecked(@InsetsType int types,
             WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme,
             long durationMs, Interpolator interpolator, boolean fade,
             @AnimationType int animationType,
             @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
+        CancellationSignal cancellationSignal = new CancellationSignal();
         if (types == 0) {
             // nothing to animate.
             listener.onCancelled();
-            return;
+            cancellationSignal.cancel();
+            return cancellationSignal;
         }
         cancelExistingControllers(types);
 
@@ -546,21 +556,31 @@
         if (!imeReady) {
             // IME isn't ready, all requested types will be animated once IME is ready
             abortPendingImeControlRequest();
-            mPendingImeControlRequest = new PendingControlRequest(types, listener, durationMs,
-                    interpolator, animationType, layoutInsetsDuringAnimation);
+            final PendingControlRequest request = new PendingControlRequest(types,
+                    listener, durationMs,
+                    interpolator, animationType, layoutInsetsDuringAnimation, cancellationSignal);
+            mPendingImeControlRequest = request;
             mHandler.postDelayed(mPendingControlTimeout, PENDING_CONTROL_TIMEOUT_MS);
-            return;
+            cancellationSignal.setOnCancelListener(() -> {
+                if (mPendingImeControlRequest == request) {
+                    abortPendingImeControlRequest();
+                }
+            });
+            return cancellationSignal;
         }
 
         if (typesReady == 0) {
             listener.onCancelled();
-            return;
+            cancellationSignal.cancel();
+            return cancellationSignal;
         }
 
         final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(controls,
                 frame, mState, listener, typesReady, this, durationMs, interpolator, fade,
                 layoutInsetsDuringAnimation);
         mRunningAnimations.add(new RunningAnimation(controller, animationType));
+        cancellationSignal.setOnCancelListener(controller::onCancelled);
+        return cancellationSignal;
     }
 
     /**
@@ -680,7 +700,14 @@
             }
             mApplier = new SyncRtSurfaceTransactionApplier(mViewRoot.mView);
         }
-        mApplier.scheduleApply(params);
+        if (mViewRoot.mView.isHardwareAccelerated()) {
+            mApplier.scheduleApply(params);
+        } else {
+            // Window doesn't support hardware acceleration, no synchronization for now.
+            // TODO(b/149342281): use mViewRoot.mSurface.getNextFrameNumber() to sync on every
+            //  frame instead.
+            mApplier.applyParams(new Transaction(), -1 /* frame */, params);
+        }
     }
 
     void notifyControlRevoked(InsetsSourceConsumer consumer) {
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index d4961ea..6caa4fe 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -198,7 +198,7 @@
         return "InsetsSource: {"
                 + "mType=" + InsetsState.typeToString(mType)
                 + ", mFrame=" + mFrame.toShortString()
-                + ", mVisible" + mVisible
+                + ", mVisible=" + mVisible
                 + "}";
     }
 
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 78a080d..4ac6a66 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -258,14 +258,14 @@
      */
     public void release() {
         synchronized (mLock) {
-            if (mNativeObject != 0) {
-                nativeRelease(mNativeObject);
-                setNativeObjectLocked(0);
-            }
             if (mHwuiContext != null) {
                 mHwuiContext.destroy();
                 mHwuiContext = null;
             }
+            if (mNativeObject != 0) {
+                nativeRelease(mNativeObject);
+                setNativeObjectLocked(0);
+            }
         }
     }
 
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index b1c354f..d5ed36b 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -22,7 +22,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.CompatibilityInfo.Translator;
@@ -43,8 +42,8 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.SurfaceControl.Transaction;
-import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.SurfaceControlViewHost;
+import android.view.accessibility.AccessibilityNodeInfo;
 
 import com.android.internal.view.SurfaceCallbackHelper;
 
@@ -386,7 +385,7 @@
                  * This gets called on a RenderThread worker thread, so members accessed here must
                  * be protected by a lock.
                  */
-                final boolean useBLAST = WindowManagerGlobal.USE_BLAST_ADAPTER;
+                final boolean useBLAST = WindowManagerGlobal.useBLAST();
                 viewRoot.registerRtFrameCallback(frame -> {
                     try {
                         final SurfaceControl.Transaction t = useBLAST ?
@@ -1120,7 +1119,7 @@
 
     private void applySurfaceTransforms(SurfaceControl surface, SurfaceControl.Transaction t,
             Rect position, long frameNumber) {
-        if (frameNumber > 0 && !WindowManagerGlobal.USE_BLAST_ADAPTER) {
+        if (frameNumber > 0 && !WindowManagerGlobal.useBLAST()) {
             final ViewRootImpl viewRoot = getViewRootImpl();
 
             t.deferTransactionUntil(surface, viewRoot.getRenderSurfaceControl(),
@@ -1138,7 +1137,7 @@
     }
 
     private void setParentSpaceRectangle(Rect position, long frameNumber) {
-        final boolean useBLAST = WindowManagerGlobal.USE_BLAST_ADAPTER;
+        final boolean useBLAST = WindowManagerGlobal.useBLAST();
         final ViewRootImpl viewRoot = getViewRootImpl();
         final SurfaceControl.Transaction t = useBLAST ? viewRoot.getBLASTSyncTransaction() :
             mRtTransaction;
@@ -1199,7 +1198,7 @@
 
         @Override
         public void positionLost(long frameNumber) {
-            boolean useBLAST = WindowManagerGlobal.USE_BLAST_ADAPTER;
+            boolean useBLAST = WindowManagerGlobal.useBLAST();
             if (DEBUG) {
                 Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d",
                         System.identityHashCode(this), frameNumber));
@@ -1538,7 +1537,7 @@
     @Override
     public void invalidate(boolean invalidateCache) {
         super.invalidate(invalidateCache);
-        if (!WindowManagerGlobal.USE_BLAST_ADAPTER) {
+        if (!WindowManagerGlobal.useBLAST()) {
             return;
         }
         final ViewRootImpl viewRoot = getViewRootImpl();
diff --git a/core/java/android/view/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
index abe44f4..15252b1 100644
--- a/core/java/android/view/SyncRtSurfaceTransactionApplier.java
+++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
@@ -65,20 +65,33 @@
                 return;
             }
             Transaction t = new Transaction();
-            for (int i = params.length - 1; i >= 0; i--) {
-                SurfaceParams surfaceParams = params[i];
-                SurfaceControl surface = surfaceParams.surface;
-                t.deferTransactionUntil(surface, mTargetSc, frame);
-                applyParams(t, surfaceParams, mTmpFloat9);
-            }
-            t.setEarlyWakeup();
-            t.apply();
+            applyParams(t, frame, params);
         });
 
         // Make sure a frame gets scheduled.
         mTargetViewRootImpl.getView().invalidate();
     }
 
+    /**
+     * Applies surface parameters on the next frame.
+     * @param t transaction to apply all parameters in.
+     * @param frame frame to synchronize to. Set -1 when sync is not required.
+     * @param params The surface parameters to apply. DO NOT MODIFY the list after passing into
+     *               this method to avoid synchronization issues.
+     */
+    void applyParams(Transaction t, long frame, final SurfaceParams... params) {
+        for (int i = params.length - 1; i >= 0; i--) {
+            SurfaceParams surfaceParams = params[i];
+            SurfaceControl surface = surfaceParams.surface;
+            if (frame > 0) {
+                t.deferTransactionUntil(surface, mTargetSc, frame);
+            }
+            applyParams(t, surfaceParams, mTmpFloat9);
+        }
+        t.setEarlyWakeup();
+        t.apply();
+    }
+
     public static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) {
         if ((params.flags & FLAG_MATRIX) != 0) {
             t.setMatrix(params.surface, params.matrix, tmpFloat9);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a6f8fad..f99c965 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -28989,6 +28989,18 @@
         OnContentApplyWindowInsetsListener mContentOnApplyWindowInsetsListener;
 
         /**
+         * The leash token of this view's parent when it's in an embedded hierarchy that is
+         * re-parented to another window.
+         */
+        IBinder mLeashedParentToken;
+
+        /**
+         * The accessibility view id of this view's parent when it's in an embedded
+         * hierarchy that is re-parented to another window.
+         */
+        int mLeashedParentAccessibilityViewId;
+
+        /**
          * Creates a new set of attachment information with the specified
          * events handler and thread.
          *
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 159b93e..857bc50 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -135,6 +135,7 @@
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import android.view.accessibility.AccessibilityNodeProvider;
 import android.view.accessibility.AccessibilityWindowInfo;
+import android.view.accessibility.IAccessibilityEmbeddedConnection;
 import android.view.accessibility.IAccessibilityInteractionConnection;
 import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
 import android.view.animation.AccelerateDecelerateInterpolator;
@@ -315,6 +316,8 @@
      */
     private boolean mForceNextConfigUpdate;
 
+    private final boolean mUseBLASTAdapter;
+
     /**
      * Signals that compatibility booleans have been initialized according to
      * target SDK versions.
@@ -353,6 +356,8 @@
 
     final W mWindow;
 
+    final IBinder mLeashToken;
+
     final int mTargetSdkVersion;
 
     int mSeq;
@@ -650,6 +655,8 @@
 
     private final GestureExclusionTracker mGestureExclusionTracker = new GestureExclusionTracker();
 
+    private IAccessibilityEmbeddedConnection mEmbeddedConnection;
+
     static final class SystemUiVisibilityInfo {
         int seq;
         int globalVisibility;
@@ -683,6 +690,7 @@
         mVisRect = new Rect();
         mWinFrame = new Rect();
         mWindow = new W(this);
+        mLeashToken = new Binder();
         mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
         mViewVisibility = View.GONE;
         mTransparentRegion = new Region();
@@ -734,6 +742,7 @@
 
         loadSystemProperties();
         mImeFocusController = new ImeFocusController(this);
+        mUseBLASTAdapter = WindowManagerGlobal.useBLAST();
     }
 
     public static void addFirstDrawHandler(Runnable callback) {
@@ -861,7 +870,7 @@
                 if (mWindowAttributes.packageName == null) {
                     mWindowAttributes.packageName = mBasePackageName;
                 }
-                if (WindowManagerGlobal.USE_BLAST_ADAPTER) {
+                if (mUseBLASTAdapter) {
                     mWindowAttributes.privateFlags |=
                         WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
                 }
@@ -1341,7 +1350,7 @@
             }
             mWindowAttributes.privateFlags |= compatibleWindowFlag;
 
-            if (WindowManagerGlobal.USE_BLAST_ADAPTER) {
+            if (mUseBLASTAdapter) {
                 mWindowAttributes.privateFlags |=
                     WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
             }
@@ -7342,7 +7351,7 @@
                 mPendingMergedConfiguration, mSurfaceControl, mTempInsets, mSurfaceSize,
                 mBlastSurfaceControl);
         if (mSurfaceControl.isValid()) {
-            if (!WindowManagerGlobal.USE_BLAST_ADAPTER) {
+            if (!mUseBLASTAdapter) {
                 mSurface.copyFrom(mSurfaceControl);
             } else {
                 mSurface.transferFrom(getOrCreateBLASTSurface(mSurfaceSize.x,
@@ -9154,6 +9163,10 @@
                         focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
                     }
                 }
+                if (mAttachInfo.mLeashedParentToken != null) {
+                    mAccessibilityManager.associateEmbeddedHierarchy(
+                            mAttachInfo.mLeashedParentToken, mLeashToken);
+                }
             } else {
                 ensureNoConnection();
                 mHandler.obtainMessage(MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST).sendToTarget();
@@ -9166,6 +9179,7 @@
             if (!registered) {
                 mAttachInfo.mAccessibilityWindowId =
                         mAccessibilityManager.addAccessibilityInteractionConnection(mWindow,
+                                mLeashToken,
                                 mContext.getPackageName(),
                                 new AccessibilityInteractionConnection(ViewRootImpl.this));
             }
@@ -9352,6 +9366,17 @@
         }
     }
 
+    /**
+     * Gets an accessibility embedded connection interface for this ViewRootImpl.
+     * @hide
+     */
+    public IAccessibilityEmbeddedConnection getEmbeddedConnection() {
+        if (mEmbeddedConnection == null) {
+            mEmbeddedConnection = new AccessibilityEmbeddedConnection(ViewRootImpl.this);
+        }
+        return mEmbeddedConnection;
+    }
+
     private class SendWindowContentChangedAccessibilityEvent implements Runnable {
         private int mChangeTypes = 0;
 
@@ -9537,7 +9562,7 @@
     }
 
     SurfaceControl getRenderSurfaceControl() {
-        if (WindowManagerGlobal.USE_BLAST_ADAPTER) {
+        if (mUseBLASTAdapter) {
             return mBlastSurfaceControl;
         } else {
             return mSurfaceControl;
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index f501de9..27e92e5 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -16,12 +16,11 @@
 
 package android.view;
 
-import static android.view.WindowInsets.Type.ime;
-
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Insets;
+import android.os.CancellationSignal;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowInsetsAnimationCallback.InsetsAnimation;
 import android.view.animation.Interpolator;
@@ -148,7 +147,7 @@
      * @param types The {@link InsetsType}s the application has requested to control.
      * @param durationMillis Duration of animation in
      *                       {@link java.util.concurrent.TimeUnit#MILLISECONDS}, or -1 if the
-     *                       animation doesn't have a predetermined duration.T his value will be
+     *                       animation doesn't have a predetermined duration. This value will be
      *                       passed to {@link InsetsAnimation#getDurationMillis()}
      * @param interpolator The interpolator used for this animation, or {@code null} if this
      *                     animation doesn't follow an interpolation curve. This value will be
@@ -156,13 +155,15 @@
      *                     {@link InsetsAnimation#getInterpolatedFraction()}.
      * @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
      *                 windows are ready to be controlled, among other callbacks.
-     *
+     * @return A cancellation signal that the caller can use to cancel the request to obtain
+     *         control, or once they have control, to cancel the control.
      * @see InsetsAnimation#getFraction()
      * @see InsetsAnimation#getInterpolatedFraction()
      * @see InsetsAnimation#getInterpolator()
      * @see InsetsAnimation#getDurationMillis()
      */
-    void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
+    @NonNull
+    CancellationSignal controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
             @Nullable Interpolator interpolator,
             @NonNull WindowInsetsAnimationControlListener listener);
 
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index dad7671..e731323 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -3687,6 +3687,8 @@
                     return "always";
                 case LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER:
                     return "never";
+                case LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES:
+                    return "shortEdges";
                 default:
                     return "unknown(" + mode + ")";
             }
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index f03c4e7..01a1c77 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -56,13 +56,7 @@
 public final class WindowManagerGlobal {
     private static final String TAG = "WindowManager";
 
-    private static final String WM_USE_BLAST_ADAPTER_FLAG = "wm_use_blast_adapter";
-
-    /**
-     * This flag controls whether ViewRootImpl will utilize the Blast Adapter
-     * to send buffer updates to SurfaceFlinger
-     */
-    public static final boolean USE_BLAST_ADAPTER = false;
+    private static boolean sUseBLASTAdapter = false;
 
     /**
      * The user is navigating with keys (not the touch screen), so
@@ -192,6 +186,7 @@
                     if (sWindowManagerService != null) {
                         ValueAnimator.setDurationScale(
                                 sWindowManagerService.getCurrentAnimatorScale());
+                        sUseBLASTAdapter = sWindowManagerService.useBLAST();
                     }
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
@@ -233,6 +228,13 @@
         }
     }
 
+    /**
+     * Whether or not to use BLAST for ViewRootImpl
+     */
+    public static boolean useBLAST() {
+        return sUseBLASTAdapter;
+    }
+
     @UnsupportedAppUsage
     public String[] getViewRootNames() {
         synchronized (mLock) {
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 02b098b..dc87453 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -1086,6 +1086,50 @@
     }
 
     /**
+     * Associate the connection between the host View and the embedded SurfaceControlViewHost.
+     *
+     * @hide
+     */
+    public void associateEmbeddedHierarchy(@NonNull IBinder host, @NonNull IBinder embedded) {
+        final IAccessibilityManager service;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return;
+            }
+        }
+        try {
+            service.associateEmbeddedHierarchy(host, embedded);
+        } catch (RemoteException e) {
+            return;
+        }
+    }
+
+    /**
+     * Disassociate the connection between the host View and the embedded SurfaceControlViewHost.
+     * The given token could be either from host side or embedded side.
+     *
+     * @hide
+     */
+    public void disassociateEmbeddedHierarchy(@NonNull IBinder token) {
+        if (token == null) {
+            return;
+        }
+        final IAccessibilityManager service;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return;
+            }
+        }
+        try {
+            service.disassociateEmbeddedHierarchy(token);
+        } catch (RemoteException e) {
+            return;
+        }
+    }
+
+    /**
      * Sets the current state and notifies listeners, if necessary.
      *
      * @param stateFlags The state flags.
@@ -1147,11 +1191,12 @@
     /**
      * Adds an accessibility interaction connection interface for a given window.
      * @param windowToken The window token to which a connection is added.
+     * @param leashToken The leash token to which a connection is added.
      * @param connection The connection.
      *
      * @hide
      */
-    public int addAccessibilityInteractionConnection(IWindow windowToken,
+    public int addAccessibilityInteractionConnection(IWindow windowToken, IBinder leashToken,
             String packageName, IAccessibilityInteractionConnection connection) {
         final IAccessibilityManager service;
         final int userId;
@@ -1163,8 +1208,8 @@
             userId = mUserId;
         }
         try {
-            return service.addAccessibilityInteractionConnection(windowToken, connection,
-                    packageName, userId);
+            return service.addAccessibilityInteractionConnection(windowToken, leashToken,
+                    connection, packageName, userId);
         } catch (RemoteException re) {
             Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re);
         }
diff --git a/core/java/android/view/accessibility/IAccessibilityEmbeddedConnection.aidl b/core/java/android/view/accessibility/IAccessibilityEmbeddedConnection.aidl
new file mode 100644
index 0000000..707099e
--- /dev/null
+++ b/core/java/android/view/accessibility/IAccessibilityEmbeddedConnection.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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 android.view.accessibility;
+
+/**
+ * Interface used by host View to talk to the view root of the embedded SurfaceControlViewHost
+ * that actually implements the functionality.
+ *
+ * @hide
+ */
+interface IAccessibilityEmbeddedConnection {
+
+    IBinder associateEmbeddedHierarchy(IBinder hostToken, int sourceId);
+
+    void disassociateEmbeddedHierarchy();
+
+    oneway void setScreenMatrix(in float[] matrixValues);
+}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 7f8fdf8..97036f3 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -47,7 +47,7 @@
     @UnsupportedAppUsage
     List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType, int userId);
 
-    int addAccessibilityInteractionConnection(IWindow windowToken,
+    int addAccessibilityInteractionConnection(IWindow windowToken, IBinder leashToken,
             in IAccessibilityInteractionConnection connection,
             String packageName, int userId);
 
@@ -88,4 +88,8 @@
     oneway void registerSystemAction(in RemoteAction action, int actionId);
     oneway void unregisterSystemAction(int actionId);
     oneway void setWindowMagnificationConnection(in IWindowMagnificationConnection connection);
+
+    void associateEmbeddedHierarchy(IBinder host, IBinder embedded);
+
+    void disassociateEmbeddedHierarchy(IBinder token);
 }
diff --git a/core/java/android/view/textclassifier/TextClassificationSession.java b/core/java/android/view/textclassifier/TextClassificationSession.java
index 4329a20..fed3dbf 100644
--- a/core/java/android/view/textclassifier/TextClassificationSession.java
+++ b/core/java/android/view/textclassifier/TextClassificationSession.java
@@ -16,6 +16,7 @@
 
 package android.view.textclassifier;
 
+import android.annotation.NonNull;
 import android.annotation.WorkerThread;
 import android.view.textclassifier.SelectionEvent.InvocationMethod;
 
@@ -23,6 +24,8 @@
 
 import java.util.Objects;
 
+import sun.misc.Cleaner;
+
 /**
  * Session-aware TextClassifier.
  */
@@ -35,6 +38,7 @@
     private final SelectionEventHelper mEventHelper;
     private final TextClassificationSessionId mSessionId;
     private final TextClassificationContext mClassificationContext;
+    private final Cleaner mCleaner;
 
     private boolean mDestroyed;
 
@@ -44,6 +48,8 @@
         mSessionId = new TextClassificationSessionId();
         mEventHelper = new SelectionEventHelper(mSessionId, mClassificationContext);
         initializeRemoteSession();
+        // This ensures destroy() is called if the client forgot to do so.
+        mCleaner = Cleaner.create(this, new CleanerRunnable(mEventHelper, mDelegate));
     }
 
     @Override
@@ -114,8 +120,7 @@
 
     @Override
     public void destroy() {
-        mEventHelper.endSession();
-        mDelegate.destroy();
+        mCleaner.clean();
         mDestroyed = true;
     }
 
@@ -258,4 +263,25 @@
             }
         }
     }
+
+    // We use a static nested class here to avoid retaining the object reference of the outer
+    // class. Otherwise. the Cleaner would never be triggered.
+    private static class CleanerRunnable implements Runnable {
+        @NonNull
+        private final SelectionEventHelper mEventHelper;
+        @NonNull
+        private final TextClassifier mDelegate;
+
+        CleanerRunnable(
+                @NonNull SelectionEventHelper eventHelper, @NonNull TextClassifier delegate) {
+            mEventHelper = Objects.requireNonNull(eventHelper);
+            mDelegate = Objects.requireNonNull(delegate);
+        }
+
+        @Override
+        public void run() {
+            mEventHelper.endSession();
+            mDelegate.destroy();
+        }
+    }
 }
diff --git a/core/java/android/view/textclassifier/TextClassificationSessionId.java b/core/java/android/view/textclassifier/TextClassificationSessionId.java
index f90e6b2..0b6fba2 100644
--- a/core/java/android/view/textclassifier/TextClassificationSessionId.java
+++ b/core/java/android/view/textclassifier/TextClassificationSessionId.java
@@ -17,6 +17,8 @@
 package android.view.textclassifier;
 
 import android.annotation.NonNull;
+import android.os.Binder;
+import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -28,7 +30,10 @@
  * This class represents the id of a text classification session.
  */
 public final class TextClassificationSessionId implements Parcelable {
-    private final @NonNull String mValue;
+    @NonNull
+    private final String mValue;
+    @NonNull
+    private final IBinder mToken;
 
     /**
      * Creates a new instance.
@@ -36,7 +41,7 @@
      * @hide
      */
     public TextClassificationSessionId() {
-        this(UUID.randomUUID().toString());
+        this(UUID.randomUUID().toString(), new Binder());
     }
 
     /**
@@ -46,34 +51,28 @@
      *
      * @hide
      */
-    public TextClassificationSessionId(@NonNull String value) {
-        mValue = value;
+    public TextClassificationSessionId(@NonNull String value, @NonNull IBinder token) {
+        mValue = Objects.requireNonNull(value);
+        mToken = Objects.requireNonNull(token);
+    }
+
+    /** @hide */
+    @NonNull
+    public IBinder getToken() {
+        return mToken;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        TextClassificationSessionId that = (TextClassificationSessionId) o;
+        return Objects.equals(mValue, that.mValue) && Objects.equals(mToken, that.mToken);
     }
 
     @Override
     public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + mValue.hashCode();
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        TextClassificationSessionId other = (TextClassificationSessionId) obj;
-        if (!mValue.equals(other.mValue)) {
-            return false;
-        }
-        return true;
+        return Objects.hash(mValue, mToken);
     }
 
     @Override
@@ -84,6 +83,7 @@
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeString(mValue);
+        parcel.writeStrongBinder(mToken);
     }
 
     @Override
@@ -96,28 +96,18 @@
      *
      * @return The flattened id.
      */
-    public @NonNull String flattenToString() {
+    @NonNull
+    public String flattenToString() {
         return mValue;
     }
 
-    /**
-     * Unflattens a print job id from a string.
-     *
-     * @param string The string.
-     * @return The unflattened id, or null if the string is malformed.
-     *
-     * @hide
-     */
-    public static @NonNull TextClassificationSessionId unflattenFromString(@NonNull String string) {
-        return new TextClassificationSessionId(string);
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<TextClassificationSessionId> CREATOR =
+    @NonNull
+    public static final Parcelable.Creator<TextClassificationSessionId> CREATOR =
             new Parcelable.Creator<TextClassificationSessionId>() {
                 @Override
                 public TextClassificationSessionId createFromParcel(Parcel parcel) {
                     return new TextClassificationSessionId(
-                            Objects.requireNonNull(parcel.readString()));
+                            parcel.readString(), parcel.readStrongBinder());
                 }
 
                 @Override
diff --git a/core/java/android/webkit/WebViewProviderResponse.java b/core/java/android/webkit/WebViewProviderResponse.java
index b58cc4bb..02e48dd 100644
--- a/core/java/android/webkit/WebViewProviderResponse.java
+++ b/core/java/android/webkit/WebViewProviderResponse.java
@@ -16,6 +16,8 @@
 
 package android.webkit;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.pm.PackageInfo;
 import android.os.Parcel;
@@ -30,7 +32,7 @@
     }
 
     // aidl stuff
-    public static final @android.annotation.NonNull Parcelable.Creator<WebViewProviderResponse> CREATOR =
+    public static final @NonNull Parcelable.Creator<WebViewProviderResponse> CREATOR =
         new Parcelable.Creator<WebViewProviderResponse>() {
             public WebViewProviderResponse createFromParcel(Parcel in) {
                 return new WebViewProviderResponse(in);
@@ -58,6 +60,7 @@
     }
 
     @UnsupportedAppUsage
+    @Nullable
     public final PackageInfo packageInfo;
     public final int status;
 }
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 4e4a983..46b8b77 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -5111,8 +5111,10 @@
                     int lineRight = (int) layout.getLineRight(line);
                     lineRight += mTextView.getTotalPaddingLeft() - mTextView.getScrollX();
                     mMagnifierAnimator.mMagnifier.setSourceHorizontalBounds(lineLeft, lineRight);
+                    mMagnifierAnimator.mMagnifier.show(showPosInView.x, showPosInView.y);
+                } else {
+                  mMagnifierAnimator.show(showPosInView.x, showPosInView.y);
                 }
-                mMagnifierAnimator.show(showPosInView.x, showPosInView.y);
                 updateHandlesVisibility();
             } else {
                 dismissMagnifier();
@@ -5248,9 +5250,6 @@
         private boolean mIsInActionMode;
         // The timestamp for the last up event, which is used for double tap detection.
         private long mLastUpTime;
-        // The text height of the font of the text view, which is used to calculate the Y coordinate
-        // of the touch through events.
-        private float mTextHeight;
 
         // The delta height applied to the insertion handle view.
         private final int mDeltaHeight;
@@ -5403,8 +5402,6 @@
                     if (ev.getEventTime() - mLastUpTime < ViewConfiguration.getDoubleTapTimeout()) {
                         stopTextActionMode();  // Avoid crash when double tap and drag backwards.
                     }
-                    final Paint.FontMetrics fontMetrics = mTextView.getPaint().getFontMetrics();
-                    mTextHeight = fontMetrics.descent - fontMetrics.ascent;
                     mTouchState.setIsOnHandle(true);
                     break;
                 case MotionEvent.ACTION_UP:
@@ -5443,6 +5440,10 @@
         }
 
         private MotionEvent transformEventForTouchThrough(MotionEvent ev) {
+            final Layout layout = mTextView.getLayout();
+            final int line = layout.getLineForOffset(getCurrentCursorOffset());
+            final int textHeight =
+                    layout.getLineBottomWithoutSpacing(line) - layout.getLineTop(line);
             // Transforms the touch events to screen coordinates.
             // And also shift up to make the hit point is on the text.
             // Note:
@@ -5450,7 +5451,7 @@
             //  - The revised Y should be at the top of the text.
             Matrix m = new Matrix();
             m.setTranslate(ev.getRawX() - ev.getX() + (getMeasuredWidth() >> 1) - mTouchDownX,
-                    ev.getRawY() - ev.getY() - mTouchDownY - mTextHeight);
+                    ev.getRawY() - ev.getY() - (textHeight >> 1) - mTouchDownY);
             ev.transform(m);
             // Transforms the touch events to text view coordinates.
             mTextView.toLocalMotionEvent(ev);
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 50da6ce..a6a5ec5 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -45,6 +45,7 @@
 import android.os.HandlerThread;
 import android.os.Message;
 import android.util.Log;
+import android.util.TypedValue;
 import android.view.ContextThemeWrapper;
 import android.view.Display;
 import android.view.PixelCopy;
@@ -76,6 +77,8 @@
     // the Handler of this Thread when the copy is finished.
     private static final HandlerThread sPixelCopyHandlerThread =
             new HandlerThread("magnifier pixel copy result handler");
+    // The width of the ramp region in DP on the left & right sides of the fish-eye effect.
+    private static final float FISHEYE_RAMP_WIDTH = 12f;
 
     // The view to which this magnifier is attached.
     private final View mView;
@@ -151,6 +154,8 @@
     // The horizontal bounds of the content source in pixels, relative to the view.
     private int mLeftBound = Integer.MIN_VALUE;
     private int mRightBound = Integer.MAX_VALUE;
+    // The width of the ramp region in pixels on the left & right sides of the fish-eye effect.
+    private final int mRamp;
 
     /**
      * Initializes a magnifier.
@@ -232,6 +237,8 @@
         mBottomContentBound = params.mBottomContentBound;
         // The view's surface coordinates will not be updated until the magnifier is first shown.
         mViewCoordinatesInSurface = new int[2];
+        mRamp = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, FISHEYE_RAMP_WIDTH,
+                mView.getContext().getResources().getDisplayMetrics());
     }
 
     static {
@@ -303,16 +310,54 @@
             // The magnifier center is the same as source center in new style.
             magnifierCenterX = mClampedCenterZoomCoords.x - mViewCoordinatesInSurface[0];
             magnifierCenterY = mClampedCenterZoomCoords.y - mViewCoordinatesInSurface[1];
+
+            // mLeftBound & mRightBound (typically the text line left/right) is for magnified
+            // content. However the PixelCopy requires the pre-magnified bounds.
+            // The below logic calculates the leftBound & rightBound for the pre-magnified bounds.
+            final float rampPre =
+                    (mSourceWidth - (mSourceWidth - 2 * mRamp) / mZoom) / 2;
+
+            // Calculates the pre-zoomed left edge.
+            // The leftEdge moves from the left of view towards to sourceCenterX, considering the
+            // fisheye-like zooming.
+            final float x0 = sourceCenterX - mSourceWidth / 2;
+            final float rampX0 = x0 + mRamp;
+            float leftEdge = 0;
+            if (leftEdge > rampX0) {
+                // leftEdge is in the zoom range, the distance from leftEdge to sourceCenterX
+                // should reduce per mZoom.
+                leftEdge = sourceCenterX - (sourceCenterX - leftEdge) / mZoom;
+            } else if (leftEdge > x0) {
+                // leftEdge is in the ramp range, the distance from leftEdge to rampX0 should
+                // increase per ramp zoom (ramp / rampPre).
+                leftEdge = x0 + rampPre - (rampX0 - leftEdge) * rampPre / mRamp;
+            }
+            int leftBound = Math.min(Math.max((int) leftEdge, mLeftBound), mRightBound);
+
+            // Calculates the pre-zoomed right edge.
+            // The rightEdge moves from the right of view towards to sourceCenterX, considering the
+            // fisheye-like zooming.
+            final float x1 = sourceCenterX + mSourceWidth / 2;
+            final float rampX1 = x1 - mRamp;
+            float rightEdge = mView.getWidth();
+            if (rightEdge < rampX1) {
+                // rightEdge is in the zoom range, the distance from rightEdge to sourceCenterX
+                // should reduce per mZoom.
+                rightEdge = sourceCenterX + (rightEdge - sourceCenterX) / mZoom;
+            } else if (rightEdge < x1) {
+                // rightEdge is in the ramp range, the distance from rightEdge to rampX1 should
+                // increase per ramp zoom (ramp / rampPre).
+                rightEdge = x1 - rampPre + (rightEdge - rampX1) * rampPre / mRamp;
+            }
+            int rightBound = Math.max(leftBound, Math.min((int) rightEdge, mRightBound));
+
             // Gets the startX for new style, which should be bounded by the horizontal bounds.
             // Also calculates the left/right cut width for pixel copy.
-            final int left = startX;
-            final int right = startX + mSourceWidth;
-            final int leftBound = mViewCoordinatesInSurface[0] + Math.max(0, mLeftBound);
-            final int rightBound =
-                    mViewCoordinatesInSurface[0] + Math.min(mView.getWidth(), mRightBound);
-            startX = Math.max(left, leftBound);
-            mLeftCutWidth = Math.max(0, leftBound - left);
-            mRightCutWidth = Math.max(0, right - rightBound);
+            leftBound += mViewCoordinatesInSurface[0];
+            rightBound += mViewCoordinatesInSurface[0];
+            mLeftCutWidth = Math.max(0, leftBound - startX);
+            mRightCutWidth = Math.max(0, startX + mSourceWidth - rightBound);
+            startX = Math.max(startX, leftBound);
         }
         obtainWindowCoordinates(magnifierCenterX, magnifierCenterY);
 
@@ -322,7 +367,7 @@
                 synchronized (mLock) {
                     mWindow = new InternalPopupWindow(mView.getContext(), mView.getDisplay(),
                             mParentSurface.mSurfaceControl, mWindowWidth, mWindowHeight, mZoom,
-                            mWindowElevation, mWindowCornerRadius,
+                            mRamp, mWindowElevation, mWindowCornerRadius,
                             mOverlay != null ? mOverlay : new ColorDrawable(Color.TRANSPARENT),
                             Handler.getMain() /* draw the magnifier on the UI thread */, mLock,
                             mCallback, mIsFishEyeStyle);
@@ -854,8 +899,6 @@
         // The z of the magnifier surface, defining its z order in the list of
         // siblings having the same parent surface (usually the main app surface).
         private static final int SURFACE_Z = 5;
-        // The width of the ramp region in pixels on the left & right sides of the fish-eye effect.
-        private static final int FISHEYE_RAMP_WIDTH = 30;
 
         // Display associated to the view the magnifier is attached to.
         private final Display mDisplay;
@@ -905,6 +948,8 @@
         private Bitmap mCurrentContent;
 
         private final float mZoom;
+        // The width of the ramp region in pixels on the left & right sides of the fish-eye effect.
+        private final int mRamp;
         // Whether is in the new magnifier style.
         private boolean mIsFishEyeStyle;
         // The mesh matrix for the fish-eye effect.
@@ -915,7 +960,7 @@
 
         InternalPopupWindow(final Context context, final Display display,
                 final SurfaceControl parentSurfaceControl, final int width, final int height,
-                final float zoom, final float elevation, final float cornerRadius,
+                final float zoom, final int ramp, final float elevation, final float cornerRadius,
                 final Drawable overlay, final Handler handler, final Object lock,
                 final Callback callback, final boolean isFishEyeStyle) {
             mDisplay = display;
@@ -926,6 +971,7 @@
             mContentWidth = width;
             mContentHeight = height;
             mZoom = zoom;
+            mRamp = ramp;
             mOffsetX = (int) (1.05f * elevation);
             mOffsetY = (int) (1.05f * elevation);
             // Setup the surface we will use for drawing the content and shadow.
@@ -995,14 +1041,13 @@
             final float h = mContentHeight;
             final float h0 = h / mZoom;
             final float dh = h - h0;
-            final float ramp = FISHEYE_RAMP_WIDTH;
             mMeshLeft = new float[2 * (mMeshWidth + 1) * (mMeshHeight + 1)];
             mMeshRight = new float[2 * (mMeshWidth + 1) * (mMeshHeight + 1)];
             for (int i = 0; i < 2 * (mMeshWidth + 1) * (mMeshHeight + 1); i += 2) {
                 // Calculates X value.
                 final int colIndex = i % (2 * (mMeshWidth + 1)) / 2;
-                mMeshLeft[i] = (float) colIndex * ramp / mMeshWidth;
-                mMeshRight[i] = w - ramp + colIndex * ramp / mMeshWidth;
+                mMeshLeft[i] = (float) colIndex * mRamp / mMeshWidth;
+                mMeshRight[i] = w - mRamp + colIndex * mRamp / mMeshWidth;
 
                 // Calculates Y value.
                 final int rowIndex = i / 2 / (mMeshWidth + 1);
@@ -1174,14 +1219,13 @@
                     final Paint paint = new Paint();
                     paint.setFilterBitmap(true);
                     if (mIsFishEyeStyle) {
-                        final int ramp = FISHEYE_RAMP_WIDTH;
                         final int margin =
-                            (int)((mContentWidth - (mContentWidth - 2 * ramp) / mZoom) / 2);
+                            (int)((mContentWidth - (mContentWidth - 2 * mRamp) / mZoom) / 2);
 
                         // Draws the middle part.
                         final Rect srcRect = new Rect(margin, 0, w - margin, h);
                         final Rect dstRect = new Rect(
-                            ramp, 0, mContentWidth - ramp, mContentHeight);
+                            mRamp, 0, mContentWidth - mRamp, mContentHeight);
                         canvas.drawBitmap(mBitmap, srcRect, dstRect, paint);
 
                         // Draws the left/right parts with mesh matrixes.
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 8c52d1f..9e4ebfe 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -460,8 +460,19 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public WindowManager.LayoutParams getWindowParams() {
-        return mTN.mParams;
+    @Nullable public WindowManager.LayoutParams getWindowParams() {
+        if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
+            if (mNextView != null) {
+                // Custom toasts
+                return mTN.mParams;
+            } else {
+                // Text toasts
+                return null;
+            }
+        } else {
+            // Text and custom toasts are app-rendered
+            return mTN.mParams;
+        }
     }
 
     /**
diff --git a/core/java/com/android/internal/BrightnessSynchronizer.java b/core/java/com/android/internal/BrightnessSynchronizer.java
new file mode 100644
index 0000000..aa23251
--- /dev/null
+++ b/core/java/com/android/internal/BrightnessSynchronizer.java
@@ -0,0 +1,273 @@
+/*
+ * 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.internal;
+
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.MathUtils;
+
+import java.util.LinkedList;
+import java.util.Queue;
+
+/**
+ * BrightnessSynchronizer helps convert between the int (old) system and float
+ * (new) system for storing the brightness. It has methods to convert between the two and also
+ * observes for when one of the settings is changed and syncs this with the other.
+ */
+public class BrightnessSynchronizer{
+
+    private static final int MSG_UPDATE_FLOAT = 1;
+    private static final int MSG_UPDATE_INT = 2;
+
+    private static final String TAG = "BrightnessSynchronizer";
+    private static final Uri BRIGHTNESS_URI =
+            Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
+    private static final Uri BRIGHTNESS_FLOAT_URI =
+            Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FLOAT);
+
+    // The tolerance within which we consider brightness values approximately equal to eachother.
+    // This value is approximately 1/3 of the smallest possible brightness value.
+    public static final float EPSILON = 0.001f;
+
+    private final Context mContext;
+
+    private final Queue<Object> mWriteHistory = new LinkedList<>();
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_UPDATE_FLOAT:
+                    updateBrightnessFloatFromInt(msg.arg1);
+                    break;
+                case MSG_UPDATE_INT:
+                    updateBrightnessIntFromFloat(Float.intBitsToFloat(msg.arg1));
+                    break;
+                default:
+                    super.handleMessage(msg);
+            }
+
+        }
+    };
+
+
+    public BrightnessSynchronizer(Context context) {
+        final BrightnessSyncObserver mBrightnessSyncObserver;
+        mContext = context;
+        mBrightnessSyncObserver = new BrightnessSyncObserver(mHandler);
+        mBrightnessSyncObserver.startObserving();
+    }
+
+    /**
+     * Converts between the int brightness system and the float brightness system.
+     */
+    public static float brightnessIntToFloat(Context context, int brightnessInt) {
+        PowerManager pm = context.getSystemService(PowerManager.class);
+        float pmMinBrightness = pm.getBrightnessConstraint(
+                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
+        float pmMaxBrightness = pm.getBrightnessConstraint(
+                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
+        int minBrightnessInt = brightnessFloatToInt(pmMinBrightness, PowerManager.BRIGHTNESS_MIN,
+                PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_OFF + 1,
+                PowerManager.BRIGHTNESS_ON);
+        int maxBrightnessInt = brightnessFloatToInt(pmMaxBrightness, PowerManager.BRIGHTNESS_MIN,
+                PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_OFF + 1,
+                PowerManager.BRIGHTNESS_ON);
+
+        return brightnessIntToFloat(brightnessInt, minBrightnessInt, maxBrightnessInt,
+                pmMinBrightness, pmMaxBrightness);
+    }
+
+    /**
+     * Converts between the int brightness system and the float brightness system.
+     */
+    public static float brightnessIntToFloat(int brightnessInt, int minInt, int maxInt,
+            float minFloat, float maxFloat) {
+        if (brightnessInt == PowerManager.BRIGHTNESS_OFF) {
+            return PowerManager.BRIGHTNESS_OFF_FLOAT;
+        } else if (brightnessInt == PowerManager.BRIGHTNESS_INVALID) {
+            return PowerManager.BRIGHTNESS_INVALID_FLOAT;
+        } else {
+            return MathUtils.constrainedMap(minFloat, maxFloat, (float) minInt, (float) maxInt,
+                    brightnessInt);
+        }
+    }
+
+    /**
+     * Converts between the float brightness system and the int brightness system.
+     */
+    public static int brightnessFloatToInt(Context context, float brightnessFloat) {
+        PowerManager pm = context.getSystemService(PowerManager.class);
+        float pmMinBrightness = pm.getBrightnessConstraint(
+                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
+        float pmMaxBrightness = pm.getBrightnessConstraint(
+                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
+        int minBrightnessInt = brightnessFloatToInt(pmMinBrightness, PowerManager.BRIGHTNESS_MIN,
+                PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_OFF + 1,
+                PowerManager.BRIGHTNESS_ON);
+        int maxBrightnessInt = brightnessFloatToInt(pmMaxBrightness, PowerManager.BRIGHTNESS_MIN,
+                PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_OFF + 1,
+                PowerManager.BRIGHTNESS_ON);
+
+        return brightnessFloatToInt(brightnessFloat, pmMinBrightness, pmMaxBrightness,
+                minBrightnessInt, maxBrightnessInt);
+    }
+
+    /**
+     * Converts between the float brightness system and the int brightness system.
+     */
+    public static int brightnessFloatToInt(float brightnessFloat, float minFloat, float maxFloat,
+            int minInt, int maxInt) {
+        if (floatEquals(brightnessFloat, PowerManager.BRIGHTNESS_OFF_FLOAT)) {
+            return PowerManager.BRIGHTNESS_OFF;
+        } else if (Float.isNaN(brightnessFloat)) {
+            return PowerManager.BRIGHTNESS_INVALID;
+        } else {
+            return Math.round(MathUtils.constrainedMap((float) minInt, (float) maxInt, minFloat,
+                    maxFloat, brightnessFloat));
+        }
+    }
+
+    private static float getScreenBrightnessFloat(Context context) {
+        return Settings.System.getFloatForUser(context.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_FLOAT, Float.NaN, UserHandle.USER_CURRENT);
+    }
+
+    private static int getScreenBrightnessInt(Context context) {
+        return Settings.System.getIntForUser(context.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS, 0, UserHandle.USER_CURRENT);
+    }
+
+    private float mPreferredSettingValue;
+
+    /**
+     * Updates the float setting based on a passed in int value. This is called whenever the int
+     * setting changes. mWriteHistory keeps a record of the values that been written to the settings
+     * from either this method or updateBrightnessIntFromFloat. This is to ensure that the value
+     * being set is due to an external value being set, rather than the updateBrightness* methods.
+     * The intention of this is to avoid race conditions when the setting is being changed
+     * frequently and to ensure we are not reacting to settings changes from this file.
+     * @param value Brightness value as int to store in the float setting.
+     */
+    private void updateBrightnessFloatFromInt(int value) {
+        Object topOfQueue = mWriteHistory.peek();
+        if (topOfQueue != null && topOfQueue.equals(value)) {
+            mWriteHistory.poll();
+        } else {
+            if (brightnessFloatToInt(mContext, mPreferredSettingValue) == value) {
+                return;
+            }
+            float newBrightnessFloat = brightnessIntToFloat(mContext, value);
+            mWriteHistory.offer(newBrightnessFloat);
+            mPreferredSettingValue = newBrightnessFloat;
+            Settings.System.putFloatForUser(mContext.getContentResolver(),
+                    Settings.System.SCREEN_BRIGHTNESS_FLOAT, newBrightnessFloat,
+                    UserHandle.USER_CURRENT);
+        }
+    }
+
+    /**
+     * Updates the int setting based on a passed in float value. This is called whenever the float
+     * setting changes. mWriteHistory keeps a record of the values that been written to the settings
+     * from either this method or updateBrightnessFloatFromInt. This is to ensure that the value
+     * being set is due to an external value being set, rather than the updateBrightness* methods.
+     * The intention of this is to avoid race conditions when the setting is being changed
+     * frequently and to ensure we are not reacting to settings changes from this file.
+     * @param value Brightness setting as float to store in int setting.
+     */
+    private void updateBrightnessIntFromFloat(float value) {
+        int newBrightnessInt = brightnessFloatToInt(mContext, value);
+        Object topOfQueue = mWriteHistory.peek();
+        if (topOfQueue != null && topOfQueue.equals(value)) {
+            mWriteHistory.poll();
+        } else {
+            mWriteHistory.offer(newBrightnessInt);
+            mPreferredSettingValue = value;
+            Settings.System.putIntForUser(mContext.getContentResolver(),
+                    Settings.System.SCREEN_BRIGHTNESS, newBrightnessInt, UserHandle.USER_CURRENT);
+        }
+    }
+
+    /**
+     * Tests whether two brightness float values are within a small enough tolerance
+     * of each other.
+     * @param a first float to compare
+     * @param b second float to compare
+     * @return whether the two values are within a small enough tolerance value
+     */
+    public static boolean floatEquals(float a, float b) {
+        if (a == b) {
+            return true;
+        } else if (Float.isNaN(a) && Float.isNaN(b)) {
+            return true;
+        } else if (Math.abs(a - b) < EPSILON) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private class BrightnessSyncObserver extends ContentObserver {
+        /**
+         * Creates a content observer.
+         * @param handler The handler to run {@link #onChange} on, or null if none.
+         */
+        BrightnessSyncObserver(Handler handler) {
+            super(handler);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            onChange(selfChange, null);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            if (selfChange) {
+                return;
+            }
+            if (BRIGHTNESS_URI.equals(uri)) {
+                int currentBrightness = getScreenBrightnessInt(mContext);
+                mHandler.obtainMessage(MSG_UPDATE_FLOAT, currentBrightness, 0).sendToTarget();
+            } else if (BRIGHTNESS_FLOAT_URI.equals(uri)) {
+                float currentFloat = getScreenBrightnessFloat(mContext);
+                int toSend = Float.floatToIntBits(currentFloat);
+                mHandler.obtainMessage(MSG_UPDATE_INT, toSend, 0).sendToTarget();
+            }
+        }
+
+        public void startObserving() {
+            final ContentResolver cr = mContext.getContentResolver();
+            cr.unregisterContentObserver(this);
+            cr.registerContentObserver(BRIGHTNESS_URI, false, this, UserHandle.USER_ALL);
+            cr.registerContentObserver(BRIGHTNESS_FLOAT_URI, false, this, UserHandle.USER_ALL);
+        }
+
+        public void stopObserving() {
+            final ContentResolver cr = mContext.getContentResolver();
+            cr.unregisterContentObserver(this);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index ab8c19e..78e8518 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -15,15 +15,25 @@
  */
 package com.android.internal.app;
 
+import android.annotation.DrawableRes;
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.app.AppGlobals;
+import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.IPackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
 
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.widget.PagerAdapter;
 import com.android.internal.widget.ViewPager;
@@ -213,26 +223,115 @@
 
     abstract @Nullable ViewGroup getInactiveAdapterView();
 
-    boolean rebuildActiveTab(boolean post) {
-        return rebuildTab(getActiveListAdapter(), post);
+    /**
+     * Rebuilds the tab that is currently visible to the user.
+     * <p>Returns {@code true} if rebuild has completed.
+     */
+    boolean rebuildActiveTab(boolean doPostProcessing) {
+        return rebuildTab(getActiveListAdapter(), doPostProcessing);
     }
 
-    boolean rebuildInactiveTab(boolean post) {
+    /**
+     * Rebuilds the tab that is not currently visible to the user, if such one exists.
+     * <p>Returns {@code true} if rebuild has completed.
+     */
+    boolean rebuildInactiveTab(boolean doPostProcessing) {
         if (getItemCount() == 1) {
             return false;
         }
-        return rebuildTab(getInactiveListAdapter(), post);
+        return rebuildTab(getInactiveListAdapter(), doPostProcessing);
+    }
+
+    private int userHandleToPageIndex(UserHandle userHandle) {
+        if (userHandle == getPersonalListAdapter().mResolverListController.getUserHandle()) {
+            return PROFILE_PERSONAL;
+        } else {
+            return PROFILE_WORK;
+        }
     }
 
     private boolean rebuildTab(ResolverListAdapter activeListAdapter, boolean doPostProcessing) {
         UserHandle listUserHandle = activeListAdapter.getUserHandle();
-        if (UserHandle.myUserId() != listUserHandle.getIdentifier() &&
-                !hasAppsInOtherProfile(activeListAdapter)) {
-            // TODO(arangelov): Show empty state UX here
+        UserManager userManager = mContext.getSystemService(UserManager.class);
+        if (listUserHandle == mWorkProfileUserHandle
+                && userManager.isQuietModeEnabled(mWorkProfileUserHandle)) {
+            showEmptyState(activeListAdapter,
+                    R.drawable.ic_work_apps_off,
+                    R.string.resolver_turn_on_work_apps,
+                    R.string.resolver_turn_on_work_apps_explanation,
+                    (View.OnClickListener) v ->
+                            userManager.requestQuietModeEnabled(false, mWorkProfileUserHandle));
             return false;
-        } else {
-            return activeListAdapter.rebuildList(doPostProcessing);
         }
+        if (UserHandle.myUserId() != listUserHandle.getIdentifier()) {
+            if (!hasCrossProfileIntents(activeListAdapter.getIntents(),
+                    UserHandle.myUserId(), listUserHandle.getIdentifier())) {
+                if (listUserHandle == mPersonalProfileUserHandle) {
+                    showEmptyState(activeListAdapter,
+                            R.drawable.ic_sharing_disabled,
+                            R.string.resolver_cant_share_with_personal_apps,
+                            R.string.resolver_cant_share_cross_profile_explanation);
+                } else {
+                    showEmptyState(activeListAdapter,
+                            R.drawable.ic_sharing_disabled,
+                            R.string.resolver_cant_share_with_work_apps,
+                            R.string.resolver_cant_share_cross_profile_explanation);
+                }
+                return false;
+            }
+        }
+        return activeListAdapter.rebuildList(doPostProcessing);
+    }
+
+    void showEmptyState(ResolverListAdapter listAdapter) {
+        UserHandle listUserHandle = listAdapter.getUserHandle();
+        if (UserHandle.myUserId() == listUserHandle.getIdentifier()
+                || !hasAppsInOtherProfile(listAdapter)) {
+            showEmptyState(listAdapter,
+                    R.drawable.ic_no_apps,
+                    R.string.resolver_no_apps_available,
+                    R.string.resolver_no_apps_available_explanation);
+        }
+    }
+
+    private void showEmptyState(ResolverListAdapter activeListAdapter,
+            @DrawableRes int iconRes, @StringRes int titleRes, @StringRes int subtitleRes) {
+        showEmptyState(activeListAdapter, iconRes, titleRes, subtitleRes, /* buttonOnClick */ null);
+    }
+
+    private void showEmptyState(ResolverListAdapter activeListAdapter,
+            @DrawableRes int iconRes, @StringRes int titleRes, @StringRes int subtitleRes,
+            View.OnClickListener buttonOnClick) {
+        ProfileDescriptor descriptor = getItem(
+                userHandleToPageIndex(activeListAdapter.getUserHandle()));
+        descriptor.rootView.findViewById(R.id.resolver_list).setVisibility(View.GONE);
+        View emptyStateView = descriptor.rootView.findViewById(R.id.resolver_empty_state);
+        emptyStateView.setVisibility(View.VISIBLE);
+
+        ImageView icon = emptyStateView.findViewById(R.id.resolver_empty_state_icon);
+        icon.setImageResource(iconRes);
+
+        TextView title = emptyStateView.findViewById(R.id.resolver_empty_state_title);
+        title.setText(titleRes);
+
+        TextView subtitle = emptyStateView.findViewById(R.id.resolver_empty_state_subtitle);
+        subtitle.setText(subtitleRes);
+
+        Button button = emptyStateView.findViewById(R.id.resolver_empty_state_button);
+        button.setVisibility(buttonOnClick != null ? View.VISIBLE : View.GONE);
+        button.setOnClickListener(buttonOnClick);
+    }
+
+    private boolean hasCrossProfileIntents(List<Intent> intents, int source, int target) {
+        IPackageManager packageManager = AppGlobals.getPackageManager();
+        ContentResolver contentResolver = mContext.getContentResolver();
+        for (Intent intent : intents) {
+            if (IntentForwarderActivity.canForward(intent, source, target, packageManager,
+                    contentResolver) != null) {
+                return true;
+            }
+        }
+        return false;
     }
 
     private boolean hasAppsInOtherProfile(ResolverListAdapter adapter) {
diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
index 5f35622..86d2ed6 100644
--- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
+++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
@@ -30,10 +30,12 @@
 import static com.android.internal.util.Preconditions.checkArgument;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityShortcutInfo;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Activity;
+import android.app.ActivityManager;
 import android.app.AlertDialog;
 import android.content.ComponentName;
 import android.content.Context;
@@ -265,6 +267,7 @@
             @ShortcutType int shortcutType) {
         final List<AccessibilityButtonTarget> targets = new ArrayList<>();
         targets.addAll(getAccessibilityServiceTargets(context));
+        targets.addAll(getAccessibilityActivityTargets(context));
         targets.addAll(getWhiteListingServiceTargets(context));
 
         final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class);
@@ -291,6 +294,24 @@
         return targets;
     }
 
+    private static List<AccessibilityButtonTarget> getAccessibilityActivityTargets(
+            @NonNull Context context) {
+        final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class);
+        final List<AccessibilityShortcutInfo> installedServices =
+                ams.getInstalledAccessibilityShortcutListAsUser(context,
+                        ActivityManager.getCurrentUser());
+        if (installedServices == null) {
+            return Collections.emptyList();
+        }
+
+        final List<AccessibilityButtonTarget> targets = new ArrayList<>(installedServices.size());
+        for (AccessibilityShortcutInfo info : installedServices) {
+            targets.add(new AccessibilityButtonTarget(context, info));
+        }
+
+        return targets;
+    }
+
     private static List<AccessibilityButtonTarget> getWhiteListingServiceTargets(
             @NonNull Context context) {
         final List<AccessibilityButtonTarget> targets = new ArrayList<>();
@@ -540,6 +561,14 @@
             this.mFragmentType = getAccessibilityServiceFragmentType(serviceInfo);
         }
 
+        AccessibilityButtonTarget(@NonNull Context context,
+                @NonNull AccessibilityShortcutInfo shortcutInfo) {
+            this.mId = shortcutInfo.getComponentName().flattenToString();
+            this.mLabel = shortcutInfo.getActivityInfo().loadLabel(context.getPackageManager());
+            this.mDrawable = shortcutInfo.getActivityInfo().loadIcon(context.getPackageManager());
+            this.mFragmentType = AccessibilityServiceFragmentType.BOUNCE;
+        }
+
         AccessibilityButtonTarget(Context context, @NonNull String id, int labelResId,
                 int iconRes, @AccessibilityServiceFragmentType int fragmentType) {
             this.mId = id;
@@ -594,7 +623,7 @@
                 onIntuitiveTargetSelected(target);
                 break;
             case AccessibilityServiceFragmentType.BOUNCE:
-                // Do nothing
+                onBounceTargetSelected(target);
                 break;
             default:
                 throw new IllegalStateException("Unexpected fragment type");
@@ -625,6 +654,15 @@
         switchServiceState(target);
     }
 
+    private void onBounceTargetSelected(AccessibilityButtonTarget target) {
+        final AccessibilityManager ams = getSystemService(AccessibilityManager.class);
+        if (mShortcutType == ACCESSIBILITY_BUTTON) {
+            ams.notifyAccessibilityButtonClicked(getDisplayId(), target.getId());
+        } else if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) {
+            ams.performAccessibilityShortcut(target.getId());
+        }
+    }
+
     private void switchServiceState(AccessibilityButtonTarget target) {
         final ComponentName componentName =
                 ComponentName.unflattenFromString(target.getId());
@@ -656,7 +694,7 @@
                 onIntuitiveTargetDeleted(position, componentId);
                 break;
             case AccessibilityServiceFragmentType.BOUNCE:
-                // Do nothing
+                onBounceTargetDeleted(position, componentId);
                 break;
             default:
                 throw new IllegalStateException("Unexpected fragment type");
@@ -694,6 +732,12 @@
         mTargetAdapter.notifyDataSetChanged();
     }
 
+    private void onBounceTargetDeleted(int position, String componentId) {
+        optOutValueFromSettings(this, mShortcutUserType, componentId);
+        mTargets.remove(position);
+        mTargetAdapter.notifyDataSetChanged();
+    }
+
     private void onCancelButtonClicked() {
         mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.LAUNCH);
         mTargetAdapter.notifyDataSetChanged();
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 65cad83..07ca79a 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -373,7 +373,11 @@
                 Log.i(TAG, "Hiding image preview area. Timed out waiting for preview to load"
                         + " within " + mImageLoadTimeoutMillis + "ms.");
                 collapseParentView();
-                hideContentPreview();
+                if (hasWorkProfile() && ENABLE_TABBED_VIEW) {
+                    hideStickyContentPreview();
+                } else if (mChooserMultiProfilePagerAdapter.getCurrentRootAdapter() != null) {
+                    mChooserMultiProfilePagerAdapter.getCurrentRootAdapter().hideContentPreview();
+                }
                 mHideParentOnFail = false;
             }
         }
@@ -829,7 +833,12 @@
 
     @Override
     protected boolean postRebuildList(boolean rebuildCompleted) {
-        updateContentPreview();
+        updateStickyContentPreview();
+        if (shouldShowStickyContentPreview()
+                || mChooserMultiProfilePagerAdapter
+                        .getCurrentRootAdapter().getContentPreviewRowCount() != 0) {
+            logActionShareWithPreview();
+        }
         return postRebuildListInternal(rebuildCompleted);
     }
 
@@ -978,6 +987,8 @@
         updateLayoutWidth(R.id.content_preview_text_layout, width, parent);
         updateLayoutWidth(R.id.content_preview_title_layout, width, parent);
         updateLayoutWidth(R.id.content_preview_file_layout, width, parent);
+        findViewById(R.id.content_preview_container)
+                .setVisibility(shouldShowStickyContentPreview() ? View.VISIBLE : View.GONE);
     }
 
     private void updateLayoutWidth(int layoutResourceId, int width, View parent) {
@@ -2413,14 +2424,14 @@
 
                 // still zero? then use a default height and leave, which
                 // can happen when there are no targets to show
-                if (rowsToShow == 0 && !shouldShowContentPreview()) {
+                if (rowsToShow == 0 && !shouldShowStickyContentPreview()) {
                     offset += getResources().getDimensionPixelSize(
                             R.dimen.chooser_max_collapsed_height);
                     mResolverDrawerLayout.setCollapsibleHeightReserved(offset);
                     return;
                 }
 
-                if (shouldShowContentPreview()) {
+                if (shouldShowStickyContentPreview()) {
                     offset += findViewById(R.id.content_preview_container).getHeight();
                 }
 
@@ -2552,7 +2563,7 @@
     }
 
     private void setupScrollListener() {
-        if (mResolverDrawerLayout == null) {
+        if (mResolverDrawerLayout == null || (hasWorkProfile() && ENABLE_TABBED_VIEW)) {
             return;
         }
         final View chooserHeader = mResolverDrawerLayout.findViewById(R.id.chooser_header);
@@ -2597,28 +2608,36 @@
         return false;
     }
 
-    private boolean shouldShowContentPreview() {
-        return mMultiProfilePagerAdapter.getActiveListAdapter().getCount() > 0
-                && isSendAction(getTargetIntent());
+    /**
+     * The sticky content preview is shown only when we have a tabbed view. It's shown above
+     * the tabs so it is not part of the scrollable list. If we are not in tabbed view,
+     * we instead show the content preview as a regular list item.
+     */
+    private boolean shouldShowStickyContentPreview() {
+        return hasWorkProfile()
+                && ENABLE_TABBED_VIEW
+                && mMultiProfilePagerAdapter.getListAdapterForUserHandle(
+                        UserHandle.of(UserHandle.myUserId())).getCount() > 0
+                && isSendAction(getTargetIntent())
+                && getResources().getBoolean(R.bool.sharesheet_show_content_preview);
     }
 
-    private void updateContentPreview() {
-        if (shouldShowContentPreview()) {
-            showContentPreview();
+    private void updateStickyContentPreview() {
+        if (shouldShowStickyContentPreview()) {
+            showStickyContentPreview();
         } else {
-            hideContentPreview();
+            hideStickyContentPreview();
         }
     }
 
-    private void showContentPreview() {
+    private void showStickyContentPreview() {
         ViewGroup contentPreviewContainer = findViewById(R.id.content_preview_container);
         contentPreviewContainer.setVisibility(View.VISIBLE);
         ViewGroup contentPreviewView = createContentPreviewView(contentPreviewContainer);
         contentPreviewContainer.addView(contentPreviewView);
-        logActionShareWithPreview();
     }
 
-    private void hideContentPreview() {
+    private void hideStickyContentPreview() {
         ViewGroup contentPreviewContainer = findViewById(R.id.content_preview_container);
         contentPreviewContainer.removeAllViews();
         contentPreviewContainer.setVisibility(View.GONE);
@@ -2634,6 +2653,7 @@
     /**
      * Used to bind types of individual item including
      * {@link ChooserGridAdapter#VIEW_TYPE_NORMAL},
+     * {@link ChooserGridAdapter#VIEW_TYPE_CONTENT_PREVIEW},
      * {@link ChooserGridAdapter#VIEW_TYPE_PROFILE},
      * and {@link ChooserGridAdapter#VIEW_TYPE_AZ_LABEL}.
      */
@@ -2695,16 +2715,18 @@
         private int mChooserTargetWidth = 0;
         private boolean mShowAzLabelIfPoss;
 
+        private boolean mHideContentPreview = false;
         private boolean mLayoutRequested = false;
 
         private int mFooterHeight = 0;
 
         private static final int VIEW_TYPE_DIRECT_SHARE = 0;
         private static final int VIEW_TYPE_NORMAL = 1;
-        private static final int VIEW_TYPE_PROFILE = 2;
-        private static final int VIEW_TYPE_AZ_LABEL = 3;
-        private static final int VIEW_TYPE_CALLER_AND_RANK = 4;
-        private static final int VIEW_TYPE_FOOTER = 5;
+        private static final int VIEW_TYPE_CONTENT_PREVIEW = 2;
+        private static final int VIEW_TYPE_PROFILE = 3;
+        private static final int VIEW_TYPE_AZ_LABEL = 4;
+        private static final int VIEW_TYPE_CALLER_AND_RANK = 5;
+        private static final int VIEW_TYPE_FOOTER = 6;
 
         private static final int MAX_TARGETS_PER_ROW_PORTRAIT = 4;
         private static final int MAX_TARGETS_PER_ROW_LANDSCAPE = 8;
@@ -2765,6 +2787,17 @@
             return maxTargets;
         }
 
+        /**
+         * Hides the list item content preview.
+         * <p>Not to be confused with the sticky content preview which is above the
+         * personal and work tabs.
+         */
+        public void hideContentPreview() {
+            mHideContentPreview = true;
+            mLayoutRequested = true;
+            notifyDataSetChanged();
+        }
+
         public boolean consumeLayoutRequest() {
             boolean oldValue = mLayoutRequested;
             mLayoutRequested = false;
@@ -2773,7 +2806,8 @@
 
         public int getRowCount() {
             return (int) (
-                    getProfileRowCount()
+                    getContentPreviewRowCount()
+                            + getProfileRowCount()
                             + getServiceTargetRowCount()
                             + getCallerAndRankedTargetRowCount()
                             + getAzLabelRowCount()
@@ -2783,7 +2817,33 @@
             );
         }
 
+        /**
+         * Returns either {@code 0} or {@code 1} depending on whether we want to show the list item
+         * content preview. Not to be confused with the sticky content preview which is above the
+         * personal and work tabs.
+         */
+        public int getContentPreviewRowCount() {
+            // For the tabbed case we show the sticky content preview above the tabs,
+            // please refer to shouldShowStickyContentPreview
+            if (hasWorkProfile() && ENABLE_TABBED_VIEW) {
+                return 0;
+            }
+            if (!isSendAction(getTargetIntent())) {
+                return 0;
+            }
+
+            if (mHideContentPreview || mChooserListAdapter == null
+                    || mChooserListAdapter.getCount() == 0) {
+                return 0;
+            }
+
+            return 1;
+        }
+
         public int getProfileRowCount() {
+            if (hasWorkProfile() && ENABLE_TABBED_VIEW) {
+                return 0;
+            }
             return mChooserListAdapter.getOtherProfile() == null ? 0 : 1;
         }
 
@@ -2815,7 +2875,8 @@
         @Override
         public int getItemCount() {
             return (int) (
-                    getProfileRowCount()
+                    getContentPreviewRowCount()
+                            + getProfileRowCount()
                             + getServiceTargetRowCount()
                             + getCallerAndRankedTargetRowCount()
                             + getAzLabelRowCount()
@@ -2827,6 +2888,8 @@
         @Override
         public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
             switch (viewType) {
+                case VIEW_TYPE_CONTENT_PREVIEW:
+                    return new ItemViewHolder(createContentPreviewView(parent), false);
                 case VIEW_TYPE_PROFILE:
                     return new ItemViewHolder(createProfileView(parent), false);
                 case VIEW_TYPE_AZ_LABEL:
@@ -2866,7 +2929,10 @@
         public int getItemViewType(int position) {
             int count;
 
-            int countSum = (count = getProfileRowCount());
+            int countSum = (count = getContentPreviewRowCount());
+            if (count > 0 && position < countSum) return VIEW_TYPE_CONTENT_PREVIEW;
+
+            countSum += (count = getProfileRowCount());
             if (count > 0 && position < countSum) return VIEW_TYPE_PROFILE;
 
             countSum += (count = getServiceTargetRowCount());
@@ -3082,7 +3148,7 @@
         }
 
         int getListPosition(int position) {
-            position -= getProfileRowCount();
+            position -= getContentPreviewRowCount() + getProfileRowCount();
 
             final int serviceCount = mChooserListAdapter.getServiceTargetCount();
             final int serviceRows = (int) Math.ceil((float) serviceCount
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 9488e4f..7a0afa2 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -26,6 +26,7 @@
 import android.app.AppGlobals;
 import android.app.admin.DevicePolicyManager;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ContentResolver;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.IPackageManager;
@@ -108,7 +109,8 @@
         }
 
         final int callingUserId = getUserId();
-        final Intent newIntent = canForward(intentReceived, targetUserId);
+        final Intent newIntent = canForward(intentReceived, getUserId(), targetUserId,
+                mInjector.getIPackageManager(), getContentResolver());
         if (newIntent != null) {
             if (Intent.ACTION_CHOOSER.equals(newIntent.getAction())) {
                 Intent innerIntent = newIntent.getParcelableExtra(Intent.EXTRA_INTENT);
@@ -191,7 +193,8 @@
      * Check whether the intent can be forwarded to target user. Return the intent used for
      * forwarding if it can be forwarded, {@code null} otherwise.
      */
-    Intent canForward(Intent incomingIntent, int targetUserId)  {
+    static Intent canForward(Intent incomingIntent, int sourceUserId, int targetUserId,
+            IPackageManager packageManager, ContentResolver contentResolver)  {
         Intent forwardIntent = new Intent(incomingIntent);
         forwardIntent.addFlags(
                 Intent.FLAG_ACTIVITY_FORWARD_RESULT | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
@@ -220,11 +223,11 @@
         if (forwardIntent.getSelector() != null) {
             intentToCheck = forwardIntent.getSelector();
         }
-        String resolvedType = intentToCheck.resolveTypeIfNeeded(getContentResolver());
+        String resolvedType = intentToCheck.resolveTypeIfNeeded(contentResolver);
         sanitizeIntent(intentToCheck);
         try {
-            if (mInjector.getIPackageManager().
-                    canForwardTo(intentToCheck, resolvedType, getUserId(), targetUserId)) {
+            if (packageManager.canForwardTo(
+                    intentToCheck, resolvedType, sourceUserId, targetUserId)) {
                 return forwardIntent;
             }
         } catch (RemoteException e) {
@@ -267,7 +270,7 @@
     /**
      * Sanitize the intent in place.
      */
-    private void sanitizeIntent(Intent intent) {
+    private static void sanitizeIntent(Intent intent) {
         // Apps should not be allowed to target a specific package/ component in the target user.
         intent.setPackage(null);
         intent.setComponent(null);
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index a934de3..022573c 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -387,6 +387,11 @@
                     | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
             rdl.setOnApplyWindowInsetsListener(this::onApplyWindowInsets);
 
+            rdl.setMaxCollapsedHeight(hasWorkProfile() && ENABLE_TABBED_VIEW
+                    ? getResources().getDimensionPixelSize(
+                            R.dimen.resolver_empty_state_height_with_tabs)
+                    : getResources().getDimensionPixelSize(R.dimen.resolver_empty_state_height));
+
             mResolverDrawerLayout = rdl;
         }
 
@@ -548,13 +553,6 @@
             applyFooterView(mSystemWindowInsets.bottom);
         }
 
-        View emptyView = findViewById(R.id.empty);
-        if (emptyView != null) {
-            emptyView.setPadding(0, 0, 0, mSystemWindowInsets.bottom
-                                 + getResources().getDimensionPixelSize(
-                                         R.dimen.chooser_edge_margin_normal) * 2);
-        }
-
         return insets.consumeSystemWindowInsets();
     }
 
@@ -941,10 +939,13 @@
     }
 
     @Override // ResolverListCommunicator
-    public void onPostListReady(ResolverListAdapter listAdapter, boolean doPostProcessing) {
+    public final void onPostListReady(ResolverListAdapter listAdapter, boolean doPostProcessing) {
         if (isAutolaunching() || maybeAutolaunchActivity()) {
             return;
         }
+        if (shouldShowEmptyState(listAdapter)) {
+            mMultiProfilePagerAdapter.showEmptyState(listAdapter);
+        }
         if (doPostProcessing) {
             if (mMultiProfilePagerAdapter.getCurrentUserHandle().getIdentifier()
                     == UserHandle.myUserId()) {
@@ -1497,15 +1498,17 @@
     }
 
     private void setupViewVisibilities() {
-        int count = mMultiProfilePagerAdapter.getActiveListAdapter().getUnfilteredCount();
-        boolean shouldShowEmptyState = count == 0
-                && mMultiProfilePagerAdapter.getActiveListAdapter().getPlaceholderCount() == 0;
-        //TODO(arangelov): Handle empty state
-        if (!shouldShowEmptyState) {
-            addUseDifferentAppLabelIfNecessary(mMultiProfilePagerAdapter.getActiveListAdapter());
+        ResolverListAdapter activeListAdapter = mMultiProfilePagerAdapter.getActiveListAdapter();
+        if (!shouldShowEmptyState(activeListAdapter)) {
+            addUseDifferentAppLabelIfNecessary(activeListAdapter);
         }
     }
 
+    private boolean shouldShowEmptyState(ResolverListAdapter listAdapter) {
+        int count = listAdapter.getUnfilteredCount();
+        return count == 0 && listAdapter.getPlaceholderCount() == 0;
+    }
+
     /**
      * Add a label to signify that the user can pick a different app.
      * @param adapter The adapter used to provide data to item views.
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index 2321da1..ea84090 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -607,6 +607,10 @@
                 mIntents, userHandle);
     }
 
+    protected List<Intent> getIntents() {
+        return mIntents;
+    }
+
     /**
      * Necessary methods to communicate between {@link ResolverListAdapter}
      * and {@link ResolverActivity}.
diff --git a/core/java/com/android/internal/app/ResolverViewPager.java b/core/java/com/android/internal/app/ResolverViewPager.java
index 8ec94f1..84fed9c 100644
--- a/core/java/com/android/internal/app/ResolverViewPager.java
+++ b/core/java/com/android/internal/app/ResolverViewPager.java
@@ -57,10 +57,15 @@
         widthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY);
         int height = getMeasuredHeight();
         if (getChildCount() > 0) {
-            View firstChild = getChildAt(0);
-            firstChild.measure(widthMeasureSpec,
+            View child = getChildAt(0);
+            child.measure(widthMeasureSpec,
                     MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
-            height = firstChild.getMeasuredHeight();
+            if (height > child.getMeasuredHeight()) {
+                height = child.getMeasuredHeight();
+            }
+        }
+        if (height < getMinimumHeight()) {
+            height = getMinimumHeight();
         }
         heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 3322834..086b9d8 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -377,6 +377,11 @@
     public static final String NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN =
             "nav_bar_handle_show_over_lockscreen";
 
+    /**
+     * (boolean) Whether to enable user-drag resizing for PIP.
+     */
+    public static final String PIP_USER_RESIZE = "pip_user_resize";
+
     private SystemUiDeviceConfigFlags() {
     }
 }
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index e2985566..dfd700f 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -279,10 +279,8 @@
                 uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
                 fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp,
                 pkgDataInfoList);
-        // Enable tracing as soon as possible for the child process.
         if (pid == 0) {
             Zygote.disableExecuteOnly(targetSdkVersion);
-            Trace.setTracingEnabled(true, runtimeFlags);
 
             // Note that this event ends at the end of handleChildProc,
             Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
@@ -334,9 +332,6 @@
                 niceName, startChildZygote, instructionSet, appDataDir, isTopApp,
                 pkgDataInfoList);
 
-        // Enable tracing as soon as possible for the child process.
-        Trace.setTracingEnabled(true, runtimeFlags);
-
         // Note that this event ends at the end of handleChildProc.
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
 
@@ -393,11 +388,6 @@
                 uid, gid, gids, runtimeFlags, rlimits,
                 permittedCapabilities, effectiveCapabilities);
 
-        // Enable tracing as soon as we enter the system_server.
-        if (pid == 0) {
-            Trace.setTracingEnabled(true, runtimeFlags);
-        }
-
         // Set the Java Language thread priority to the default value for new apps.
         Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
 
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 7b6262b..ae54eb2 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -914,10 +914,6 @@
             bootTimingsTraceLog.traceEnd(); // PostZygoteInitGC
 
             bootTimingsTraceLog.traceEnd(); // ZygoteInit
-            // Disable tracing so that forked processes do not inherit stale tracing tags from
-            // Zygote.
-            Trace.setTracingEnabled(false, 0);
-
 
             Zygote.initNativeState(isPrimaryZygote);
 
diff --git a/core/java/com/android/internal/telephony/WapPushManagerParams.java b/core/java/com/android/internal/telephony/WapPushManagerParams.java
deleted file mode 100644
index eafb8f1..0000000
--- a/core/java/com/android/internal/telephony/WapPushManagerParams.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony;
-
-import android.telephony.WapPushManagerConnector;
-
-/**
- * WapPushManager constant value definitions.
- * @hide
- */
-public class WapPushManagerParams {
-    /**
-     * Application type activity
-     */
-    public static final int APP_TYPE_ACTIVITY = 0;
-
-    /**
-     * Application type service
-     */
-    public static final int APP_TYPE_SERVICE = 1;
-
-    /**
-     * Process Message return value
-     * Message is handled
-     */
-    public static final int MESSAGE_HANDLED = WapPushManagerConnector.RESULT_MESSAGE_HANDLED;
-
-    /**
-     * Process Message return value
-     * Application ID or content type was not found in the application ID table
-     */
-    public static final int APP_QUERY_FAILED = WapPushManagerConnector.RESULT_APP_QUERY_FAILED;
-
-    /**
-     * Process Message return value
-     * Receiver application signature check failed
-     */
-    public static final int SIGNATURE_NO_MATCH = WapPushManagerConnector.RESULT_SIGNATURE_NO_MATCH;
-
-    /**
-     * Process Message return value
-     * Receiver application was not found
-     */
-    public static final int INVALID_RECEIVER_NAME =
-            WapPushManagerConnector.RESULT_INVALID_RECEIVER_NAME;
-
-    /**
-     * Process Message return value
-     * Unknown exception
-     */
-    public static final int EXCEPTION_CAUGHT = WapPushManagerConnector.RESULT_EXCEPTION_CAUGHT;
-
-    /**
-     * Process Message return value
-     * Need further processing after WapPushManager message processing
-     */
-    public static final int FURTHER_PROCESSING = WapPushManagerConnector.RESULT_FURTHER_PROCESSING;
-}
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index e0c3823..b117e40 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -66,6 +66,12 @@
      */
     private int mMaxCollapsedHeightSmall;
 
+    /**
+     * Whether {@code mMaxCollapsedHeightSmall} was set explicitly as a layout attribute or
+     * inferred by {@code mMaxCollapsedHeight}.
+     */
+    private final boolean mIsMaxCollapsedHeightSmallExplicit;
+
     private boolean mSmallCollapsed;
 
     /**
@@ -146,6 +152,8 @@
         mMaxCollapsedHeightSmall = a.getDimensionPixelSize(
                 R.styleable.ResolverDrawerLayout_maxCollapsedHeightSmall,
                 mMaxCollapsedHeight);
+        mIsMaxCollapsedHeightSmallExplicit =
+                a.hasValue(R.styleable.ResolverDrawerLayout_maxCollapsedHeightSmall);
         mShowAtTop = a.getBoolean(R.styleable.ResolverDrawerLayout_showAtTop, false);
         a.recycle();
 
@@ -162,6 +170,21 @@
         setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
     }
 
+    /**
+     * Dynamically set the max collapsed height. Note this also updates the small collapsed
+     * height if it wasn't specified explicitly.
+     */
+    public void setMaxCollapsedHeight(int heightInPixels) {
+        if (heightInPixels == mMaxCollapsedHeight) {
+            return;
+        }
+        mMaxCollapsedHeight = heightInPixels;
+        if (!mIsMaxCollapsedHeightSmallExplicit) {
+            mMaxCollapsedHeightSmall = mMaxCollapsedHeight;
+        }
+        requestLayout();
+    }
+
     public void setSmallCollapsed(boolean smallCollapsed) {
         mSmallCollapsed = smallCollapsed;
         requestLayout();
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 2a56fd6..0fd9cc7 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -27,8 +27,6 @@
 #include <nativehelper/ScopedPrimitiveArray.h>
 #include <nativehelper/ScopedUtfChars.h>
 #include <android_runtime/AndroidRuntime.h>
-#include <android_runtime/android_util_AssetManager.h>
-#include <androidfw/AssetManager2.h>
 #include "Utils.h"
 #include "FontUtils.h"
 
@@ -212,63 +210,6 @@
     return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic);
 }
 
-static void releaseAsset(const void* ptr, void* context) {
-    delete static_cast<Asset*>(context);
-}
-
-static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong builderPtr,
-        jobject jassetMgr, jstring jpath, jint cookie, jboolean isAsset, jint ttcIndex,
-        jint weight, jint isItalic) {
-#ifdef __ANDROID__ // Layoutlib does not support native AssetManager
-    NPE_CHECK_RETURN_ZERO(env, jassetMgr);
-    NPE_CHECK_RETURN_ZERO(env, jpath);
-
-    NativeFamilyBuilder* builder = toNativeBuilder(builderPtr);
-    Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(env, jassetMgr);
-    if (NULL == mgr) {
-        builder->axes.clear();
-        return false;
-    }
-
-    ScopedUtfChars str(env, jpath);
-    if (str.c_str() == nullptr) {
-        builder->axes.clear();
-        return false;
-    }
-
-    std::unique_ptr<Asset> asset;
-    {
-      ScopedLock<AssetManager2> locked_mgr(*mgr);
-      if (isAsset) {
-          asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER);
-      } else if (cookie > 0) {
-          // Valid java cookies are 1-based, but AssetManager cookies are 0-based.
-          asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast<ApkAssetsCookie>(cookie - 1),
-                  Asset::ACCESS_BUFFER);
-      } else {
-          asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER);
-      }
-    }
-
-    if (nullptr == asset) {
-        builder->axes.clear();
-        return false;
-    }
-
-    const void* buf = asset->getBuffer(false);
-    if (NULL == buf) {
-        builder->axes.clear();
-        return false;
-    }
-
-    sk_sp<SkData> data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset,
-            asset.release()));
-    return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic);
-#else
-    return false;
-#endif
-}
-
 static void FontFamily_addAxisValue(CRITICAL_JNI_PARAMS_COMMA jlong builderPtr, jint tag, jfloat value) {
     NativeFamilyBuilder* builder = toNativeBuilder(builderPtr);
     builder->axes.push_back({static_cast<minikin::AxisTag>(tag), value});
@@ -284,8 +225,6 @@
     { "nAddFont",               "(JLjava/nio/ByteBuffer;III)Z", (void*)FontFamily_addFont },
     { "nAddFontWeightStyle",    "(JLjava/nio/ByteBuffer;III)Z",
             (void*)FontFamily_addFontWeightStyle },
-    { "nAddFontFromAssetManager",    "(JLandroid/content/res/AssetManager;Ljava/lang/String;IZIII)Z",
-            (void*)FontFamily_addFontFromAssetManager },
     { "nAddAxisValue",         "(JIF)V", (void*)FontFamily_addAxisValue },
 };
 
diff --git a/core/jni/android/graphics/fonts/Font.cpp b/core/jni/android/graphics/fonts/Font.cpp
index 8d84e87..bfb9bae 100644
--- a/core/jni/android/graphics/fonts/Font.cpp
+++ b/core/jni/android/graphics/fonts/Font.cpp
@@ -26,8 +26,6 @@
 #include "GraphicsJNI.h"
 #include <nativehelper/ScopedUtfChars.h>
 #include <android_runtime/AndroidRuntime.h>
-#include <android_runtime/android_util_AssetManager.h>
-#include <androidfw/AssetManager2.h>
 #include "Utils.h"
 #include "FontUtils.h"
 
@@ -48,14 +46,6 @@
     return reinterpret_cast<NativeFontBuilder*>(ptr);
 }
 
-static inline Asset* toAsset(jlong ptr) {
-    return reinterpret_cast<Asset*>(ptr);
-}
-
-static void releaseAsset(jlong asset) {
-    delete toAsset(asset);
-}
-
 static void releaseFont(jlong font) {
     delete reinterpret_cast<FontWrapper*>(font);
 }
@@ -79,54 +69,6 @@
 }
 
 // Regular JNI
-static jlong Font_Builder_getNativeAsset(
-    JNIEnv* env, jobject clazz, jobject assetMgr, jstring path, jboolean isAsset, jint cookie) {
-#ifdef __ANDROID__ // Layoutlib does not support native AssetManager
-    NPE_CHECK_RETURN_ZERO(env, assetMgr);
-    NPE_CHECK_RETURN_ZERO(env, path);
-
-    Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(env, assetMgr);
-    if (mgr == nullptr) {
-        return 0;
-    }
-
-    ScopedUtfChars str(env, path);
-    if (str.c_str() == nullptr) {
-        return 0;
-    }
-
-    std::unique_ptr<Asset> asset;
-    {
-      ScopedLock<AssetManager2> locked_mgr(*mgr);
-      if (isAsset) {
-          asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER);
-      } else if (cookie > 0) {
-          // Valid java cookies are 1-based, but AssetManager cookies are 0-based.
-          asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast<ApkAssetsCookie>(cookie - 1),
-                  Asset::ACCESS_BUFFER);
-      } else {
-          asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER);
-      }
-    }
-
-    return reinterpret_cast<jlong>(asset.release());
-#else
-    return 0;
-#endif
-}
-
-// Regular JNI
-static jobject Font_Builder_getAssetBuffer(JNIEnv* env, jobject clazz, jlong nativeAsset) {
-    Asset* asset = toAsset(nativeAsset);
-    return env->NewDirectByteBuffer(const_cast<void*>(asset->getBuffer(false)), asset->getLength());
-}
-
-// CriticalNative
-static jlong Font_Builder_getReleaseNativeAssetFunc(CRITICAL_JNI_PARAMS) {
-    return reinterpret_cast<jlong>(&releaseAsset);
-}
-
-// Regular JNI
 static jlong Font_Builder_initBuilder(JNIEnv*, jobject) {
     return reinterpret_cast<jlong>(new NativeFontBuilder());
 }
@@ -196,11 +138,6 @@
     { "nAddAxis", "(JIF)V", (void*) Font_Builder_addAxis },
     { "nBuild", "(JLjava/nio/ByteBuffer;Ljava/lang/String;IZI)J", (void*) Font_Builder_build },
     { "nGetReleaseNativeFont", "()J", (void*) Font_Builder_getReleaseNativeFont },
-
-    { "nGetNativeAsset", "(Landroid/content/res/AssetManager;Ljava/lang/String;ZI)J",
-      (void*) Font_Builder_getNativeAsset },
-    { "nGetAssetBuffer", "(J)Ljava/nio/ByteBuffer;", (void*) Font_Builder_getAssetBuffer },
-    { "nGetReleaseNativeAssetFunc", "()J", (void*) Font_Builder_getReleaseNativeAssetFunc },
 };
 
 int register_android_graphics_fonts_Font(JNIEnv* env) {
diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index fad5b0e..c523e2d 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -89,13 +89,13 @@
         ALOGE("%s: Invalid output metadata object.", __FUNCTION__);
         return BAD_VALUE;
     }
-    CameraMetadata* nativePtr = reinterpret_cast<CameraMetadata*>(env->GetLongField(thiz,
-            fields.metadata_ptr));
+    auto nativePtr = reinterpret_cast<std::shared_ptr<CameraMetadata> *>(
+            env->GetLongField(thiz, fields.metadata_ptr));
     if (nativePtr == NULL) {
         ALOGE("%s: Invalid native pointer in java metadata object.", __FUNCTION__);
         return BAD_VALUE;
     }
-    *metadata = *nativePtr;
+    *metadata = *(nativePtr->get());
     return OK;
 }
 
@@ -171,12 +171,15 @@
 
 // Less safe access to native pointer. Does NOT throw any Java exceptions if NULL.
 static CameraMetadata* CameraMetadata_getPointerNoThrow(JNIEnv *env, jobject thiz) {
-
-    if (thiz == NULL) {
-        return NULL;
+    if (thiz == nullptr) {
+        return nullptr;
     }
-
-    return reinterpret_cast<CameraMetadata*>(env->GetLongField(thiz, fields.metadata_ptr));
+    auto metadata = reinterpret_cast<std::shared_ptr<CameraMetadata> *>(
+            env->GetLongField(thiz, fields.metadata_ptr));
+    if (metadata == nullptr) {
+        return nullptr;
+    }
+    return metadata->get();
 }
 
 // Safe access to native pointer from object. Throws if not possible to access.
@@ -205,7 +208,7 @@
 static jlong CameraMetadata_allocate(JNIEnv *env, jobject thiz) {
     ALOGV("%s", __FUNCTION__);
 
-    return reinterpret_cast<jlong>(new CameraMetadata());
+    return reinterpret_cast<jlong>(new std::shared_ptr<CameraMetadata>(new CameraMetadata()));
 }
 
 static jlong CameraMetadata_allocateCopy(JNIEnv *env, jobject thiz,
@@ -214,12 +217,12 @@
 
     CameraMetadata* otherMetadata =
             CameraMetadata_getPointerThrow(env, other, "other");
-
     // In case of exception, return
     if (otherMetadata == NULL) return NULL;
 
-    // Clone native metadata and return new pointer
-    return reinterpret_cast<jlong>(new CameraMetadata(*otherMetadata));
+    // Clone native metadata and return new pointer.
+    auto clonedMetadata = new CameraMetadata(*otherMetadata);
+    return reinterpret_cast<jlong>(new std::shared_ptr<CameraMetadata>(clonedMetadata));
 }
 
 
@@ -256,14 +259,16 @@
 static void CameraMetadata_close(JNIEnv *env, jobject thiz) {
     ALOGV("%s", __FUNCTION__);
 
-    CameraMetadata* metadata = CameraMetadata_getPointerNoThrow(env, thiz);
-
-    if (metadata != NULL) {
-        delete metadata;
-        env->SetLongField(thiz, fields.metadata_ptr, 0);
+    if (thiz != nullptr) {
+        auto metadata = reinterpret_cast<std::shared_ptr<CameraMetadata> *>(
+                env->GetLongField(thiz, fields.metadata_ptr));
+        if (metadata != nullptr) {
+            delete metadata;
+            env->SetLongField(thiz, fields.metadata_ptr, 0);
+        }
     }
 
-    LOG_ALWAYS_FATAL_IF(CameraMetadata_getPointerNoThrow(env, thiz) != NULL,
+    LOG_ALWAYS_FATAL_IF(CameraMetadata_getPointerNoThrow(env, thiz) != nullptr,
                         "Expected the native ptr to be 0 after #close");
 }
 
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index b4590f4..87be5f6 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -147,6 +147,7 @@
 static struct {
     jfieldID    mCriteria;
     jfieldID    mAllowPrivilegedPlaybackCapture;
+    jfieldID    mVoiceCommunicationCaptureAllowed;
     // other fields unused by JNI
 } gAudioMixingRuleFields;
 
@@ -1919,6 +1920,8 @@
     jobject jRuleCriteria = env->GetObjectField(jRule, gAudioMixingRuleFields.mCriteria);
     nAudioMix->mAllowPrivilegedPlaybackCapture =
             env->GetBooleanField(jRule, gAudioMixingRuleFields.mAllowPrivilegedPlaybackCapture);
+    nAudioMix->mVoiceCommunicationCaptureAllowed =
+            env->GetBooleanField(jRule, gAudioMixingRuleFields.mVoiceCommunicationCaptureAllowed);
     env->DeleteLocalRef(jRule);
     jobjectArray jCriteria = (jobjectArray)env->CallObjectMethod(jRuleCriteria,
                                                                  gArrayListMethods.toArray);
@@ -2682,6 +2685,9 @@
     gAudioMixingRuleFields.mAllowPrivilegedPlaybackCapture =
             GetFieldIDOrDie(env, audioMixingRuleClass, "mAllowPrivilegedPlaybackCapture", "Z");
 
+    gAudioMixingRuleFields.mVoiceCommunicationCaptureAllowed =
+            GetFieldIDOrDie(env, audioMixingRuleClass, "mVoiceCommunicationCaptureAllowed", "Z");
+
     jclass audioMixMatchCriterionClass =
                 FindClassOrDie(env, "android/media/audiopolicy/AudioMixingRule$AudioMixMatchCriterion");
     gAudioMixMatchCriterionClass = MakeGlobalRefOrDie(env,audioMixMatchCriterionClass);
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index b523811..0d0dc3e 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -263,6 +263,8 @@
             which_heap = HEAP_NATIVE;
         } else if (base::StartsWith(name, "[anon:scudo:")) {
             which_heap = HEAP_NATIVE;
+        } else if (base::StartsWith(name, "[anon:GWP-ASan")) {
+            which_heap = HEAP_NATIVE;
         } else if (base::StartsWith(name, "[stack")) {
             which_heap = HEAP_STACK;
         } else if (base::StartsWith(name, "[anon:stack_and_tls:")) {
diff --git a/core/jni/android_os_incremental_IncrementalManager.cpp b/core/jni/android_os_incremental_IncrementalManager.cpp
index d41e982..44bff01 100644
--- a/core/jni/android_os_incremental_IncrementalManager.cpp
+++ b/core/jni/android_os_incremental_IncrementalManager.cpp
@@ -37,10 +37,28 @@
     return (jboolean)IncFs_IsIncFsPath(path.c_str());
 }
 
-static const JNINativeMethod method_table[] = {
-        {"nativeIsEnabled", "()Z", (void*)nativeIsEnabled},
-        {"nativeIsIncrementalPath", "(Ljava/lang/String;)Z", (void*)nativeIsIncrementalPath},
-};
+static jbyteArray nativeUnsafeGetFileSignature(JNIEnv* env, jobject clazz, jstring javaPath) {
+    ScopedUtfChars path(env, javaPath);
+
+    char signature[INCFS_MAX_SIGNATURE_SIZE];
+    size_t size = sizeof(signature);
+    if (IncFs_UnsafeGetSignatureByPath(path.c_str(), signature, &size) < 0) {
+        return nullptr;
+    }
+
+    jbyteArray result = env->NewByteArray(size);
+    if (result != nullptr) {
+        env->SetByteArrayRegion(result, 0, size, (const jbyte*)signature);
+    }
+    return result;
+}
+
+static const JNINativeMethod method_table[] = {{"nativeIsEnabled", "()Z", (void*)nativeIsEnabled},
+                                               {"nativeIsIncrementalPath", "(Ljava/lang/String;)Z",
+                                                (void*)nativeIsIncrementalPath},
+                                               {"nativeUnsafeGetFileSignature",
+                                                "(Ljava/lang/String;)[B",
+                                                (void*)nativeUnsafeGetFileSignature}};
 
 int register_android_os_incremental_IncrementalManager(JNIEnv* env) {
     return jniRegisterNativeMethods(env, "android/os/incremental/IncrementalManager",
diff --git a/core/jni/android_service_DataLoaderService.cpp b/core/jni/android_service_DataLoaderService.cpp
index b307a73..ed0d381 100644
--- a/core/jni/android_service_DataLoaderService.cpp
+++ b/core/jni/android_service_DataLoaderService.cpp
@@ -75,8 +75,9 @@
         {"nativeStartDataLoader", "(I)Z", (void*)nativeStartDataLoader},
         {"nativeStopDataLoader", "(I)Z", (void*)nativeStopDataLoader},
         {"nativeDestroyDataLoader", "(I)Z", (void*)nativeDestroyDataLoader},
-        {"nativePrepareImage", "(ILjava/util/Collection;Ljava/util/Collection;)Z", (void*)nativePrepareImage},
-        {"nativeWriteData", "(JLjava/lang/String;JJLandroid/os/ParcelFileDescriptor;)V", (void*)nativeWriteData},
+        {"nativePrepareImage", "(ILjava/util/List;Ljava/util/List;)Z", (void*)nativePrepareImage},
+        {"nativeWriteData", "(JLjava/lang/String;JJLandroid/os/ParcelFileDescriptor;)V",
+         (void*)nativeWriteData},
 };
 
 }  // namespace
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 92941b8..7a9a3f8 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -611,6 +611,12 @@
 
   // Set the jemalloc decay time to 1.
   mallopt(M_DECAY_TIME, 1);
+
+  // Maybe initialize GWP-ASan here. Must be called after
+  // mallopt(M_SET_ZYGOTE_CHILD).
+  bool ForceEnableGwpAsan = false;
+  android_mallopt(M_INITIALIZE_GWP_ASAN, &ForceEnableGwpAsan,
+                  sizeof(ForceEnableGwpAsan));
 }
 
 static void SetUpSeccompFilter(uid_t uid, bool is_child_zygote) {
@@ -1490,6 +1496,11 @@
     std::string mirrorCurPackageProfile = StringPrintf("/data_mirror/cur_profiles/%d/%s",
         user_id, packageName.c_str());
 
+    if (access(mirrorCurPackageProfile.c_str(), F_OK) != 0) {
+      ALOGW("Can't access app profile directory: %s", mirrorCurPackageProfile.c_str());
+      continue;
+    }
+
     PrepareDir(actualCurPackageProfile, DEFAULT_DATA_DIR_PERMISSION, uid, uid, fail_fn);
     BindMount(mirrorCurPackageProfile, actualCurPackageProfile, fail_fn);
   }
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 77d4e87..6d9e8ab 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -80,6 +80,11 @@
     }
     optional Accessibility accessibility = 2;
 
+    message AdaptiveSleep {
+        optional SettingProto enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional AdaptiveSleep adaptive_sleep = 78;
+
     // Origins for which browsers should allow geolocation by default.
     // The value is a space-separated list of origins.
     optional SettingProto allowed_geolocation_origins = 3;
@@ -574,5 +579,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 78;
+    // Next tag = 79;
 }
diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto
index f8143de8..b723b53 100644
--- a/core/proto/android/providers/settings/system.proto
+++ b/core/proto/android/providers/settings/system.proto
@@ -161,6 +161,8 @@
         optional SettingProto brightness_for_vr = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto brightness_mode = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto auto_brightness_adj = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto brightness_float = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto brightness_for_vr_float = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional Screen screen = 22;
 
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 546c5a0..4bef2e3 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -170,30 +170,13 @@
 message ConstantsProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
-    // Minimum # of idle jobs that must be ready in order to force the JMS to
-    // schedule things early.
-    optional int32 min_idle_count = 1;
-    // Minimum # of charging jobs that must be ready in order to force the JMS
-    // to schedule things early.
-    optional int32 min_charging_count = 2;
-    // Minimum # of "battery not low" jobs that must be ready in order to force
-    // the JMS to schedule things early.
-    optional int32 min_battery_not_low_count = 3;
-    // Minimum # of "storage not low" jobs that must be ready in order to force
-    // the JMS to schedule things early.
-    optional int32 min_storage_not_low_count = 4;
-    // Minimum # of connectivity jobs that must be ready in order to force the
-    // JMS to schedule things early. 1 == Run connectivity jobs as soon as
-    // ready.
-    optional int32 min_connectivity_count = 5;
-    // Minimum # of content trigger jobs that must be ready in order to force
-    // the JMS to schedule things early.
-    optional int32 min_content_count = 6;
-    // Minimum # of jobs (with no particular constraints) for which the JMS will
-    // be happy running some work early. This (and thus the other min counts)
-    // is now set to 1, to prevent any batching at this level. Since we now do
-    // batching through doze, that is a much better mechanism.
-    optional int32 min_ready_jobs_count = 7;
+    reserved 1; // min_idle_count
+    reserved 2; // min_charging_count
+    reserved 3; // min_battery_not_low_count
+    reserved 4; // min_storage_not_low_count
+    reserved 5; // min_connectivity_count
+    reserved 6; // min_content_count
+    reserved 7; // min_ready_jobs_count
     // Minimum # of non-ACTIVE jobs for which the JMS will be happy running some work early.
     optional int32 min_ready_non_active_jobs_count = 29;
     // Don't batch a non-ACTIVE job if it's been delayed due to force batching attempts for
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ff69671..d1031f4 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -96,6 +96,7 @@
     <protected-broadcast android:name="android.intent.action.OVERLAY_PRIORITY_CHANGED" />
     <protected-broadcast android:name="android.intent.action.MY_PACKAGE_SUSPENDED" />
     <protected-broadcast android:name="android.intent.action.MY_PACKAGE_UNSUSPENDED" />
+    <protected-broadcast android:name="android.intent.action.LOAD_DATA" />
 
     <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGED" />
     <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGING" />
@@ -1911,6 +1912,15 @@
     <permission android:name="android.permission.VIBRATE_ALWAYS_ON"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi Allows access to the vibrator state.
+         <p>Protection level: signature
+         @hide
+    -->
+    <permission android:name="android.permission.ACCESS_VIBRATOR_STATE"
+        android:label="@string/permdesc_vibrator_state"
+        android:description="@string/permdesc_vibrator_state"
+        android:protectionLevel="signature|privileged" />
+
     <!-- Allows using PowerManager WakeLocks to keep processor from sleeping or screen
          from dimming.
          <p>Protection level: normal
@@ -2376,11 +2386,18 @@
          @hide -->
     <permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
         android:protectionLevel="signature|installer|telephony" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
 
     <!-- Allows interaction across profiles in the same profile group. -->
     <permission android:name="android.permission.INTERACT_ACROSS_PROFILES"
         android:protectionLevel="signature|appop|documenter|wellbeing" />
 
+    <!-- Allows configuring apps to have the INTERACT_ACROSS_PROFILES permission so that they can
+         interact across profiles in the same profile group.
+         @hide -->
+    <permission android:name="android.permission.CONFIGURE_INTERACT_ACROSS_PROFILES"
+        android:protectionLevel="signature" />
+
     <!-- @SystemApi @hide Allows an application to call APIs that allow it to query and manage
          users on the device. This permission is not available to
          third party applications. -->
@@ -4030,6 +4047,15 @@
         android:protectionLevel="signature|privileged|development|appop|retailDemo" />
     <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
 
+    <!-- Allows a data loader to read a package's access logs. The access logs contain the
+         set of pages referenced over time.
+         <p>Declaring the permission implies intention to use the API and the user of the
+         device can grant permission through the Settings application.
+         <p>Protection level: signature|privileged|appop -->
+    <permission android:name="android.permission.LOADER_USAGE_STATS"
+        android:protectionLevel="signature|privileged|appop" />
+    <uses-permission android:name="android.permission.LOADER_USAGE_STATS" />
+
     <!-- @hide @SystemApi Allows an application to observe usage time of apps. The app can register
          for callbacks when apps reach a certain usage time limit, etc. -->
     <permission android:name="android.permission.OBSERVE_APP_USAGE"
diff --git a/core/res/res/drawable/ic_no_apps.xml b/core/res/res/drawable/ic_no_apps.xml
new file mode 100644
index 0000000..4d296bd
--- /dev/null
+++ b/core/res/res/drawable/ic_no_apps.xml
@@ -0,0 +1,4 @@
+<vector android:height="32dp" android:viewportHeight="24"
+    android:viewportWidth="24" android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@color/resolver_empty_state_icon" android:fillType="evenOdd" android:pathData="M18.8123,20.0145L21.3999,22.6021L22.602,21.4L2.602,1.4L1.3999,2.6021L3.9873,5.1895C3.6248,5.552 3.3998,6.052 3.3998,6.602C3.3998,7.702 4.2998,8.602 5.3998,8.602C5.9498,8.602 6.4498,8.377 6.8123,8.0145L9.9873,11.1895C9.6248,11.552 9.3998,12.052 9.3998,12.602C9.3998,13.702 10.2998,14.602 11.3998,14.602C11.9498,14.602 12.4498,14.377 12.8123,14.0145L15.9873,17.1895C15.6248,17.552 15.3998,18.052 15.3998,18.602C15.3998,19.702 16.2998,20.602 17.3998,20.602C17.9498,20.602 18.4498,20.377 18.8123,20.0145ZM17.3998,8.602C16.2998,8.602 15.3998,7.7021 15.3998,6.602C15.3998,5.502 16.2998,4.602 17.3998,4.602C18.4998,4.602 19.3998,5.502 19.3998,6.602C19.3998,7.7021 18.4998,8.602 17.3998,8.602ZM5.3998,14.6021C6.4998,14.6021 7.3998,13.7021 7.3998,12.6021C7.3998,11.5021 6.4998,10.6021 5.3998,10.6021C4.2998,10.6021 3.3998,11.5021 3.3998,12.6021C3.3998,13.7021 4.2998,14.6021 5.3998,14.6021ZM7.3998,18.6021C7.3998,19.7021 6.4998,20.6021 5.3998,20.6021C4.2998,20.6021 3.3998,19.7021 3.3998,18.6021C3.3998,17.5021 4.2998,16.6021 5.3998,16.6021C6.4998,16.6021 7.3998,17.5021 7.3998,18.6021ZM13.3998,18.6021C13.3998,19.7021 12.4998,20.6021 11.3998,20.6021C10.2998,20.6021 9.3998,19.7021 9.3998,18.6021C9.3998,17.5021 10.2998,16.6021 11.3998,16.6021C12.4998,16.6021 13.3998,17.5021 13.3998,18.6021ZM13.3999,6.602C13.3999,7.547 12.7357,8.3444 11.8511,8.5504L9.4516,6.1509C9.6576,5.2663 10.4549,4.602 11.3999,4.602C12.4999,4.602 13.3999,5.502 13.3999,6.602ZM17.8511,14.5504C18.7357,14.3444 19.3999,13.547 19.3999,12.6021C19.3999,11.5021 18.4999,10.6021 17.3999,10.6021C16.4549,10.6021 15.6576,11.2663 15.4516,12.1509L17.8511,14.5504Z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_sharing_disabled.xml b/core/res/res/drawable/ic_sharing_disabled.xml
new file mode 100644
index 0000000..d488cdb
--- /dev/null
+++ b/core/res/res/drawable/ic_sharing_disabled.xml
@@ -0,0 +1,4 @@
+<vector android:height="32dp" android:viewportHeight="24"
+    android:viewportWidth="24" android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@color/resolver_empty_state_icon" android:fillType="evenOdd" android:pathData="M19.7225,20.9245L21.2011,22.4031L22.4032,21.201L2.8022,1.6L1.6001,2.8021L8.1265,9.3284L7.64,9.612C7.1,9.112 6.39,8.802 5.6,8.802C3.94,8.802 2.6,10.142 2.6,11.802C2.6,13.462 3.94,14.802 5.6,14.802C6.39,14.802 7.1,14.492 7.64,13.992L14.69,18.112C14.64,18.332 14.6,18.562 14.6,18.802C14.6,20.462 15.94,21.802 17.6,21.802C18.43,21.802 19.18,21.467 19.7225,20.9245ZM16.8938,18.0958L18.3063,19.5083C18.125,19.6895 17.875,19.802 17.6,19.802C17.05,19.802 16.6,19.352 16.6,18.802C16.6,18.527 16.7125,18.277 16.8938,18.0958ZM15.1871,16.3891L9.3881,10.5901L8.51,11.102C8.56,11.332 8.6,11.562 8.6,11.802C8.6,12.042 8.56,12.272 8.51,12.502L15.1871,16.3891ZM15.56,6.992L12.4382,8.8119L11.1766,7.5503L14.69,5.502C14.64,5.282 14.6,5.042 14.6,4.802C14.6,3.142 15.94,1.802 17.6,1.802C19.26,1.802 20.6,3.142 20.6,4.802C20.6,6.462 19.26,7.802 17.6,7.802C16.81,7.802 16.09,7.492 15.56,6.992ZM18.6,4.802C18.6,4.252 18.15,3.802 17.6,3.802C17.05,3.802 16.6,4.252 16.6,4.802C16.6,5.352 17.05,5.802 17.6,5.802C18.15,5.802 18.6,5.352 18.6,4.802ZM5.6,12.802C5.05,12.802 4.6,12.352 4.6,11.802C4.6,11.252 5.05,10.802 5.6,10.802C6.15,10.802 6.6,11.252 6.6,11.802C6.6,12.352 6.15,12.802 5.6,12.802Z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_work_apps_off.xml b/core/res/res/drawable/ic_work_apps_off.xml
new file mode 100644
index 0000000..e91806f
--- /dev/null
+++ b/core/res/res/drawable/ic_work_apps_off.xml
@@ -0,0 +1,4 @@
+<vector android:height="32dp" android:viewportHeight="24"
+    android:viewportWidth="24" android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@color/resolver_empty_state_icon" android:pathData="M22,7.95c0.05,-1.11 -0.84,-2 -1.95,-1.95L16,6L16,3.95c0,-1.11 -0.84,-2 -1.95,-1.95h-4C8.94,1.95 8,2.84 8,3.95v0.32l14,14L22,7.95zM14,6h-4L10,4h4v2zM21.54,20.28l-7.56,-7.56v0.01l-1.7,-1.7h0.01L7.21,5.95 3.25,1.99 1.99,3.27 4.69,6h-0.64c-1.11,0 -1.99,0.86 -1.99,1.97l-0.01,11.02c0,1.11 0.89,2.01 2,2.01h15.64l2.05,2.02L23,21.75l-1.46,-1.47z"/>
+</vector>
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index 4e8a41f..24a21eb 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -75,7 +75,8 @@
             <TabWidget
                 android:id="@android:id/tabs"
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content">
+                android:layout_height="wrap_content"
+                android:visibility="gone">
             </TabWidget>
             <FrameLayout
                 android:id="@android:id/tabcontent"
@@ -89,15 +90,4 @@
         </LinearLayout>
     </TabHost>
 
-    <TextView android:id="@+id/empty"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:layout_alwaysShow="true"
-              android:background="?attr/colorBackgroundFloating"
-              android:text="@string/noApplications"
-              android:padding="@dimen/chooser_edge_margin_normal"
-              android:gravity="center"
-              android:elevation="1dp"
-              android:visibility="gone"/>
-
 </com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/layout/chooser_list_per_profile.xml b/core/res/res/layout/chooser_list_per_profile.xml
index 212813f..38b1b52 100644
--- a/core/res/res/layout/chooser_list_per_profile.xml
+++ b/core/res/res/layout/chooser_list_per_profile.xml
@@ -13,15 +13,21 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-
-<com.android.internal.widget.RecyclerView
+<RelativeLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:layoutManager="com.android.internal.widget.GridLayoutManager"
-    android:id="@+id/resolver_list"
-    android:clipToPadding="false"
-    android:background="?attr/colorBackgroundFloating"
-    android:scrollbars="none"
-    android:elevation="1dp"
-    android:nestedScrollingEnabled="true"/>
\ No newline at end of file
+    android:layout_marginTop="8dp">
+    <com.android.internal.widget.RecyclerView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layoutManager="com.android.internal.widget.GridLayoutManager"
+        android:id="@+id/resolver_list"
+        android:clipToPadding="false"
+        android:background="?attr/colorBackgroundFloating"
+        android:scrollbars="none"
+        android:elevation="1dp"
+        android:nestedScrollingEnabled="true" />
+
+    <include layout="@layout/resolver_empty_states" />
+</RelativeLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/resolver_empty_states.xml b/core/res/res/layout/resolver_empty_states.xml
new file mode 100644
index 0000000..6959e9c
--- /dev/null
+++ b/core/res/res/layout/resolver_empty_states.xml
@@ -0,0 +1,58 @@
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/resolver_empty_state"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/resolver_empty_state_height"
+    android:orientation="vertical"
+    android:layout_centerInParent="true"
+    android:gravity="center"
+    android:visibility="gone"
+    android:paddingStart="24dp"
+    android:paddingEnd="24dp">
+    <ImageView
+        android:id="@+id/resolver_empty_state_icon"
+        android:layout_width="32dp"
+        android:layout_height="32dp"/>
+    <TextView
+        android:id="@+id/resolver_empty_state_title"
+        android:layout_marginTop="16dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?attr/textAppearanceMedium"
+        android:textColor="@color/resolver_empty_state_text"
+        android:textSize="18sp"/>
+    <TextView
+        android:id="@+id/resolver_empty_state_subtitle"
+        android:layout_marginTop="8dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textColor="@color/resolver_empty_state_text"
+        android:textSize="14sp"
+        android:gravity="center_horizontal" />
+    <Button
+        android:id="@+id/resolver_empty_state_button"
+        android:layout_marginTop="24dp"
+        android:text="@string/resolver_switch_on_work"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:background="@null"
+        android:textAppearance="?attr/textAppearanceMedium"
+        android:textSize="14sp"
+        android:textColor="@color/resolver_tabs_active_color"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index 757cd53..6dfe24b 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -21,7 +21,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:maxWidth="@dimen/resolver_max_width"
-    android:maxCollapsedHeight="192dp"
+    android:maxCollapsedHeight="@dimen/resolver_empty_state_height"
     android:maxCollapsedHeightSmall="56dp"
     android:id="@id/contentPanel">
 
@@ -91,7 +91,8 @@
             <TabWidget
                 android:id="@android:id/tabs"
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content">
+                android:layout_height="wrap_content"
+                android:visibility="gone">
             </TabWidget>
             <FrameLayout
                 android:id="@android:id/tabcontent"
@@ -101,6 +102,7 @@
                     android:id="@+id/profile_pager"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
+                    android:minHeight="@dimen/resolver_empty_state_height"
                     android:divider="?attr/dividerVertical"
                     android:footerDividersEnabled="false"
                     android:headerDividersEnabled="false"
@@ -116,17 +118,6 @@
         android:background="?attr/colorBackgroundFloating"
         android:foreground="?attr/dividerVertical" />
 
-    <TextView android:id="@+id/empty"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:background="?attr/colorBackgroundFloating"
-              android:elevation="@dimen/resolver_elevation"
-              android:layout_alwaysShow="true"
-              android:text="@string/noApplications"
-              android:padding="32dp"
-              android:gravity="center"
-              android:visibility="gone" />
-
     <LinearLayout
         android:id="@+id/button_bar"
         android:visibility="gone"
diff --git a/core/res/res/layout/resolver_list_per_profile.xml b/core/res/res/layout/resolver_list_per_profile.xml
index 6d8d348..d481eff 100644
--- a/core/res/res/layout/resolver_list_per_profile.xml
+++ b/core/res/res/layout/resolver_list_per_profile.xml
@@ -14,18 +14,26 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ListView
+<RelativeLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:id="@+id/resolver_list"
-    android:clipToPadding="false"
-    android:background="?attr/colorBackgroundFloating"
-    android:elevation="@dimen/resolver_elevation"
-    android:nestedScrollingEnabled="true"
-    android:scrollbarStyle="outsideOverlay"
-    android:scrollIndicators="top|bottom"
-    android:divider="@null"
-    android:footerDividersEnabled="false"
-    android:headerDividersEnabled="false"
-    android:dividerHeight="0dp" />
\ No newline at end of file
+    android:layout_height="match_parent"
+    android:layout_marginTop="8dp">
+    <ListView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:id="@+id/resolver_list"
+        android:clipToPadding="false"
+        android:background="?attr/colorBackgroundFloating"
+        android:elevation="@dimen/resolver_elevation"
+        android:nestedScrollingEnabled="true"
+        android:scrollbarStyle="outsideOverlay"
+        android:scrollIndicators="top|bottom"
+        android:divider="@null"
+        android:footerDividersEnabled="false"
+        android:headerDividersEnabled="false"
+        android:dividerHeight="0dp" />
+
+    <include layout="@layout/resolver_empty_states" />
+</RelativeLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml
index b546738..4f88017 100644
--- a/core/res/res/layout/resolver_list_with_default.xml
+++ b/core/res/res/layout/resolver_list_with_default.xml
@@ -183,6 +183,7 @@
                     android:id="@+id/profile_pager"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
+                    android:minHeight="@dimen/resolver_empty_state_height"
                     android:dividerHeight="1dp"
                     android:divider="?attr/dividerVertical"
                     android:footerDividersEnabled="false"
diff --git a/core/res/res/values-h480dp/bools.xml b/core/res/res/values-h480dp/bools.xml
new file mode 100644
index 0000000..65e3ae6
--- /dev/null
+++ b/core/res/res/values-h480dp/bools.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<resources>
+    <bool name="sharesheet_show_content_preview">true</bool>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml
index 33caeb6..7f77e6c 100644
--- a/core/res/res/values-night/colors.xml
+++ b/core/res/res/values-night/colors.xml
@@ -34,4 +34,6 @@
     <color name="chooser_gradient_highlight">@color/loading_gradient_highlight_color_dark</color>
 
     <color name="resolver_tabs_active_color">#FF8AB4F8</color>
+    <color name="resolver_empty_state_text">#FFFFFF</color>
+    <color name="resolver_empty_state_icon">#FFFFFF</color>
 </resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 20901e0..7d8b8db 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3790,6 +3790,12 @@
         <attr name="description" />
         <!-- Brief summary of the target of accessibility shortcut purpose or behavior. -->
         <attr name="summary" />
+        <!-- Animated image of the target of accessibility shortcut purpose or behavior, to help
+             users understand how the target of accessibility shortcut can help them.-->
+        <attr name="animatedImageDrawable" format="reference"/>
+        <!-- Html description of the target of accessibility shortcut purpose or behavior, to help
+             users understand how the target of accessibility shortcut can help them. -->
+        <attr name="htmlDescription" format="string"/>
     </declare-styleable>
 
     <!-- Use <code>print-service</code> as the root tag of the XML resource that
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index 29f9f6c..c5127dc 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -29,4 +29,5 @@
          <p>The main purpose is for OEMs to customize the rendering of the
          lockscreen, setting this to true should come with customized drawables. -->
     <bool name="use_lock_pattern_drawable">false</bool>
+    <bool name="sharesheet_show_content_preview">false</bool>
 </resources>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 731e2ec..bdec096 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -226,4 +226,6 @@
     <color name="resolver_text_color_secondary_dark">#ffC4C6C6</color>
     <color name="resolver_tabs_active_color">#FF1A73E8</color>
     <color name="resolver_tabs_inactive_color">#FF80868B</color>
+    <color name="resolver_empty_state_text">#FF202124</color>
+    <color name="resolver_empty_state_icon">#FF5F6368</color>
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f17cd45..9282925 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -244,7 +244,7 @@
          correctly use them when installed on your device.  Otherwise, keep this disabled
          so that applications can still use their own mechanisms. -->
     <bool name="config_enableAutoPowerModes">false</bool>
-
+ 
     <!-- Whether (if true) this is a kind of device that can be moved around (eg. phone/laptop),
          or (if false) something for which movement is either not measurable or should not count
          toward power states (eg. tv/soundbar). -->
@@ -269,6 +269,11 @@
          when there's no network connection. If the scan doesn't timeout, use zero -->
     <integer name="config_radioScanningTimeout">0</integer>
 
+    <!-- When true, Android uses the PAC implementation included in WebView to handle
+         networks with PAC scripts.
+         When false, Android's own implementation of libpac is used.-->
+    <bool name ="config_useWebViewPacProcessor">false</bool>
+
     <!-- XXXXX NOTE THE FOLLOWING RESOURCES USE THE WRONG NAMING CONVENTION.
          Please don't copy them, copy anything else. -->
 
@@ -1258,32 +1263,63 @@
     -->
     <integer name="config_doubleTapOnHomeBehavior">0</integer>
 
-    <!-- Minimum screen brightness setting allowed by the power manager.
-         The user is forbidden from setting the brightness below this level. -->
+    <!-- Note: This setting is deprecated, please use
+    config_screenBrightnessSettingMinimumFloat instead -->
     <integer name="config_screenBrightnessSettingMinimum">10</integer>
 
-    <!-- Maximum screen brightness allowed by the power manager.
-         The user is forbidden from setting the brightness above this level. -->
+    <!-- Note: This setting is deprecated, please use
+    config_screenBrightnessSettingMaximumFloat instead -->
     <integer name="config_screenBrightnessSettingMaximum">255</integer>
 
-    <!-- Default screen brightness setting.
-         Must be in the range specified by minimum and maximum. -->
+    <!-- Note: This setting is deprecated, please use
+    config_screenBrightnessSettingDefaultFloat instead -->
     <integer name="config_screenBrightnessSettingDefault">102</integer>
 
-    <!-- Default screen brightness for VR setting. -->
+    <!-- Minimum screen brightness setting allowed by power manager.
+        The user is forbidden from setting the brightness below this level.
+        Equivalent to 10/255. -->
+    <item name="config_screenBrightnessSettingMinimumFloat" format="float" type="dimen">0.035433073</item>
+
+    <!-- Maximum screen brightness allowed by the power manager.
+         The user is forbidden from setting the brightness above this level.
+         This value is a fraction between 3.5% and 100%. -->
+    <item name="config_screenBrightnessSettingMaximumFloat" format="float" type="dimen">1.0</item>
+
+    <!-- Default screen brightness setting.
+         Must be in the range specified by minimum and maximum.
+         This value is a fraction between 3.5% and 100%.
+         Equivalent to 102/255 (default for this device)    -->
+    <item name="config_screenBrightnessSettingDefaultFloat" format="float" type="dimen">0.397637795276</item>
+
+    <!-- Note: This setting is deprecated, please use
+    config_screenBrightnessSettingForVrDefaultFloat instead -->
     <integer name="config_screenBrightnessForVrSettingDefault">86</integer>
 
-    <!-- Minimum screen brightness setting allowed for VR. Device panels start increasing pulse
-         width as brightness decreases below this theshold. -->
+    <!-- Note: This setting is deprecated, please use
+    config_screenBrightnessSettingForVrMinimumFloat instead -->
     <integer name="config_screenBrightnessForVrSettingMinimum">79</integer>
 
-    <!-- Maximum screen brightness setting allowed for VR. -->
+    <!-- Note: This setting is deprecated, please use
+    config_screenBrightnessSettingForVrMaximumFloat instead -->
     <integer name="config_screenBrightnessForVrSettingMaximum">255</integer>
 
+    <!-- Default screen brightness for VR setting as a float.
+    Equivalent to 86/255-->
+    <item name="config_screenBrightnessSettingForVrDefaultFloat" format="float" type="dimen">0.33464</item>
+
+    <!-- Minimum screen brightness setting allowed for VR. Device panels start increasing pulse
+     width as brightness decreases below this threshold as float.
+     Equivalent to 79/255 -->
+    <item name="config_screenBrightnessSettingForVrMinimumFloat" format="float" type="dimen">0.307087</item>
+
+    <!-- Maximum screen brightness setting allowed for VR as float. -->
+    <item name="config_screenBrightnessSettingForVrMaximumFloat" format="float" type="dimen">1.0</item>
+
     <!-- Screen brightness used to dim the screen while dozing in a very low power state.
          May be less than the minimum allowed brightness setting
          that can be set by the user. -->
     <integer name="config_screenBrightnessDoze">1</integer>
+    <item name="config_screenBrightnessDozeFloat" format="float" type="dimen">0.0</item>
 
     <!-- Delay that allows some content to arrive at the display before switching
          from DOZE to ON. -->
@@ -1339,6 +1375,7 @@
          timeout expires.  May be less than the minimum allowed brightness setting
          that can be set by the user. -->
     <integer name="config_screenBrightnessDim">10</integer>
+    <item name="config_screenBrightnessDimFloat" format="float" type="dimen">0.05</item>
 
     <!-- Minimum allowable screen brightness to use in a very dark room.
          This value sets the floor for the darkest possible auto-brightness
@@ -2351,7 +2388,7 @@
          type. These are flags and can be freely combined.
          0  - disable whitelist (install all system packages; no logging)
          1  - enforce (only install system packages if they are whitelisted)
-         2  - log (log when a non-whitelisted package is run)
+         2  - log (log non-whitelisted packages)
          4  - any package not mentioned in the whitelist file is implicitly whitelisted on all users
          8  - same as 4, but just for the SYSTEM user
          16 - ignore OTAs (don't install system packages during OTAs)
@@ -3792,6 +3829,10 @@
          or empty if the default should be used. -->
     <string translatable="false" name="config_deviceSpecificAudioService"></string>
 
+    <!-- Class name of the device specific implementation of DisplayAreaPolicy.Provider
+         or empty if the default should be used. -->
+    <string translatable="false" name="config_deviceSpecificDisplayAreaPolicyProvider"></string>
+
     <!-- Component name of media projection permission dialog -->
     <string name="config_mediaProjectionPermissionDialogComponent" translatable="false">com.android.systemui/com.android.systemui.media.MediaProjectionPermissionActivity</string>
 
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 22abedc..c44a0be 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -775,6 +775,8 @@
     <dimen name="resolver_small_margin">18dp</dimen>
     <dimen name="resolver_edge_margin">24dp</dimen>
     <dimen name="resolver_elevation">1dp</dimen>
+    <dimen name="resolver_empty_state_height">212dp</dimen>
+    <dimen name="resolver_empty_state_height_with_tabs">268dp</dimen>
 
     <dimen name="chooser_action_button_icon_size">18dp</dimen>
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a54566c..39cd00c 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -414,6 +414,14 @@
          logging. [CHAR LIMIT=NONE]-->
     <string name="network_logging_notification_text">Your organization manages this device and may monitor network traffic. Tap for details.</string>
 
+    <!-- Content title for a notification. This notification indicates that the device owner has
+         changed the location settings. [CHAR LIMIT=NONE] -->
+    <string name="location_changed_notification_title">Location settings changed by your admin</string>
+    <!-- Content text for a notification. Tapping opens device location settings.
+         [CHAR LIMIT=NONE] -->
+    <string name="location_changed_notification_text">Tap to see your location settings.</string>
+
+
     <!-- Factory reset warning dialog strings--> <skip />
     <!-- Shows up in the dialog's title to warn about an impeding factory reset. [CHAR LIMIT=NONE] -->
     <string name="factory_reset_warning">Your device will be erased</string>
@@ -1148,6 +1156,8 @@
     <string name="permlab_vibrate">control vibration</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_vibrate">Allows the app to control the vibrator.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_vibrator_state">Allows the app to access the vibrator state.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_callPhone">directly call phone numbers</string>
@@ -5333,6 +5343,24 @@
         <xliff:g id="package_name" example="com.android.example">%1$s</xliff:g> has been put into the RESTRICTED bucket</string>
 
     <!-- ResolverActivity - profile tabs -->
+    <!-- Label for the perosnal tab of the share sheet and intent resolver [CHAR LIMIT=NONE] -->
     <string name="resolver_personal_tab">Personal</string>
+    <!-- Label for the work tab of the share sheet and intent resolver [CHAR LIMIT=NONE] -->
     <string name="resolver_work_tab">Work</string>
+    <!-- Label to indicate that the user cannot share with work apps [CHAR LIMIT=NONE] -->
+    <string name="resolver_cant_share_with_work_apps">Can\u2019t share with work apps</string>
+    <!-- Label to indicate that the user cannot share with personal apps [CHAR LIMIT=NONE] -->
+    <string name="resolver_cant_share_with_personal_apps">Can\u2019t share with personal apps</string>
+    <!-- Label to explain to the user that sharing between personal and work apps is not enabled by the IT admin [CHAR LIMIT=NONE] -->
+    <string name="resolver_cant_share_cross_profile_explanation">Your IT admin blocked sharing between personal and work apps</string>
+    <!-- Label to indicate that the user has to turn on work apps in order to access work data [CHAR LIMIT=NONE] -->
+    <string name="resolver_turn_on_work_apps">Turn on work apps</string>
+    <!-- Label to explain that the user has to turn on work apps in order to access work data [CHAR LIMIT=NONE] -->
+    <string name="resolver_turn_on_work_apps_explanation">Turn on work apps to access work apps &amp; contacts</string>
+    <!-- Label to indicate that no apps are resolved [CHAR LIMIT=NONE] -->
+    <string name="resolver_no_apps_available">No apps available</string>
+    <!-- Label to explain that no apps are resolved [CHAR LIMIT=NONE] -->
+    <string name="resolver_no_apps_available_explanation">We couldn\u2019t find any apps</string>
+    <!-- Button which switches on the disabled work profile [CHAR LIMIT=NONE] -->
+    <string name="resolver_switch_on_work">Switch on work</string>
 </resources>
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index c63be7b..64768cf 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -285,7 +285,7 @@
         <item name="fontFamily">@string/config_bodyFontFamily</item>
     </style>
     <style name="TextAppearance.DeviceDefault.Notification" parent="TextAppearance.Material.Notification">
-        <item name="fontFamily">@string/config_headlineFontFamily</item>
+        <item name="fontFamily">@string/config_bodyFontFamily</item>
     </style>
     <style name="TextAppearance.DeviceDefault.Notification.Title" parent="TextAppearance.Material.Notification.Title">
         <item name="fontFamily">@string/config_headlineFontFamilyMedium</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2dd62c1..8581084 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -286,6 +286,7 @@
   <java-symbol type="bool" name="config_duplicate_port_omadm_wappush" />
   <java-symbol type="bool" name="config_disableTransitionAnimation" />
   <java-symbol type="bool" name="config_enableAutoPowerModes" />
+  <java-symbol type="bool" name="config_useWebViewPacProcessor" />
   <java-symbol type="integer" name="config_autoPowerModeThresholdAngle" />
   <java-symbol type="integer" name="config_autoPowerModeAnyMotionSensor" />
   <java-symbol type="bool" name="config_autoPowerModePreferWristTilt" />
@@ -1213,6 +1214,8 @@
   <java-symbol type="string" name="device_ownership_relinquished" />
   <java-symbol type="string" name="network_logging_notification_title" />
   <java-symbol type="string" name="network_logging_notification_text" />
+  <java-symbol type="string" name="location_changed_notification_title" />
+  <java-symbol type="string" name="location_changed_notification_text" />
   <java-symbol type="string" name="personal_apps_suspended_notification_title" />
   <java-symbol type="string" name="personal_apps_suspended_notification_text" />
   <java-symbol type="string" name="factory_reset_warning" />
@@ -1558,6 +1561,7 @@
   <java-symbol type="layout" name="notification_material_media_seekbar" />
   <java-symbol type="layout" name="resolver_list_per_profile" />
   <java-symbol type="layout" name="chooser_list_per_profile" />
+  <java-symbol type="layout" name="resolver_empty_states" />
 
   <java-symbol type="anim" name="slide_in_child_bottom" />
   <java-symbol type="anim" name="slide_in_right" />
@@ -1978,6 +1982,14 @@
   <java-symbol type="integer" name="config_screenBrightnessForVrSettingDefault" />
   <java-symbol type="integer" name="config_screenBrightnessForVrSettingMaximum" />
   <java-symbol type="integer" name="config_screenBrightnessForVrSettingMinimum" />
+  <java-symbol type="dimen" name="config_screenBrightnessSettingForVrMinimumFloat" />
+  <java-symbol type="dimen" name="config_screenBrightnessSettingForVrMaximumFloat" />
+  <java-symbol type="dimen" name="config_screenBrightnessSettingForVrDefaultFloat" />
+  <java-symbol type="dimen" name="config_screenBrightnessSettingMinimumFloat" />
+  <java-symbol type="dimen" name="config_screenBrightnessSettingMaximumFloat" />
+  <java-symbol type="dimen" name="config_screenBrightnessSettingDefaultFloat" />
+  <java-symbol type="dimen" name="config_screenBrightnessDozeFloat" />
+  <java-symbol type="dimen" name="config_screenBrightnessDimFloat" />
   <java-symbol type="integer" name="config_screenBrightnessDark" />
   <java-symbol type="integer" name="config_screenBrightnessDim" />
   <java-symbol type="integer" name="config_screenBrightnessDoze" />
@@ -3860,10 +3872,32 @@
   <java-symbol type="string" name="resolver_personal_tab" />
   <java-symbol type="string" name="resolver_work_tab" />
   <java-symbol type="id" name="stub" />
+  <java-symbol type="id" name="resolver_empty_state" />
+  <java-symbol type="id" name="resolver_empty_state_icon" />
+  <java-symbol type="id" name="resolver_empty_state_title" />
+  <java-symbol type="id" name="resolver_empty_state_subtitle" />
+  <java-symbol type="id" name="resolver_empty_state_button" />
+  <java-symbol type="string" name="resolver_cant_share_with_work_apps" />
+  <java-symbol type="string" name="resolver_cant_share_with_personal_apps" />
+  <java-symbol type="string" name="resolver_cant_share_cross_profile_explanation" />
+  <java-symbol type="string" name="resolver_turn_on_work_apps" />
+  <java-symbol type="string" name="resolver_turn_on_work_apps_explanation" />
+  <java-symbol type="string" name="resolver_no_apps_available" />
+  <java-symbol type="string" name="resolver_no_apps_available_explanation" />
+  <java-symbol type="string" name="resolver_no_apps_available_explanation" />
+  <java-symbol type="string" name="resolver_switch_on_work" />
+  <java-symbol type="drawable" name="ic_work_apps_off" />
+  <java-symbol type="drawable" name="ic_sharing_disabled" />
+  <java-symbol type="drawable" name="ic_no_apps" />
+  <java-symbol type="dimen" name="resolver_empty_state_height" />
+  <java-symbol type="dimen" name="resolver_empty_state_height_with_tabs" />
+  <java-symbol type="bool" name="sharesheet_show_content_preview" />
 
   <!-- Toast message for background started foreground service while-in-use permission restriction feature -->
   <java-symbol type="string" name="allow_while_in_use_permission_in_fgs" />
 
+  <java-symbol type="string" name="config_deviceSpecificDisplayAreaPolicyProvider" />
+
   <!-- Whether to expand the lock screen user switcher by default -->
   <java-symbol type="bool" name="config_expandLockScreenUserSwitcher" />
 </resources>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 718ca46..59335a5 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1330,12 +1330,6 @@
             android:process=":FakeProvider">
         </provider>
 
-        <provider
-            android:name="android.content.SlowProvider"
-            android:authorities="android.content.SlowProvider"
-            android:process=":SlowProvider">
-        </provider>
-
         <!-- Application components used for os tests -->
 
         <service android:name="android.os.MessengerService"
diff --git a/core/tests/coretests/res/values/strings.xml b/core/tests/coretests/res/values/strings.xml
index f630188..21613a8 100644
--- a/core/tests/coretests/res/values/strings.xml
+++ b/core/tests/coretests/res/values/strings.xml
@@ -148,4 +148,7 @@
 
     <!-- Summary of the accessibility shortcut [CHAR LIMIT=NONE] -->
     <string name="accessibility_shortcut_summary">Accessibility shortcut summary</string>
+
+    <!-- Html description of the accessibility shortcut [CHAR LIMIT=NONE] -->
+    <string name="accessibility_shortcut_html_description">Accessibility shortcut html description</string>
 </resources>
diff --git a/core/tests/coretests/res/xml/accessibility_shortcut_test_activity.xml b/core/tests/coretests/res/xml/accessibility_shortcut_test_activity.xml
index 60e2998..a597b71 100644
--- a/core/tests/coretests/res/xml/accessibility_shortcut_test_activity.xml
+++ b/core/tests/coretests/res/xml/accessibility_shortcut_test_activity.xml
@@ -19,4 +19,6 @@
 <accessibility-shortcut-target xmlns:android="http://schemas.android.com/apk/res/android"
                         android:description="@string/accessibility_shortcut_description"
                         android:summary="@string/accessibility_shortcut_summary"
+                        android:animatedImageDrawable="@drawable/bitmap_drawable"
+                        android:htmlDescription="@string/accessibility_shortcut_html_description"
 />
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java b/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java
index ae6d8df..82a7b2c 100644
--- a/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java
+++ b/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java
@@ -84,6 +84,22 @@
     }
 
     @Test
+    public void testAnimatedImageRes() {
+        assertThat("Animated image resource id is not correct",
+                mShortcutInfo.getAnimatedImageRes(), is(R.drawable.bitmap_drawable));
+    }
+
+    @Test
+    public void testHtmlDescription() {
+        final String htmlDescription = mTargetContext.getResources()
+                .getString(R.string.accessibility_shortcut_html_description);
+
+        assertNotNull("Can't find html description string", htmlDescription);
+        assertThat("Html description is not correct",
+                mShortcutInfo.loadHtmlDescription(mPackageManager), is(htmlDescription));
+    }
+
+    @Test
     public void testEquals() {
         assertTrue(mShortcutInfo.equals(mShortcutInfo));
         assertFalse(mShortcutInfo.equals(null));
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index 6dc7392..9dcce1e 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -209,13 +209,4 @@
         String type = mResolver.getType(Uri.parse("content://android.content.FakeProviderRemote"));
         assertEquals("fake/remote", type);
     }
-
-
-    @Test
-    public void testGetType_slowProvider() {
-        // This provider is running in a different process and is intentionally slow to start.
-        // We are trying to confirm that it does not cause an ANR
-        String type = mResolver.getType(Uri.parse("content://android.content.SlowProvider"));
-        assertEquals("slow", type);
-    }
 }
diff --git a/core/tests/coretests/src/android/content/SlowProvider.java b/core/tests/coretests/src/android/content/SlowProvider.java
deleted file mode 100644
index aba32e8..0000000
--- a/core/tests/coretests/src/android/content/SlowProvider.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content;
-
-import android.database.Cursor;
-import android.net.Uri;
-
-/**
- * A dummy content provider for tests.  This provider runs in a different process from the test and
- * is intentionally slow.
- */
-public class SlowProvider extends ContentProvider {
-
-    private static final int ON_CREATE_LATENCY_MILLIS = 3000;
-
-    @Override
-    public boolean onCreate() {
-        try {
-            Thread.sleep(ON_CREATE_LATENCY_MILLIS);
-        } catch (InterruptedException e) {
-            // Ignore
-        }
-        return true;
-    }
-
-    @Override
-    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
-            String sortOrder) {
-        return null;
-    }
-
-    @Override
-    public String getType(Uri uri) {
-        return "slow";
-    }
-
-    @Override
-    public Uri insert(Uri uri, ContentValues values) {
-        return null;
-    }
-
-    @Override
-    public int delete(Uri uri, String selection, String[] selectionArgs) {
-        return 0;
-    }
-
-    @Override
-    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
-        return 0;
-    }
-}
diff --git a/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java b/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java
new file mode 100644
index 0000000..c897ace
--- /dev/null
+++ b/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.integrity;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableMap;
+
+import org.testng.annotations.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+public class InstallerAllowedByManifestFormulaTest {
+
+    private static final InstallerAllowedByManifestFormula
+            FORMULA = new InstallerAllowedByManifestFormula();
+
+    @Test
+    public void testFormulaMatches_installerAndCertBothInManifest() {
+        AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder()
+                .setInstallerName("installer1")
+                .setInstallerCertificates(Arrays.asList("installer_cert1", "random_cert"))
+                .setAllowedInstallersAndCert(ImmutableMap.of(
+                        "installer1", "installer_cert1",
+                        "installer2", "installer_cert2"
+                )).build();
+
+        assertThat(FORMULA.matches(appInstallMetadata)).isTrue();
+    }
+
+    @Test
+    public void testFormulaMatches_installerAndCertDoesNotMatchInManifest() {
+        AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder()
+                .setInstallerName("installer1")
+                .setInstallerCertificates(Arrays.asList("installer_cert1", "random_cert"))
+                .setAllowedInstallersAndCert(ImmutableMap.of(
+                        "installer1", "installer_cert2",
+                        "installer2", "installer_cert1"
+                )).build();
+
+        assertThat(FORMULA.matches(appInstallMetadata)).isFalse();
+    }
+
+    @Test
+    public void testFormulaMatches_installerNotInManifest() {
+        AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder()
+                .setInstallerName("installer3")
+                .setInstallerCertificates(Arrays.asList("installer_cert1", "random_cert"))
+                .setAllowedInstallersAndCert(ImmutableMap.of(
+                        "installer1", "installer_cert2",
+                        "installer2", "installer_cert1"
+                )).build();
+
+        assertThat(FORMULA.matches(appInstallMetadata)).isFalse();
+    }
+
+    @Test
+    public void testFormulaMatches_certificateNotInManifest() {
+        AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder()
+                .setInstallerName("installer1")
+                .setInstallerCertificates(Arrays.asList("installer_cert3", "random_cert"))
+                .setAllowedInstallersAndCert(ImmutableMap.of(
+                        "installer1", "installer_cert2",
+                        "installer2", "installer_cert1"
+                )).build();
+
+        assertThat(FORMULA.matches(appInstallMetadata)).isFalse();
+    }
+
+    @Test
+    public void testFormulaMatches_emptyManifest() {
+        AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder()
+                .setInstallerName("installer1")
+                .setInstallerCertificates(Arrays.asList("installer_cert3", "random_cert"))
+                .setAllowedInstallersAndCert(ImmutableMap.of()).build();
+
+        assertThat(FORMULA.matches(appInstallMetadata)).isTrue();
+    }
+
+    /** Returns a builder with all fields filled with some dummy data. */
+    private AppInstallMetadata.Builder getAppInstallMetadataBuilder() {
+        return new AppInstallMetadata.Builder()
+                .setPackageName("abc")
+                .setAppCertificates(Collections.emptyList())
+                .setInstallerCertificates(Collections.emptyList())
+                .setInstallerName("abc")
+                .setVersionCode(-1)
+                .setIsPreInstalled(true);
+    }
+}
diff --git a/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java b/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java
index 75ef1f2..62c9c98 100644
--- a/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java
+++ b/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java
@@ -20,8 +20,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.testng.Assert.assertThrows;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -32,8 +30,7 @@
     @Test
     public void createEqualsFormula_packageName() {
         String packageName = "com.test.app";
-        IntegrityFormula formula =
-                IntegrityFormula.PACKAGE_NAME.equalTo(packageName);
+        IntegrityFormula formula = IntegrityFormula.Application.packageNameEquals(packageName);
 
         AtomicFormula.StringAtomicFormula stringAtomicFormula =
                 (AtomicFormula.StringAtomicFormula) formula;
@@ -46,8 +43,7 @@
     @Test
     public void createEqualsFormula_appCertificate() {
         String appCertificate = "com.test.app";
-        IntegrityFormula formula =
-                IntegrityFormula.APP_CERTIFICATE.equalTo(appCertificate);
+        IntegrityFormula formula = IntegrityFormula.Application.certificatesContain(appCertificate);
 
         AtomicFormula.StringAtomicFormula stringAtomicFormula =
                 (AtomicFormula.StringAtomicFormula) formula;
@@ -60,8 +56,7 @@
     @Test
     public void createEqualsFormula_installerName() {
         String installerName = "com.test.app";
-        IntegrityFormula formula =
-                IntegrityFormula.INSTALLER_NAME.equalTo(installerName);
+        IntegrityFormula formula = IntegrityFormula.Installer.packageNameEquals(installerName);
 
         AtomicFormula.StringAtomicFormula stringAtomicFormula =
                 (AtomicFormula.StringAtomicFormula) formula;
@@ -75,7 +70,7 @@
     public void createEqualsFormula_installerCertificate() {
         String installerCertificate = "com.test.app";
         IntegrityFormula formula =
-                IntegrityFormula.INSTALLER_CERTIFICATE.equalTo(installerCertificate);
+                IntegrityFormula.Installer.certificatesContain(installerCertificate);
 
         AtomicFormula.StringAtomicFormula stringAtomicFormula =
                 (AtomicFormula.StringAtomicFormula) formula;
@@ -88,8 +83,7 @@
     @Test
     public void createEqualsFormula_versionCode() {
         int versionCode = 12;
-        IntegrityFormula formula =
-                IntegrityFormula.VERSION_CODE.equalTo(versionCode);
+        IntegrityFormula formula = IntegrityFormula.Application.versionCodeEquals(versionCode);
 
         AtomicFormula.LongAtomicFormula stringAtomicFormula =
                 (AtomicFormula.LongAtomicFormula) formula;
@@ -100,24 +94,9 @@
     }
 
     @Test
-    public void createEqualsFormula_invalidKeyTypeForStringParameter() {
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> IntegrityFormula.PRE_INSTALLED.equalTo("wrongString"));
-    }
-
-    @Test
-    public void createEqualsFormula_invalidKeyTypeForLongParameter() {
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> IntegrityFormula.PACKAGE_NAME.equalTo(12));
-    }
-
-    @Test
     public void createGreaterThanFormula_versionCode() {
         int versionCode = 12;
-        IntegrityFormula formula =
-                IntegrityFormula.VERSION_CODE.greaterThan(versionCode);
+        IntegrityFormula formula = IntegrityFormula.Application.versionCodeGreaterThan(versionCode);
 
         AtomicFormula.LongAtomicFormula stringAtomicFormula =
                 (AtomicFormula.LongAtomicFormula) formula;
@@ -128,17 +107,10 @@
     }
 
     @Test
-    public void createGreaterThanFormula_invalidKeyTypeForLongParameter() {
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> IntegrityFormula.PACKAGE_NAME.greaterThan(12));
-    }
-
-    @Test
     public void createGreaterThanOrEqualsToFormula_versionCode() {
         int versionCode = 12;
-        IntegrityFormula formula =
-                IntegrityFormula.VERSION_CODE.greaterThanOrEquals(versionCode);
+        IntegrityFormula formula = IntegrityFormula.Application.versionCodeGreaterThanOrEqualTo(
+                versionCode);
 
         AtomicFormula.LongAtomicFormula stringAtomicFormula =
                 (AtomicFormula.LongAtomicFormula) formula;
@@ -149,15 +121,8 @@
     }
 
     @Test
-    public void createGreaterThanOrEqualsToFormula_invalidKeyTypeForLongParameter() {
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> IntegrityFormula.PACKAGE_NAME.greaterThanOrEquals(12));
-    }
-
-    @Test
     public void createIsTrueFormula_preInstalled() {
-        IntegrityFormula formula = IntegrityFormula.PRE_INSTALLED.equalTo(true);
+        IntegrityFormula formula = IntegrityFormula.Application.isPreInstalled();
 
         AtomicFormula.BooleanAtomicFormula stringAtomicFormula =
                 (AtomicFormula.BooleanAtomicFormula) formula;
@@ -167,20 +132,12 @@
     }
 
     @Test
-    public void createIsTrueFormula_invalidKeyTypeForBoolParameter() {
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> IntegrityFormula.PACKAGE_NAME.equalTo(true));
-    }
-
-    @Test
     public void createAllFormula() {
         String packageName = "com.test.package";
         String certificateName = "certificate";
-        IntegrityFormula formula1 =
-                IntegrityFormula.PACKAGE_NAME.equalTo(packageName);
-        IntegrityFormula formula2 =
-                IntegrityFormula.APP_CERTIFICATE.equalTo(certificateName);
+        IntegrityFormula formula1 = IntegrityFormula.Application.packageNameEquals(packageName);
+        IntegrityFormula formula2 = IntegrityFormula.Application.certificatesContain(
+                certificateName);
 
         IntegrityFormula compoundFormula = IntegrityFormula.all(formula1, formula2);
 
@@ -191,10 +148,9 @@
     public void createAnyFormula() {
         String packageName = "com.test.package";
         String certificateName = "certificate";
-        IntegrityFormula formula1 =
-                IntegrityFormula.PACKAGE_NAME.equalTo(packageName);
-        IntegrityFormula formula2 =
-                IntegrityFormula.APP_CERTIFICATE.equalTo(certificateName);
+        IntegrityFormula formula1 = IntegrityFormula.Application.packageNameEquals(packageName);
+        IntegrityFormula formula2 = IntegrityFormula.Application.certificatesContain(
+                certificateName);
 
         IntegrityFormula compoundFormula = IntegrityFormula.any(formula1, formula2);
 
@@ -206,8 +162,7 @@
         String packageName = "com.test.package";
 
         IntegrityFormula compoundFormula =
-                IntegrityFormula.not(
-                        IntegrityFormula.PACKAGE_NAME.equalTo(packageName));
+                IntegrityFormula.not(IntegrityFormula.Application.packageNameEquals(packageName));
 
         assertThat(compoundFormula.getTag()).isEqualTo(COMPOUND_FORMULA_TAG);
     }
diff --git a/core/tests/coretests/src/android/content/res/ConfigurationTest.java b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
index 2c956c9..669138c 100644
--- a/core/tests/coretests/src/android/content/res/ConfigurationTest.java
+++ b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
@@ -148,6 +148,15 @@
         assertEquals(SMALLEST_SCREEN_WIDTH_DP_UNDEFINED, config.smallestScreenWidthDp);
     }
 
+    @Test
+    public void testNightModeHelper() {
+        Configuration config = new Configuration();
+        config.uiMode = Configuration.UI_MODE_NIGHT_YES;
+        assertTrue(config.isNightModeActive());
+        config.uiMode = Configuration.UI_MODE_NIGHT_NO;
+        assertFalse(config.isNightModeActive());
+    }
+
     private void dumpDebug(File f, Configuration config) throws Exception {
         final AtomicFile af = new AtomicFile(f);
         FileOutputStream fos = af.startWrite();
diff --git a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
index 49fb75b..b8dbfd3 100644
--- a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
@@ -466,179 +466,7 @@
             // expected
         }
     }
-    
-    @MediumTest
-    public void testTokenize() throws Exception {
-        Cursor c;
-        mDatabase.execSQL("CREATE TABLE tokens (" +
-                "token TEXT COLLATE unicode," +
-                "source INTEGER," +
-                "token_index INTEGER," +
-                "tag TEXT" +
-                ");");
-        mDatabase.execSQL("CREATE TABLE tokens_no_index (" +
-                "token TEXT COLLATE unicode," +
-                "source INTEGER" +
-                ");");
-        
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT _TOKENIZE(NULL, NULL, NULL, NULL)", null));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT _TOKENIZE('tokens', NULL, NULL, NULL)", null));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT _TOKENIZE('tokens', 10, NULL, NULL)", null));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT _TOKENIZE('tokens', 10, 'some string', NULL)", null));
-     
-        Assert.assertEquals(3, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT _TOKENIZE('tokens', 11, 'some string ok', ' ', 1, 'foo')", null));
-        Assert.assertEquals(2, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT _TOKENIZE('tokens', 11, 'second field', ' ', 1, 'bar')", null));
 
-        Assert.assertEquals(3, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT _TOKENIZE('tokens_no_index', 20, 'some string ok', ' ')", null));
-        Assert.assertEquals(3, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT _TOKENIZE('tokens_no_index', 21, 'foo bar baz', ' ', 0)", null));
-
-        // test Chinese
-        String chinese = new String("\u4eac\u4ec5 \u5c3d\u5f84\u60ca"); 
-        Assert.assertEquals(2, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT _TOKENIZE('tokens', 12,'" + chinese + "', ' ', 1)", null));
-        
-        String icustr = new String("Fr\u00e9d\u00e9ric Hj\u00f8nnev\u00e5g");
-        
-        Assert.assertEquals(2, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT _TOKENIZE('tokens', 13, '" + icustr + "', ' ', 1)", null));
-        
-        Assert.assertEquals(9, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT count(*) from tokens;", null));      
-
-        String key = DatabaseUtils.getHexCollationKey("Frederic Hjonneva");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));      
-        Assert.assertEquals(13, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        key = DatabaseUtils.getHexCollationKey("Hjonneva");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(13, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        
-        key = DatabaseUtils.getHexCollationKey("some string ok");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals("foo", DatabaseUtils.stringForQuery(mDatabase,
-                "SELECT tag from tokens where token GLOB '" + key + "*'", null));
-        key = DatabaseUtils.getHexCollationKey("string");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals("foo", DatabaseUtils.stringForQuery(mDatabase,
-                "SELECT tag from tokens where token GLOB '" + key + "*'", null));
-        key = DatabaseUtils.getHexCollationKey("ok");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(2, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals("foo", DatabaseUtils.stringForQuery(mDatabase,
-                "SELECT tag from tokens where token GLOB '" + key + "*'", null));
-
-        key = DatabaseUtils.getHexCollationKey("second field");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals("bar", DatabaseUtils.stringForQuery(mDatabase,
-                "SELECT tag from tokens where token GLOB '" + key + "*'", null));
-        key = DatabaseUtils.getHexCollationKey("field");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals("bar", DatabaseUtils.stringForQuery(mDatabase,
-                "SELECT tag from tokens where token GLOB '" + key + "*'", null));
-
-        key = DatabaseUtils.getHexCollationKey(chinese);
-        String[] a = new String[1];
-        a[0] = key;
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token= ?", a));
-        Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token= ?", a));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token= ?", a));
-        a[0] += "*";
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-             "SELECT count(*) from tokens where token GLOB ?", a));        
-        Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB ?", a));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB ?", a));
-
-       Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token= '" + key + "'", null));
-       Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
-               "SELECT source from tokens where token= '" + key + "'", null));
-       Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-               "SELECT token_index from tokens where token= '" + key + "'", null));
-        
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));        
-        Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        
-        key = DatabaseUtils.getHexCollationKey("\u4eac\u4ec5");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        
-        key = DatabaseUtils.getHexCollationKey("\u5c3d\u5f84\u60ca");
-        Log.d("DatabaseGeneralTest", "key = " + key);
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token GLOB 'ab*'", null));        
-
-        key = DatabaseUtils.getHexCollationKey("some string ok");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT count(*) from tokens_no_index where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(20, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens_no_index where token GLOB '" + key + "*'", null));
-
-        key = DatabaseUtils.getHexCollationKey("bar");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT count(*) from tokens_no_index where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(21, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens_no_index where token GLOB '" + key + "*'", null));
-    }
-    
     @MediumTest
     public void testTransactions() throws Exception {
         mDatabase.execSQL("CREATE TABLE test (num INTEGER);");
diff --git a/core/tests/coretests/src/android/os/BrightnessLimit.java b/core/tests/coretests/src/android/os/BrightnessLimit.java
index 5a3724f..be79355 100644
--- a/core/tests/coretests/src/android/os/BrightnessLimit.java
+++ b/core/tests/coretests/src/android/os/BrightnessLimit.java
@@ -42,7 +42,7 @@
 
     public void onClick(View v) {
         DisplayManager dm = getSystemService(DisplayManager.class);
-        dm.setTemporaryBrightness(0);
+        dm.setTemporaryBrightness(0.0f);
         Settings.System.putInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, 0);
     }
 }
diff --git a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
index 2648a06..a24b4e0 100644
--- a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
+++ b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
@@ -147,6 +147,34 @@
     }
 
     @Test
+    public void testLoadSuggested_withMaxNumber() throws RemoteException {
+        Control control1 = new Control.StatelessBuilder("TEST_ID", mPendingIntent).build();
+        Control control2 = new Control.StatelessBuilder("TEST_ID_2", mPendingIntent)
+                .setDeviceType(DeviceTypes.TYPE_AIR_FRESHENER).build();
+
+        @SuppressWarnings("unchecked")
+        ArgumentCaptor<List<Control>> captor = ArgumentCaptor.forClass(List.class);
+
+        ArrayList<Control> list = new ArrayList<>();
+        list.add(control1);
+        list.add(control2);
+
+        final int maxSuggested = 1;
+
+        mControlsProviderService.setControls(list);
+        mControlsProvider.loadSuggested(maxSuggested, mLoadCallback);
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        verify(mLoadCallback).accept(eq(mToken), captor.capture());
+        List<Control> l = captor.getValue();
+        assertEquals(maxSuggested, l.size());
+
+        for (int i = 0; i < maxSuggested; ++i) {
+            assertTrue(equals(list.get(i), l.get(i)));
+        }
+    }
+
+    @Test
     public void testSubscribe() throws RemoteException {
         Control control = new Control.StatefulBuilder("TEST_ID", mPendingIntent)
                 .setTitle("TEST_TITLE")
@@ -216,6 +244,11 @@
         }
 
         @Override
+        public void loadSuggestedControls(int maxNumber, Consumer<List<Control>> cb) {
+            cb.accept(mControls);
+        }
+
+        @Override
         public Publisher<Control> publisherFor(List<String> ids) {
             return new Publisher<Control>() {
                 public void subscribe(final Subscriber s) {
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 2c9dba1..24fe2a0 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -45,6 +45,7 @@
 import android.graphics.Insets;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.CancellationSignal;
 import android.platform.test.annotations.Presubmit;
 import android.view.SurfaceControl.Transaction;
 import android.view.WindowInsets.Type;
@@ -516,6 +517,34 @@
     }
 
     @Test
+    public void testCancellation_afterGainingControl() throws Exception {
+        InsetsSourceControl control =
+                new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
+        mController.onControlsChanged(new InsetsSourceControl[] { control });
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            WindowInsetsAnimationControlListener mockListener =
+                    mock(WindowInsetsAnimationControlListener.class);
+            CancellationSignal cancellationSignal = mController.controlWindowInsetsAnimation(
+                    statusBars(), 0 /* durationMs */,
+                    new LinearInterpolator(), mockListener);
+
+            // Ready gets deferred until next predraw
+            mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
+
+            verify(mockListener).onReady(any(), anyInt());
+
+            cancellationSignal.cancel();
+            verify(mockListener).onCancelled();
+        });
+        waitUntilNextFrame();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible());
+        });
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
+    @Test
     public void testControlImeNotReady() {
         prepareControls();
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
@@ -580,6 +609,28 @@
         });
     }
 
+    @Test
+    public void testControlImeNotReady_cancel() {
+        prepareControls();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            WindowInsetsAnimationControlListener listener =
+                    mock(WindowInsetsAnimationControlListener.class);
+            mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(), listener)
+                    .cancel();
+
+            verify(listener).onCancelled();
+
+            // Ready gets deferred until next predraw
+            mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
+
+            verify(listener, never()).onReady(any(), anyInt());
+
+            // Pretend that timeout is happening
+            mTestClock.fastForward(2500);
+            mTestHandler.timeAdvance();
+        });
+    }
+
     private void waitUntilNextFrame() throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
         Choreographer.getMainThreadInstance().postCallback(Choreographer.CALLBACK_COMMIT,
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index f83fb3f..af115b1 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -70,6 +70,7 @@
     <privapp-permissions package="com.android.managedprovisioning">
         <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
         <permission name="android.permission.CHANGE_CONFIGURATION"/>
+        <permission name="android.permission.CONFIGURE_INTERACT_ACROSS_PROFILES"/>
         <permission name="android.permission.CRYPT_KEEPER"/>
         <permission name="android.permission.DELETE_PACKAGES"/>
         <permission name="android.permission.INSTALL_PACKAGES"/>
@@ -378,6 +379,8 @@
         <!-- Permission required for ShortcutManagerUsageTest CTS test. -->
         <permission name="android.permission.ACCESS_SHORTCUTS"/>
         <permission name="android.permission.REBOOT"/>
+        <!-- Permission required for access VIBRATOR_STATE. -->
+        <permission name="android.permission.ACCESS_VIBRATOR_STATE"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index 447f043..f50de16 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.AssetManager;
+import android.graphics.fonts.Font;
 import android.graphics.fonts.FontVariationAxis;
 import android.os.Build;
 import android.text.TextUtils;
@@ -195,18 +196,13 @@
         if (mBuilderPtr == 0) {
             throw new IllegalStateException("Unable to call addFontFromAsset after freezing.");
         }
-        if (axes != null) {
-            for (FontVariationAxis axis : axes) {
-                nAddAxisValue(mBuilderPtr, axis.getOpenTypeTagValue(), axis.getStyleValue());
-            }
-        }
-        return nAddFontFromAssetManager(mBuilderPtr, mgr, path, cookie, isAsset, ttcIndex, weight,
-                isItalic);
-    }
 
-    // TODO: Remove once internal user stop using private API.
-    private static boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex) {
-        return nAddFont(builderPtr, font, ttcIndex, -1, -1);
+        try {
+            ByteBuffer buffer =  Font.Builder.createBuffer(mgr, path, isAsset, cookie);
+            return addFontFromBuffer(buffer, ttcIndex, axes, weight, isItalic);
+        } catch (IOException e) {
+            return false;
+        }
     }
 
     private static native long nInitBuilder(String langs, int variant);
@@ -225,8 +221,6 @@
             int weight, int isItalic);
     private static native boolean nAddFontWeightStyle(long builderPtr, ByteBuffer font,
             int ttcIndex, int weight, int isItalic);
-    private static native boolean nAddFontFromAssetManager(long builderPtr, AssetManager mgr,
-            String path, int cookie, boolean isAsset, int ttcIndex, int weight, int isItalic);
 
     // The added axis values are only valid for the next nAddFont* method call.
     @CriticalNative
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index ba96a06..4899fbe 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -19,6 +19,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.res.AssetFileDescriptor;
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.os.LocaleList;
@@ -35,7 +36,9 @@
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
 import java.nio.channels.FileChannel;
 import java.util.Arrays;
 import java.util.Objects;
@@ -54,10 +57,6 @@
      * A builder class for creating new Font.
      */
     public static final class Builder {
-        private static final NativeAllocationRegistry sAssetByteBufferRegistry =
-                NativeAllocationRegistry.createMalloced(ByteBuffer.class.getClassLoader(),
-                    nGetReleaseNativeAssetFunc());
-
         private static final NativeAllocationRegistry sFontRegistry =
                 NativeAllocationRegistry.createMalloced(Font.class.getClassLoader(),
                     nGetReleaseNativeFont());
@@ -151,7 +150,11 @@
          * @param path the file name of the font data in the asset directory
          */
         public Builder(@NonNull AssetManager am, @NonNull String path) {
-            this(am, path, true /* is asset */, 0 /* cookie */);
+            try {
+                mBuffer = createBuffer(am, path, true /* is asset */, 0 /* cookie */);
+            } catch (IOException e) {
+                mException = e;
+            }
         }
 
         /**
@@ -165,18 +168,11 @@
          */
         public Builder(@NonNull AssetManager am, @NonNull String path, boolean isAsset,
                 int cookie) {
-            final long nativeAsset = nGetNativeAsset(am, path, isAsset, cookie);
-            if (nativeAsset == 0) {
-                mException = new FileNotFoundException("Unable to open " + path);
-                return;
+            try {
+                mBuffer = createBuffer(am, path, isAsset, cookie);
+            } catch (IOException e) {
+                mException = e;
             }
-            final ByteBuffer b = nGetAssetBuffer(nativeAsset);
-            sAssetByteBufferRegistry.registerNativeAllocation(b, nativeAsset);
-            if (b == null) {
-                mException = new FileNotFoundException(path + " not found");
-                return;
-            }
-            mBuffer = b;
         }
 
         /**
@@ -199,19 +195,64 @@
                 mException = new FileNotFoundException(resId + " must be font file.");
                 return;
             }
-            final long nativeAsset = nGetNativeAsset(res.getAssets(), str, false /* is asset */,
-                    value.assetCookie);
-            if (nativeAsset == 0) {
-                mException = new FileNotFoundException("Unable to open " + str);
-                return;
+
+            try {
+                mBuffer = createBuffer(res.getAssets(), str, false, value.assetCookie);
+            } catch (IOException e) {
+                mException = e;
             }
-            final ByteBuffer b = nGetAssetBuffer(nativeAsset);
-            sAssetByteBufferRegistry.registerNativeAllocation(b, nativeAsset);
-            if (b == null) {
-                mException = new FileNotFoundException(str + " not found");
-                return;
+        }
+
+        /**
+         * Creates a buffer containing font data using the assetManager and other
+         * provided inputs.
+         *
+         * @param am the application's asset manager
+         * @param path the file name of the font data in the asset directory
+         * @param isAsset true if the undelying data is in asset
+         * @param cookie set asset cookie
+         * @return buffer containing the contents of the file
+         *
+         * @hide
+         */
+        public static ByteBuffer createBuffer(@NonNull AssetManager am, @NonNull String path,
+                                              boolean isAsset, int cookie) throws IOException {
+            Preconditions.checkNotNull(am, "assetManager can not be null");
+            Preconditions.checkNotNull(path, "path can not be null");
+
+            if (!isAsset) {
+                // Attempt to open as FD, which should work unless the asset is compressed
+                AssetFileDescriptor assetFD;
+                try {
+                    if (cookie > 0) {
+                        assetFD = am.openNonAssetFd(cookie, path);
+                    } else {
+                        assetFD = am.openNonAssetFd(path);
+                    }
+
+                    try (FileInputStream fis = assetFD.createInputStream()) {
+                        final FileChannel fc = fis.getChannel();
+                        return fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
+                    }
+                } catch (IOException e) {
+                    // failed to open as FD so now we will attempt to open as an input stream
+                }
             }
-            mBuffer = b;
+
+            try (InputStream assetStream = isAsset ? am.open(path, AssetManager.ACCESS_BUFFER)
+                    : am.openNonAsset(cookie, path, AssetManager.ACCESS_BUFFER)) {
+
+                int capacity = assetStream.available();
+                ByteBuffer buffer = ByteBuffer.allocateDirect(capacity);
+                buffer.order(ByteOrder.nativeOrder());
+                assetStream.read(buffer.array(), buffer.arrayOffset(), assetStream.available());
+
+                if (assetStream.read() != -1) {
+                    throw new IOException("Unable to access full contents of " + path);
+                }
+
+                return buffer;
+            }
         }
 
         /**
@@ -396,15 +437,6 @@
         }
 
         /**
-         * Native methods for accessing underlying buffer in Asset
-         */
-        private static native long nGetNativeAsset(
-                @NonNull AssetManager am, @NonNull String path, boolean isAsset, int cookie);
-        private static native ByteBuffer nGetAssetBuffer(long nativeAsset);
-        @CriticalNative
-        private static native long nGetReleaseNativeAssetFunc();
-
-        /**
          * Native methods for creating Font
          */
         private static native long nInitBuilder();
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
index a2d2355..8dbb5f5 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -206,6 +206,7 @@
                 blockModes,
                 userAuthenticationRequired,
                 (int) userAuthenticationValidityDurationSeconds,
+                keymasterHwEnforcedUserAuthenticators,
                 userAuthenticationRequirementEnforcedBySecureHardware,
                 userAuthenticationValidWhileOnBody,
                 trustedUserPresenceRequred,
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 450dd33..d683041 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -263,6 +263,7 @@
     private final boolean mRandomizedEncryptionRequired;
     private final boolean mUserAuthenticationRequired;
     private final int mUserAuthenticationValidityDurationSeconds;
+    private final @KeyProperties.AuthEnum int mUserAuthenticationType;
     private final boolean mUserPresenceRequired;
     private final byte[] mAttestationChallenge;
     private final boolean mUniqueIdIncluded;
@@ -301,6 +302,7 @@
             boolean randomizedEncryptionRequired,
             boolean userAuthenticationRequired,
             int userAuthenticationValidityDurationSeconds,
+            @KeyProperties.AuthEnum int userAuthenticationType,
             boolean userPresenceRequired,
             byte[] attestationChallenge,
             boolean uniqueIdIncluded,
@@ -352,6 +354,7 @@
         mUserAuthenticationRequired = userAuthenticationRequired;
         mUserPresenceRequired = userPresenceRequired;
         mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
+        mUserAuthenticationType = userAuthenticationType;
         mAttestationChallenge = Utils.cloneIfNotNull(attestationChallenge);
         mUniqueIdIncluded = uniqueIdIncluded;
         mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
@@ -605,6 +608,22 @@
     }
 
     /**
+     * Gets the modes of authentication that can authorize use of this key. This has effect only if
+     * user authentication is required (see {@link #isUserAuthenticationRequired()}).
+     *
+     * <p>This authorization applies only to secret key and private key operations. Public key
+     * operations are not restricted.
+     *
+     * @return integer representing the bitwse OR of all acceptable authentication types for the
+     *         key.
+     *
+     * @see #isUserAuthenticationRequired()
+     * @see Builder#setUserAuthenticationParameters(int, int)
+     */
+    public @KeyProperties.AuthEnum int getUserAuthenticationType() {
+        return mUserAuthenticationType;
+    }
+    /**
      * Returns {@code true} if the key is authorized to be used only if a test of user presence has
      * been performed between the {@code Signature.initSign()} and {@code Signature.sign()} calls.
      * It requires that the KeyStore implementation have a direct way to validate the user presence
@@ -746,6 +765,7 @@
         private boolean mRandomizedEncryptionRequired = true;
         private boolean mUserAuthenticationRequired;
         private int mUserAuthenticationValidityDurationSeconds = -1;
+        private @KeyProperties.AuthEnum int mUserAuthenticationType;
         private boolean mUserPresenceRequired = false;
         private byte[] mAttestationChallenge = null;
         private boolean mUniqueIdIncluded = false;
@@ -810,6 +830,7 @@
             mUserAuthenticationRequired = sourceSpec.isUserAuthenticationRequired();
             mUserAuthenticationValidityDurationSeconds =
                 sourceSpec.getUserAuthenticationValidityDurationSeconds();
+            mUserAuthenticationType = sourceSpec.getUserAuthenticationType();
             mUserPresenceRequired = sourceSpec.isUserPresenceRequired();
             mAttestationChallenge = sourceSpec.getAttestationChallenge();
             mUniqueIdIncluded = sourceSpec.isUniqueIdIncluded();
@@ -1207,14 +1228,62 @@
          * @see BiometricPrompt
          * @see BiometricPrompt.CryptoObject
          * @see KeyguardManager
+         * @deprecated See {@link #setUserAuthenticationParameters(int, int)}
          */
+        @Deprecated
         @NonNull
         public Builder setUserAuthenticationValidityDurationSeconds(
                 @IntRange(from = -1) int seconds) {
             if (seconds < -1) {
                 throw new IllegalArgumentException("seconds must be -1 or larger");
             }
-            mUserAuthenticationValidityDurationSeconds = seconds;
+            if (seconds == -1) {
+                return setUserAuthenticationParameters(0, KeyProperties.AUTH_BIOMETRIC_STRONG);
+            }
+            return setUserAuthenticationParameters(seconds, KeyProperties.AUTH_BIOMETRIC_STRONG);
+        }
+
+        /**
+         * Sets the duration of time (seconds) and authorization type for which this key is
+         * authorized to be used after the user is successfully authenticated. This has effect if
+         * the key requires user authentication for its use (see
+         * {@link #setUserAuthenticationRequired(boolean)}).
+         *
+         * <p>By default, if user authentication is required, it must take place for every use of
+         * the key.
+         *
+         * <p>These cryptographic operations will throw {@link UserNotAuthenticatedException} during
+         * initialization if the user needs to be authenticated to proceed. This situation can be
+         * resolved by the user authenticating with the appropriate biometric or credential as
+         * required by the key. See {@link BiometricPrompt.Builder#setAllowedAuthenticators(int)}
+         * and {@link BiometricManager.Authenticators}.
+         *
+         * <p>Once resolved, initializing a new cryptographic operation using this key (or any other
+         * key which is authorized to be used for a fixed duration of time after user
+         * authentication) should succeed provided the user authentication flow completed
+         * successfully.
+         *
+         * @param timeout duration in seconds or {@code 0} if user authentication must take place
+         *        for every use of the key. {@code -1} is also accepted for legacy purposes. It is
+         *        functionally the same as {@code 0}.
+         * @param type set of authentication types which can authorize use of the key. See
+         *        {@link KeyProperties}.{@code AUTH} flags.
+         *
+         * @see #setUserAuthenticationRequired(boolean)
+         * @see BiometricPrompt
+         * @see BiometricPrompt.CryptoObject
+         * @see KeyguardManager
+         */
+        @NonNull
+        public Builder setUserAuthenticationParameters(@IntRange(from = -1) int timeout,
+                                                       @KeyProperties.AuthEnum int type) {
+            if (timeout < -1) {
+                throw new IllegalArgumentException("timeout must be -1 or larger");
+            } else if (timeout == -1) {
+                timeout = 0;
+            }
+            mUserAuthenticationValidityDurationSeconds = timeout;
+            mUserAuthenticationType = type;
             return this;
         }
 
@@ -1392,6 +1461,7 @@
                     mRandomizedEncryptionRequired,
                     mUserAuthenticationRequired,
                     mUserAuthenticationValidityDurationSeconds,
+                    mUserAuthenticationType,
                     mUserPresenceRequired,
                     mAttestationChallenge,
                     mUniqueIdIncluded,
diff --git a/keystore/java/android/security/keystore/KeyInfo.java b/keystore/java/android/security/keystore/KeyInfo.java
index 0a75cd5..d891a25 100644
--- a/keystore/java/android/security/keystore/KeyInfo.java
+++ b/keystore/java/android/security/keystore/KeyInfo.java
@@ -78,6 +78,7 @@
     private final @KeyProperties.BlockModeEnum String[] mBlockModes;
     private final boolean mUserAuthenticationRequired;
     private final int mUserAuthenticationValidityDurationSeconds;
+    private final @KeyProperties.AuthEnum int mUserAuthenticationType;
     private final boolean mUserAuthenticationRequirementEnforcedBySecureHardware;
     private final boolean mUserAuthenticationValidWhileOnBody;
     private final boolean mTrustedUserPresenceRequired;
@@ -101,6 +102,7 @@
             @KeyProperties.BlockModeEnum String[] blockModes,
             boolean userAuthenticationRequired,
             int userAuthenticationValidityDurationSeconds,
+            @KeyProperties.AuthEnum int userAuthenticationType,
             boolean userAuthenticationRequirementEnforcedBySecureHardware,
             boolean userAuthenticationValidWhileOnBody,
             boolean trustedUserPresenceRequired,
@@ -122,6 +124,7 @@
         mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes));
         mUserAuthenticationRequired = userAuthenticationRequired;
         mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
+        mUserAuthenticationType = userAuthenticationType;
         mUserAuthenticationRequirementEnforcedBySecureHardware =
                 userAuthenticationRequirementEnforcedBySecureHardware;
         mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
@@ -301,6 +304,22 @@
     }
 
     /**
+     * Gets the acceptable user authentication types for which this key can be authorized to be
+     * used. This has effect only if user authentication is required (see
+     * {@link #isUserAuthenticationRequired()}).
+     *
+     * <p>This authorization applies only to secret key and private key operations. Public key
+     * operations are not restricted.
+     *
+     * @return integer representing the accepted forms of user authentication for this key
+     *
+     * @see #isUserAuthenticationRequired()
+     */
+    public @KeyProperties.AuthEnum int getUserAuthenticationType() {
+        return mUserAuthenticationType;
+    }
+
+    /**
      * Returns {@code true} if the requirement that this key can only be used if the user has been
      * authenticated is enforced by secure hardware (e.g., Trusted Execution Environment (TEE) or
      * Secure Element (SE)).
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index f12a659..c58a123 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -39,6 +39,27 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "AUTH_" }, value = {
+            AUTH_BIOMETRIC_STRONG,
+            AUTH_DEVICE_CREDENTIAL,
+    })
+    public @interface AuthEnum {}
+
+    /**
+     * The non-biometric credential used to secure the device (i.e., PIN, pattern, or password)
+     */
+    public static final int AUTH_DEVICE_CREDENTIAL = 1 << 0;
+
+    /**
+     * Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
+     * requirements for <strong>Strong</strong>, as defined by the Android CDD.
+     */
+    public static final int AUTH_BIOMETRIC_STRONG = 1 << 1;
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, prefix = { "PURPOSE_" }, value = {
             PURPOSE_ENCRYPT,
             PURPOSE_DECRYPT,
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index 26181a6..e230b7c 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -225,6 +225,7 @@
     private final @KeyProperties.BlockModeEnum String[] mBlockModes;
     private final boolean mRandomizedEncryptionRequired;
     private final boolean mUserAuthenticationRequired;
+    private final @KeyProperties.AuthEnum int mUserAuthenticationType;
     private final int mUserAuthenticationValidityDurationSeconds;
     private final boolean mUserPresenceRequred;
     private final boolean mUserAuthenticationValidWhileOnBody;
@@ -246,6 +247,7 @@
             @KeyProperties.BlockModeEnum String[] blockModes,
             boolean randomizedEncryptionRequired,
             boolean userAuthenticationRequired,
+            @KeyProperties.AuthEnum int userAuthenticationType,
             int userAuthenticationValidityDurationSeconds,
             boolean userPresenceRequred,
             boolean userAuthenticationValidWhileOnBody,
@@ -267,6 +269,7 @@
         mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes));
         mRandomizedEncryptionRequired = randomizedEncryptionRequired;
         mUserAuthenticationRequired = userAuthenticationRequired;
+        mUserAuthenticationType = userAuthenticationType;
         mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
         mUserPresenceRequred = userPresenceRequred;
         mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
@@ -429,6 +432,10 @@
         return mUserConfirmationRequired;
     }
 
+    public @KeyProperties.AuthEnum int getUserAuthenticationType() {
+        return mUserAuthenticationType;
+    }
+
     /**
      * Gets the duration of time (seconds) for which this key is authorized to be used after the
      * user is successfully authenticated. This has effect only if user authentication is required
@@ -555,6 +562,7 @@
         private @KeyProperties.BlockModeEnum String[] mBlockModes;
         private boolean mRandomizedEncryptionRequired = true;
         private boolean mUserAuthenticationRequired;
+        private @KeyProperties.AuthEnum int mUserAuthenticationType;
         private int mUserAuthenticationValidityDurationSeconds = -1;
         private boolean mUserPresenceRequired = false;
         private boolean mUserAuthenticationValidWhileOnBody;
@@ -850,14 +858,62 @@
          * @see BiometricPrompt
          * @see BiometricPrompt.CryptoObject
          * @see KeyguardManager
+         * @deprecated See {@link #setUserAuthenticationParameters(int, int)}
          */
+        @Deprecated
         @NonNull
         public Builder setUserAuthenticationValidityDurationSeconds(
                 @IntRange(from = -1) int seconds) {
             if (seconds < -1) {
                 throw new IllegalArgumentException("seconds must be -1 or larger");
             }
-            mUserAuthenticationValidityDurationSeconds = seconds;
+            if (seconds == -1) {
+                return setUserAuthenticationParameters(0, KeyProperties.AUTH_BIOMETRIC_STRONG);
+            }
+            return setUserAuthenticationParameters(seconds, KeyProperties.AUTH_BIOMETRIC_STRONG);
+        }
+
+        /**
+         * Sets the duration of time (seconds) and authorization type for which this key is
+         * authorized to be used after the user is successfully authenticated. This has effect if
+         * the key requires user authentication for its use (see
+         * {@link #setUserAuthenticationRequired(boolean)}).
+         *
+         * <p>By default, if user authentication is required, it must take place for every use of
+         * the key.
+         *
+         * <p>These cryptographic operations will throw {@link UserNotAuthenticatedException} during
+         * initialization if the user needs to be authenticated to proceed. This situation can be
+         * resolved by the user authenticating with the appropriate biometric or credential as
+         * required by the key. See {@link BiometricPrompt.Builder#setAllowedAuthenticators(int)}
+         * and {@link BiometricManager.Authenticators}.
+         *
+         * <p>Once resolved, initializing a new cryptographic operation using this key (or any other
+         * key which is authorized to be used for a fixed duration of time after user
+         * authentication) should succeed provided the user authentication flow completed
+         * successfully.
+         *
+         * @param timeout duration in seconds or {@code 0} if user authentication must take place
+         *        for every use of the key. {@code -1} is also accepted for legacy purposes. It is
+         *        functionally the same as {@code 0}.
+         * @param type set of authentication types which can authorize use of the key. See
+         *        {@link KeyProperties}.{@code AUTH} flags.
+         *
+         * @see #setUserAuthenticationRequired(boolean)
+         * @see BiometricPrompt
+         * @see BiometricPrompt.CryptoObject
+         * @see KeyguardManager
+         */
+        @NonNull
+        public Builder setUserAuthenticationParameters(@IntRange(from = -1) int timeout,
+                                                       @KeyProperties.AuthEnum int type) {
+            if (timeout < -1) {
+                throw new IllegalArgumentException("timeout must be -1 or larger");
+            } else if (timeout == -1) {
+                timeout = 0;
+            }
+            mUserAuthenticationValidityDurationSeconds = timeout;
+            mUserAuthenticationType = type;
             return this;
         }
 
@@ -1002,6 +1058,7 @@
                     mBlockModes,
                     mRandomizedEncryptionRequired,
                     mUserAuthenticationRequired,
+                    mUserAuthenticationType,
                     mUserAuthenticationValidityDurationSeconds,
                     mUserPresenceRequired,
                     mUserAuthenticationValidWhileOnBody,
diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java
index 79e48cd..37b1f23 100644
--- a/keystore/java/android/security/keystore/KeymasterUtils.java
+++ b/keystore/java/android/security/keystore/KeymasterUtils.java
@@ -88,17 +88,9 @@
      * Adds keymaster arguments to express the key's authorization policy supported by user
      * authentication.
      *
-     * @param userAuthenticationRequired whether user authentication is required to authorize the
-     *        use of the key.
-     * @param userAuthenticationValidityDurationSeconds duration of time (seconds) for which user
-     *        authentication is valid as authorization for using the key or {@code -1} if every
-     *        use of the key needs authorization.
-     * @param boundToSpecificSecureUserId if non-zero, specify which SID the key will be bound to,
-     *        overriding the default logic in this method where the key is bound to either the root
-     *        SID of the current user, or the fingerprint SID if explicit fingerprint authorization
-     *        is requested.
-     * @param userConfirmationRequired whether user confirmation is required to authorize the use
-     *        of the key.
+     * @param args The arguments sent to keymaster that need to be populated from the spec
+     * @param spec The user authentication relevant portions of the spec passed in from the caller.
+     *        This spec will be translated into the relevant keymaster tags to be loaded into args.
      * @throws IllegalStateException if user authentication is required but the system is in a wrong
      *         state (e.g., secure lock screen not set up) for generating or importing keys that
      *         require user authentication.
@@ -122,7 +114,7 @@
             return;
         }
 
-        if (spec.getUserAuthenticationValidityDurationSeconds() == -1) {
+        if (spec.getUserAuthenticationValidityDurationSeconds() == 0) {
             PackageManager pm = KeyStore.getApplicationContext().getPackageManager();
             // Every use of this key needs to be authorized by the user. This currently means
             // fingerprint or face auth.
@@ -168,7 +160,8 @@
                 args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
                         KeymasterArguments.toUint64(sids.get(i)));
             }
-            args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, KeymasterDefs.HW_AUTH_BIOMETRIC);
+
+            args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, spec.getUserAuthenticationType());
 
             if (spec.isUserAuthenticationValidWhileOnBody()) {
                 throw new ProviderException("Key validity extension while device is on-body is not "
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
index 98e4589..9c9773e 100644
--- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -97,6 +97,7 @@
         out.writeBoolean(mSpec.isRandomizedEncryptionRequired());
         out.writeBoolean(mSpec.isUserAuthenticationRequired());
         out.writeInt(mSpec.getUserAuthenticationValidityDurationSeconds());
+        out.writeInt(mSpec.getUserAuthenticationType());
         out.writeBoolean(mSpec.isUserPresenceRequired());
         out.writeByteArray(mSpec.getAttestationChallenge());
         out.writeBoolean(mSpec.isUniqueIdIncluded());
@@ -153,6 +154,7 @@
         final boolean randomizedEncryptionRequired = in.readBoolean();
         final boolean userAuthenticationRequired = in.readBoolean();
         final int userAuthenticationValidityDurationSeconds = in.readInt();
+        final int userAuthenticationTypes = in.readInt();
         final boolean userPresenceRequired = in.readBoolean();
         final byte[] attestationChallenge = in.createByteArray();
         final boolean uniqueIdIncluded = in.readBoolean();
@@ -185,6 +187,7 @@
                 randomizedEncryptionRequired,
                 userAuthenticationRequired,
                 userAuthenticationValidityDurationSeconds,
+                userAuthenticationTypes,
                 userPresenceRequired,
                 attestationChallenge,
                 uniqueIdIncluded,
diff --git a/keystore/java/android/security/keystore/UserAuthArgs.java b/keystore/java/android/security/keystore/UserAuthArgs.java
index 6952060..c9e9bf0 100644
--- a/keystore/java/android/security/keystore/UserAuthArgs.java
+++ b/keystore/java/android/security/keystore/UserAuthArgs.java
@@ -28,6 +28,7 @@
 
     boolean isUserAuthenticationRequired();
     int getUserAuthenticationValidityDurationSeconds();
+    @KeyProperties.AuthEnum int getUserAuthenticationType();
     boolean isUserAuthenticationValidWhileOnBody();
     boolean isInvalidatedByBiometricEnrollment();
     boolean isUserConfirmationRequired();
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 8cfd2d8..3208662 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -992,6 +992,11 @@
   return bag;
 }
 
+static bool compare_bag_entries(const ResolvedBag::Entry& entry1,
+    const ResolvedBag::Entry& entry2) {
+  return entry1.key < entry2.key;
+}
+
 const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& child_resids) {
   auto cached_iter = cached_bags_.find(resid);
   if (cached_iter != cached_bags_.end()) {
@@ -1027,13 +1032,15 @@
   child_resids.push_back(resid);
 
   uint32_t parent_resid = dtohl(map->parent.ident);
-  if (parent_resid == 0 || std::find(child_resids.begin(), child_resids.end(), parent_resid)
+  if (parent_resid == 0U || std::find(child_resids.begin(), child_resids.end(), parent_resid)
       != child_resids.end()) {
-    // There is no parent or that a circular dependency exist, meaning there is nothing to
-    // inherit and we can do a simple copy of the entries in the map.
+    // There is no parent or a circular dependency exist, meaning there is nothing to inherit and
+    // we can do a simple copy of the entries in the map.
     const size_t entry_count = map_entry_end - map_entry;
     util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>(
         malloc(sizeof(ResolvedBag) + (entry_count * sizeof(ResolvedBag::Entry))))};
+
+    bool sort_entries = false;
     ResolvedBag::Entry* new_entry = new_bag->entries;
     for (; map_entry != map_entry_end; ++map_entry) {
       uint32_t new_key = dtohl(map_entry->name.ident);
@@ -1059,8 +1066,15 @@
             new_entry->value.data, new_key);
         return nullptr;
       }
+      sort_entries = sort_entries ||
+          (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key));
       ++new_entry;
     }
+
+    if (sort_entries) {
+      std::sort(new_bag->entries, new_bag->entries + entry_count, compare_bag_entries);
+    }
+
     new_bag->type_spec_flags = entry.type_flags;
     new_bag->entry_count = static_cast<uint32_t>(entry_count);
     ResolvedBag* result = new_bag.get();
@@ -1091,6 +1105,7 @@
   const ResolvedBag::Entry* const parent_entry_end = parent_entry + parent_bag->entry_count;
 
   // The keys are expected to be in sorted order. Merge the two bags.
+  bool sort_entries = false;
   while (map_entry != map_entry_end && parent_entry != parent_entry_end) {
     uint32_t child_key = dtohl(map_entry->name.ident);
     if (!is_internal_resid(child_key)) {
@@ -1123,6 +1138,8 @@
       memcpy(new_entry, parent_entry, sizeof(*new_entry));
     }
 
+    sort_entries = sort_entries ||
+        (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key));
     if (child_key >= parent_entry->key) {
       // Move to the next parent entry if we used it or it was overridden.
       ++parent_entry;
@@ -1153,6 +1170,8 @@
                                        new_entry->value.dataType, new_entry->value.data, new_key);
       return nullptr;
     }
+    sort_entries = sort_entries ||
+        (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key));
     ++map_entry;
     ++new_entry;
   }
@@ -1172,6 +1191,10 @@
         new_bag.release(), sizeof(ResolvedBag) + (actual_count * sizeof(ResolvedBag::Entry)))));
   }
 
+  if (sort_entries) {
+    std::sort(new_bag->entries, new_bag->entries + actual_count, compare_bag_entries);
+  }
+
   // Combine flags from the parent and our own bag.
   new_bag->type_spec_flags = entry.type_flags | parent_bag->type_spec_flags;
   new_bag->entry_count = static_cast<uint32_t>(actual_count);
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index 2f6f3df..35fea7a 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -285,6 +285,27 @@
   EXPECT_EQ(0x03, get_package_id(bag->entries[1].key));
 }
 
+TEST_F(AssetManager2Test, FindsBagResourceFromMultipleSharedLibraries) {
+  AssetManager2 assetmanager;
+
+  // libclient is built with lib_one and then lib_two in order.
+  // Reverse the order to test that proper package ID re-assignment is happening.
+  assetmanager.SetApkAssets(
+      {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
+
+  const ResolvedBag* bag = assetmanager.GetBag(libclient::R::style::ThemeMultiLib);
+  ASSERT_NE(nullptr, bag);
+  ASSERT_EQ(bag->entry_count, 2u);
+
+  // First attribute comes from lib_two.
+  EXPECT_EQ(2, bag->entries[0].cookie);
+  EXPECT_EQ(0x02, get_package_id(bag->entries[0].key));
+
+  // The next two attributes come from lib_one.
+  EXPECT_EQ(2, bag->entries[1].cookie);
+  EXPECT_EQ(0x03, get_package_id(bag->entries[1].key));
+}
+
 TEST_F(AssetManager2Test, FindsStyleResourceWithParentFromSharedLibrary) {
   AssetManager2 assetmanager;
 
diff --git a/libs/androidfw/tests/CommonHelpers.h b/libs/androidfw/tests/CommonHelpers.h
index c160fbb..8af13f2 100644
--- a/libs/androidfw/tests/CommonHelpers.h
+++ b/libs/androidfw/tests/CommonHelpers.h
@@ -44,10 +44,6 @@
   return out << str.string();
 }
 
-static inline ::std::ostream& operator<<(::std::ostream& out, const String16& str) {
-  return out << String8(str).string();
-}
-
 static inline ::std::ostream& operator<<(::std::ostream& out, const ResTable_config& c) {
   return out << c.toString();
 }
diff --git a/libs/androidfw/tests/data/lib_two/R.h b/libs/androidfw/tests/data/lib_two/R.h
index 92b9cc1..fd5a910 100644
--- a/libs/androidfw/tests/data/lib_two/R.h
+++ b/libs/androidfw/tests/data/lib_two/R.h
@@ -30,16 +30,22 @@
     };
   };
 
+  struct integer {
+    enum : uint32_t {
+      bar = 0x02020000, // default
+    };
+  };
+
   struct string {
     enum : uint32_t {
-      LibraryString = 0x02020000,  // default
-      foo = 0x02020001, // default
+      LibraryString = 0x02030000,  // default
+      foo = 0x02030001, // default
     };
   };
 
   struct style {
     enum : uint32_t {
-      Theme = 0x02030000, // default
+      Theme = 0x02040000, // default
     };
   };
 };
diff --git a/libs/androidfw/tests/data/lib_two/lib_two.apk b/libs/androidfw/tests/data/lib_two/lib_two.apk
index 486c230..8193db6 100644
--- a/libs/androidfw/tests/data/lib_two/lib_two.apk
+++ b/libs/androidfw/tests/data/lib_two/lib_two.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/lib_two/res/values/values.xml b/libs/androidfw/tests/data/lib_two/res/values/values.xml
index 340d14c..4e1d69a 100644
--- a/libs/androidfw/tests/data/lib_two/res/values/values.xml
+++ b/libs/androidfw/tests/data/lib_two/res/values/values.xml
@@ -18,14 +18,17 @@
     <public type="attr" name="attr3" id="0x00010000" />
     <attr name="attr3" format="integer" />
 
-    <public type="string" name="LibraryString" id="0x00020000" />
+    <public type="integer" name="bar" id="0x00020000" />
+    <integer name="bar">1337</integer>
+
+    <public type="string" name="LibraryString" id="0x00030000" />
     <string name="LibraryString">Hi from library two</string>
 
-    <public type="string" name="foo" id="0x00020001" />
+    <public type="string" name="foo" id="0x00030001" />
     <string name="foo">Foo from lib_two</string>
 
-    <public type="style" name="Theme" id="0x02030000" />
+    <public type="style" name="Theme" id="0x00040000" />
     <style name="Theme">
-        <item name="com.android.lib_two:attr3">800</item>
+        <item name="com.android.lib_two:attr3">@integer/bar</item>
     </style>
 </resources>
diff --git a/libs/androidfw/tests/data/libclient/R.h b/libs/androidfw/tests/data/libclient/R.h
index 43d1f9b..e21b3eb 100644
--- a/libs/androidfw/tests/data/libclient/R.h
+++ b/libs/androidfw/tests/data/libclient/R.h
@@ -34,6 +34,7 @@
   struct style {
     enum : uint32_t {
       Theme = 0x7f020000,  // default
+      ThemeMultiLib = 0x7f020001,  // default
     };
   };
 
diff --git a/libs/androidfw/tests/data/libclient/libclient.apk b/libs/androidfw/tests/data/libclient/libclient.apk
index 1799024..4b9a8833 100644
--- a/libs/androidfw/tests/data/libclient/libclient.apk
+++ b/libs/androidfw/tests/data/libclient/libclient.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/libclient/res/values/values.xml b/libs/androidfw/tests/data/libclient/res/values/values.xml
index fead7c3..a29f473 100644
--- a/libs/androidfw/tests/data/libclient/res/values/values.xml
+++ b/libs/androidfw/tests/data/libclient/res/values/values.xml
@@ -27,6 +27,12 @@
       <item name="bar">@com.android.lib_one:string/foo</item>
     </style>
 
+    <public type="style" name="ThemeMultiLib" id="0x7f020001" />
+    <style name="ThemeMultiLib" >
+      <item name="com.android.lib_one:attr1">@com.android.lib_one:string/foo</item>
+      <item name="com.android.lib_two:attr3">@com.android.lib_two:integer/bar</item>
+    </style>
+
     <public type="string" name="foo_one" id="0x7f030000" />
     <string name="foo_one">@com.android.lib_one:string/foo</string>
 
diff --git a/location/java/android/location/AbstractListenerManager.java b/location/java/android/location/AbstractListenerManager.java
index 944ebf9..f075a53 100644
--- a/location/java/android/location/AbstractListenerManager.java
+++ b/location/java/android/location/AbstractListenerManager.java
@@ -27,6 +27,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
 
+import java.util.Objects;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
@@ -35,26 +36,34 @@
  *
  * @hide
  */
-abstract class AbstractListenerManager<T> {
+abstract class AbstractListenerManager<TRequest, TListener> {
 
-    private static class Registration<T> {
+    private static class Registration<TRequest, TListener> {
         private final Executor mExecutor;
-        @Nullable private volatile T mListener;
+        @Nullable private TRequest mRequest;
+        @Nullable private volatile TListener mListener;
 
-        private Registration(Executor executor, T listener) {
+        private Registration(@Nullable TRequest request, Executor executor, TListener listener) {
             Preconditions.checkArgument(listener != null, "invalid null listener/callback");
             Preconditions.checkArgument(executor != null, "invalid null executor");
             mExecutor = executor;
             mListener = listener;
+            mRequest = request;
+        }
+
+        @Nullable
+        public TRequest getRequest() {
+            return mRequest;
         }
 
         private void unregister() {
+            mRequest = null;
             mListener = null;
         }
 
-        private void execute(Consumer<T> operation) {
+        private void execute(Consumer<TListener> operation) {
             mExecutor.execute(() -> {
-                T listener = mListener;
+                TListener listener = mListener;
                 if (listener == null) {
                     return;
                 }
@@ -71,71 +80,135 @@
     }
 
     @GuardedBy("mListeners")
-    private final ArrayMap<Object, Registration<T>> mListeners = new ArrayMap<>();
+    private final ArrayMap<Object, Registration<TRequest, TListener>> mListeners =
+            new ArrayMap<>();
 
-    public boolean addListener(@NonNull T listener, @NonNull Handler handler)
+    @GuardedBy("mListeners")
+    @Nullable
+    private TRequest mMergedRequest;
+
+    public boolean addListener(@NonNull TListener listener, @NonNull Handler handler)
             throws RemoteException {
-        return addInternal(listener, handler);
+        return addInternal(/* request= */ null, listener, handler);
     }
 
-    public boolean addListener(@NonNull T listener, @NonNull Executor executor)
+    public boolean addListener(@NonNull TListener listener, @NonNull Executor executor)
             throws RemoteException {
-        return addInternal(listener, executor);
+        return addInternal(/* request= */ null, listener, executor);
     }
 
-    protected final boolean addInternal(@NonNull Object listener, @NonNull Handler handler)
-            throws RemoteException {
-        return addInternal(listener, new HandlerExecutor(handler));
+    public boolean addListener(@Nullable TRequest request, @NonNull TListener listener,
+            @NonNull Handler handler) throws RemoteException {
+        return addInternal(request, listener, handler);
     }
 
-    protected final boolean addInternal(@NonNull Object listener, @NonNull Executor executor)
+    public boolean addListener(@Nullable TRequest request, @NonNull TListener listener,
+            @NonNull Executor executor) throws RemoteException {
+        return addInternal(request, listener, executor);
+    }
+
+    protected final boolean addInternal(@Nullable TRequest request, @NonNull Object listener,
+            @NonNull Handler handler) throws RemoteException {
+        return addInternal(request, listener, new HandlerExecutor(handler));
+    }
+
+    protected final boolean addInternal(@Nullable TRequest request, @NonNull Object listener,
+            @NonNull Executor executor)
             throws RemoteException {
         Preconditions.checkArgument(listener != null, "invalid null listener/callback");
-        return addInternal(listener, new Registration<>(executor, convertKey(listener)));
+        return addInternal(listener, new Registration<>(request, executor, convertKey(listener)));
     }
 
-    private boolean addInternal(Object key, Registration<T> registration) throws RemoteException {
+    private boolean addInternal(Object key, Registration<TRequest, TListener> registration)
+            throws RemoteException {
         Preconditions.checkNotNull(registration);
 
         synchronized (mListeners) {
-            if (mListeners.isEmpty() && !registerService()) {
-                return false;
-            }
-            Registration<T> oldRegistration = mListeners.put(key, registration);
+            boolean initialRequest = mListeners.isEmpty();
+
+            Registration<TRequest, TListener> oldRegistration = mListeners.put(key, registration);
             if (oldRegistration != null) {
                 oldRegistration.unregister();
             }
+            TRequest merged = mergeRequests();
+
+            if (initialRequest || !Objects.equals(merged, mMergedRequest)) {
+                mMergedRequest = merged;
+                if (!initialRequest) {
+                    unregisterService();
+                }
+                registerService(mMergedRequest);
+            }
+
             return true;
         }
     }
 
     public void removeListener(Object listener) throws RemoteException {
         synchronized (mListeners) {
-            Registration<T> oldRegistration = mListeners.remove(listener);
+            Registration<TRequest, TListener> oldRegistration = mListeners.remove(listener);
             if (oldRegistration == null) {
                 return;
             }
             oldRegistration.unregister();
 
-            if (mListeners.isEmpty()) {
+            boolean lastRequest = mListeners.isEmpty();
+            TRequest merged = lastRequest ? null : mergeRequests();
+            boolean newRequest = !lastRequest && !Objects.equals(merged, mMergedRequest);
+
+            if (lastRequest || newRequest) {
                 unregisterService();
+                mMergedRequest = merged;
+                if (newRequest) {
+                    registerService(mMergedRequest);
+                }
             }
         }
     }
 
     @SuppressWarnings("unchecked")
-    protected T convertKey(@NonNull Object listener) {
-        return (T) listener;
+    protected TListener convertKey(@NonNull Object listener) {
+        return (TListener) listener;
     }
 
-    protected abstract boolean registerService() throws RemoteException;
+    protected abstract boolean registerService(TRequest request) throws RemoteException;
     protected abstract void unregisterService() throws RemoteException;
 
-    protected void execute(Consumer<T> operation) {
+    @Nullable
+    protected TRequest merge(@NonNull TRequest[] requests) {
+        for (TRequest request : requests) {
+            Preconditions.checkArgument(request == null,
+                    "merge() has to be overridden for non-null requests.");
+        }
+        return null;
+    }
+
+    protected void execute(Consumer<TListener> operation) {
         synchronized (mListeners) {
-            for (Registration<T> registration : mListeners.values()) {
+            for (Registration<TRequest, TListener> registration : mListeners.values()) {
                 registration.execute(operation);
             }
         }
     }
+
+    @GuardedBy("mListeners")
+    @SuppressWarnings("unchecked")
+    @Nullable
+    private TRequest mergeRequests() {
+        Preconditions.checkState(Thread.holdsLock(mListeners));
+
+        if (mListeners.isEmpty()) {
+            return null;
+        }
+
+        if (mListeners.size() == 1) {
+            return mListeners.valueAt(0).getRequest();
+        }
+
+        TRequest[] requests = (TRequest[]) new Object[mListeners.size()];
+        for (int index = 0; index < mListeners.size(); index++) {
+            requests[index] = mListeners.valueAt(index).getRequest();
+        }
+        return merge(requests);
+    }
 }
diff --git a/location/java/android/location/GnssAntennaInfo.aidl b/location/java/android/location/GnssAntennaInfo.aidl
new file mode 100644
index 0000000..2b956af
--- /dev/null
+++ b/location/java/android/location/GnssAntennaInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+parcelable GnssAntennaInfo;
\ No newline at end of file
diff --git a/location/java/android/location/GnssAntennaInfo.java b/location/java/android/location/GnssAntennaInfo.java
new file mode 100644
index 0000000..dfcaf81
--- /dev/null
+++ b/location/java/android/location/GnssAntennaInfo.java
@@ -0,0 +1,654 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/**
+ * A class that contains information about a GNSS antenna. GNSS antenna characteristics can change
+ * with device configuration, such as when a device is folded open or closed. Antenna information is
+ * delivered to registered instances of {@link Callback}.
+ */
+public final class GnssAntennaInfo implements Parcelable {
+    private final double mCarrierFrequencyMHz;
+    private final PhaseCenterOffsetCoordinates mPhaseCenterOffsetCoordinates;
+    private final PhaseCenterVariationCorrections mPhaseCenterVariationCorrections;
+    private final SignalGainCorrections mSignalGainCorrections;
+
+    /**
+     * Used for receiving GNSS antenna info from the GNSS engine. You can implement this interface
+     * and call {@link LocationManager#registerAntennaInfoCallback};
+     */
+    public abstract static class Callback {
+        /**
+         * The status of GNSS antenna info.
+         *
+         * @hide
+         */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef({STATUS_NOT_SUPPORTED, STATUS_READY, STATUS_LOCATION_DISABLED})
+        public @interface GnssAntennaInfoStatus {
+        }
+
+        /**
+         * The system does not support GNSS antenna info.
+         *
+         * This status will not change in the future.
+         */
+        public static final int STATUS_NOT_SUPPORTED = 0;
+
+        /**
+         * GNSS antenna info updates are being successfully tracked.
+         */
+        public static final int STATUS_READY = 1;
+
+        /**
+         * GNSS provider or Location is disabled, updated will not be received until they are
+         * enabled.
+         */
+        public static final int STATUS_LOCATION_DISABLED = 2;
+
+        /**
+         * Returns the latest GNSS antenna info. This event is triggered when a callback is
+         * registered, and whenever the antenna info changes (due to a device configuration change).
+         */
+        public void onGnssAntennaInfoReceived(@NonNull List<GnssAntennaInfo> gnssAntennaInfos) {}
+
+        /**
+         * Returns the latest status of the GNSS antenna info sub-system.
+         */
+        public void onStatusChanged(@GnssAntennaInfoStatus int status) {}
+    }
+
+    /**
+     * Class containing information about the antenna phase center offset (PCO). PCO is defined with
+     * respect to the origin of the Android sensor coordinate system, e.g., center of primary screen
+     * for mobiles - see sensor or form factor documents for details. Uncertainties are reported
+     *  to 1-sigma.
+     */
+    public static final class PhaseCenterOffsetCoordinates implements Parcelable {
+        private final double mPhaseCenterOffsetCoordinateXMillimeters;
+        private final double mPhaseCenterOffsetCoordinateXUncertaintyMillimeters;
+        private final double mPhaseCenterOffsetCoordinateYMillimeters;
+        private final double mPhaseCenterOffsetCoordinateYUncertaintyMillimeters;
+        private final double mPhaseCenterOffsetCoordinateZMillimeters;
+        private final double mPhaseCenterOffsetCoordinateZUncertaintyMillimeters;
+
+        @VisibleForTesting
+        public PhaseCenterOffsetCoordinates(double phaseCenterOffsetCoordinateXMillimeters,
+                double phaseCenterOffsetCoordinateXUncertaintyMillimeters,
+                double phaseCenterOffsetCoordinateYMillimeters,
+                double phaseCenterOffsetCoordinateYUncertaintyMillimeters,
+                double phaseCenterOffsetCoordinateZMillimeters,
+                double phaseCenterOffsetCoordinateZUncertaintyMillimeters) {
+            mPhaseCenterOffsetCoordinateXMillimeters = phaseCenterOffsetCoordinateXMillimeters;
+            mPhaseCenterOffsetCoordinateYMillimeters = phaseCenterOffsetCoordinateYMillimeters;
+            mPhaseCenterOffsetCoordinateZMillimeters = phaseCenterOffsetCoordinateZMillimeters;
+            mPhaseCenterOffsetCoordinateXUncertaintyMillimeters =
+                    phaseCenterOffsetCoordinateXUncertaintyMillimeters;
+            mPhaseCenterOffsetCoordinateYUncertaintyMillimeters =
+                    phaseCenterOffsetCoordinateYUncertaintyMillimeters;
+            mPhaseCenterOffsetCoordinateZUncertaintyMillimeters =
+                    phaseCenterOffsetCoordinateZUncertaintyMillimeters;
+        }
+
+        public static final @NonNull Creator<PhaseCenterOffsetCoordinates> CREATOR =
+                new Creator<PhaseCenterOffsetCoordinates>() {
+                    @Override
+                    public PhaseCenterOffsetCoordinates createFromParcel(Parcel in) {
+                        return new PhaseCenterOffsetCoordinates(
+                                in.readDouble(),
+                                in.readDouble(),
+                                in.readDouble(),
+                                in.readDouble(),
+                                in.readDouble(),
+                                in.readDouble()
+                        );
+                    }
+
+                    @Override
+                    public PhaseCenterOffsetCoordinates[] newArray(int size) {
+                        return new PhaseCenterOffsetCoordinates[size];
+                    }
+                };
+
+        public double getXCoordMillimeters() {
+            return mPhaseCenterOffsetCoordinateXMillimeters;
+        }
+
+        public double getXCoordUncertaintyMillimeters() {
+            return mPhaseCenterOffsetCoordinateXUncertaintyMillimeters;
+        }
+
+        public double getYCoordMillimeters() {
+            return mPhaseCenterOffsetCoordinateYMillimeters;
+        }
+
+        public double getYCoordUncertaintyMillimeters() {
+            return mPhaseCenterOffsetCoordinateYUncertaintyMillimeters;
+        }
+
+        public double getZCoordMillimeters() {
+            return mPhaseCenterOffsetCoordinateZMillimeters;
+        }
+
+        public double getZCoordUncertaintyMillimeters() {
+            return mPhaseCenterOffsetCoordinateZUncertaintyMillimeters;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeDouble(mPhaseCenterOffsetCoordinateXMillimeters);
+            dest.writeDouble(mPhaseCenterOffsetCoordinateXUncertaintyMillimeters);
+            dest.writeDouble(mPhaseCenterOffsetCoordinateYMillimeters);
+            dest.writeDouble(mPhaseCenterOffsetCoordinateYUncertaintyMillimeters);
+            dest.writeDouble(mPhaseCenterOffsetCoordinateZMillimeters);
+            dest.writeDouble(mPhaseCenterOffsetCoordinateZUncertaintyMillimeters);
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder("PhaseCenteroffset:\n");
+            builder.append("X: " + mPhaseCenterOffsetCoordinateXMillimeters + " +/- "
+                    + mPhaseCenterOffsetCoordinateXUncertaintyMillimeters + "\n");
+            builder.append("Y: " + mPhaseCenterOffsetCoordinateYMillimeters + " +/- "
+                    + mPhaseCenterOffsetCoordinateYUncertaintyMillimeters + "\n");
+            builder.append("Z: " + mPhaseCenterOffsetCoordinateZMillimeters + " +/- "
+                    + mPhaseCenterOffsetCoordinateZUncertaintyMillimeters + "\n");
+            return builder.toString();
+        }
+    }
+
+    /**
+     * Class containing information about the phase center variation (PCV) corrections. The PCV
+     * correction is added to the phase measurement to obtain the corrected value.
+     *
+     * The corrections and associated (1-sigma) uncertainties are represented by respect 2D arrays.
+     *
+     * Each row (major indices) represents a fixed theta. The first row corresponds to a
+     * theta angle of 0 degrees. The last row corresponds to a theta angle of (360 - deltaTheta)
+     * degrees, where deltaTheta is the regular spacing between azimuthal angles, i.e., deltaTheta
+     * = 360 / (number of rows).
+     *
+     * The columns (minor indices) represent fixed zenith angles, beginning at 0 degrees and ending
+     * at 180 degrees. They are separated by deltaPhi, the regular spacing between zenith angles,
+     * i.e., deltaPhi = 180 / (number of columns - 1).
+     */
+    public static final class PhaseCenterVariationCorrections extends SphericalCorrections {
+
+        @VisibleForTesting
+        public PhaseCenterVariationCorrections(
+                @NonNull double[][] phaseCenterVariationCorrectionsMillimeters,
+                @NonNull double[][] phaseCenterVariationCorrectionUncertaintiesMillimeters) {
+            super(phaseCenterVariationCorrectionsMillimeters,
+                    phaseCenterVariationCorrectionUncertaintiesMillimeters);
+        }
+
+        private PhaseCenterVariationCorrections(@NonNull Parcel in) {
+            super(in);
+        }
+
+        /**
+         * Get the phase center variation correction in millimeters at the specified row and column
+         * in the underlying 2D array.
+         * @param row zero-based major index in the array
+         * @param column zero-based minor index in the array
+         * @return phase center correction in millimeters
+         */
+        public double getPhaseCenterVariationCorrectionMillimetersAt(int row, int column) {
+            return super.getCorrectionAt(row, column);
+        }
+
+        /**
+         * Get the phase center variation correction uncertainty in millimeters at the specified row
+         * and column in the underlying 2D array.
+         * @param row zero-based major index in the array
+         * @param column zero-based minor index in the array
+         * @return 1-sigma phase center correction uncertainty in millimeters
+         */
+        public double getPhaseCenterVariationCorrectionUncertaintyMillimetersAt(
+                int row, int column) {
+            return super.getCorrectionUncertaintyAt(row, column);
+        }
+
+        public @NonNull double[][] getRawCorrectionsArray() {
+            return super.getRawCorrectionsArray().clone();
+        }
+
+        public @NonNull double[][] getRawCorrectionUncertaintiesArray() {
+            return super.getRawCorrectionUncertaintiesArray().clone();
+        }
+
+        public int getNumRows() {
+            return super.getNumRows();
+        }
+
+        public int getNumColumns() {
+            return super.getNumColumns();
+        }
+
+        /**
+         * The fixed theta angle separation between successive rows.
+         */
+        public double getDeltaTheta() {
+            return super.getDeltaTheta();
+        }
+
+        /**
+         * The fixed phi angle separation between successive columns.
+         */
+        public double getDeltaPhi() {
+            return super.getDeltaPhi();
+        }
+
+        public static final @NonNull Creator<PhaseCenterVariationCorrections> CREATOR =
+                new Creator<PhaseCenterVariationCorrections>() {
+                    @Override
+                    public PhaseCenterVariationCorrections createFromParcel(Parcel in) {
+                        return new PhaseCenterVariationCorrections(in);
+                    }
+
+                    @Override
+                    public PhaseCenterVariationCorrections[] newArray(int size) {
+                        return new PhaseCenterVariationCorrections[size];
+                    }
+                };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder("PhaseCenterVariationCorrections:\n");
+            builder.append(super.toString());
+            return builder.toString();
+        }
+    }
+
+    /**
+     * Class containing information about the signal gain (SG) corrections. The SG
+     * correction is added to the signal gain to obtain the corrected value.
+     *
+     * The corrections and associated (1-sigma) uncertainties are represented by respect 2D arrays.
+     *
+     * Each row (major indices) represents a fixed theta. The first row corresponds to a
+     * theta angle of 0 degrees. The last row corresponds to a theta angle of (360 - deltaTheta)
+     * degrees, where deltaTheta is the regular spacing between azimuthal angles, i.e., deltaTheta
+     * = 360 / (number of rows).
+     *
+     * The columns (minor indices) represent fixed zenith angles, beginning at 0 degrees and ending
+     * at 180 degrees. They are separated by deltaPhi, the regular spacing between zenith angles,
+     * i.e., deltaPhi = 180 / (number of columns - 1).
+     */
+    public static final class SignalGainCorrections extends SphericalCorrections {
+
+        @VisibleForTesting
+        public SignalGainCorrections(
+                @NonNull double[][] signalGainCorrectionsDbi,
+                @NonNull double[][] signalGainCorrectionUncertaintiesDbi) {
+            super(signalGainCorrectionsDbi,
+                    signalGainCorrectionUncertaintiesDbi);
+        }
+
+        private SignalGainCorrections(@NonNull Parcel in) {
+            super(in);
+        }
+
+        /**
+         * Get the signal gain correction in dbi at the specified row and column
+         * in the underlying 2D array.
+         * @param row zero-based major index in the array
+         * @param column zero-based minor index in the array
+         * @return signal gain correction in dbi
+         */
+        public double getSignalGainCorrectionDbiAt(int row, int column) {
+            return super.getCorrectionAt(row, column);
+        }
+
+        /**
+         * Get the signal gain correction correction uncertainty in dbi at the specified row
+         * and column in the underlying 2D array.
+         * @param row zero-based major index in the array
+         * @param column zero-based minor index in the array
+         * @return 1-sigma signal gain correction uncertainty in dbi
+         */
+        public double getSignalGainCorrectionUncertaintyDbiAt(int row, int column) {
+            return super.getCorrectionUncertaintyAt(row, column);
+        }
+
+        public @NonNull double[][] getRawCorrectionsArray() {
+            return super.getRawCorrectionsArray().clone();
+        }
+
+        public @NonNull double[][] getRawCorrectionUncertaintiesArray() {
+            return super.getRawCorrectionUncertaintiesArray().clone();
+        }
+
+        public int getNumRows() {
+            return super.getNumRows();
+        }
+
+        public int getNumColumns() {
+            return super.getNumColumns();
+        }
+
+        /**
+         * The fixed theta angle separation between successive rows.
+         */
+        public double getDeltaTheta() {
+            return super.getDeltaTheta();
+        }
+
+        /**
+         * The fixed phi angle separation between successive columns.
+         */
+        public double getDeltaPhi() {
+            return super.getDeltaPhi();
+        }
+
+        public static final @NonNull Creator<SignalGainCorrections> CREATOR =
+                new Creator<SignalGainCorrections>() {
+                    @Override
+                    public SignalGainCorrections createFromParcel(Parcel in) {
+                        return new SignalGainCorrections(in);
+                    }
+
+                    @Override
+                    public SignalGainCorrections[] newArray(int size) {
+                        return new SignalGainCorrections[size];
+                    }
+                };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder("SignalGainCorrections:\n");
+            builder.append(super.toString());
+            return builder.toString();
+        }
+    }
+
+    /**
+     * Represents corrections on a spherical mapping.
+     *
+     * Each row (major indices) represents a fixed theta. The first row corresponds to a
+     * theta angle of 0 degrees. The last row corresponds to a theta angle of (360 - deltaTheta)
+     * degrees, where deltaTheta is the regular spacing between azimuthal angles, i.e., deltaTheta
+     * = 360 / (number of rows).
+     *
+     * The columns (minor indices) represent fixed zenith angles, beginning at 0 degrees and ending
+     * at 180 degrees. They are separated by deltaPhi, the regular spacing between zenith angles,
+     * i.e., deltaPhi = 180 / (number of columns - 1).
+     */
+    private abstract static class SphericalCorrections implements Parcelable {
+        private final double[][] mCorrections;
+        private final double[][] mCorrectionUncertainties;
+        private final double mDeltaTheta;
+        private final double mDeltaPhi;
+        private final int mNumRows;
+        private final int mNumColumns;
+
+        SphericalCorrections(@NonNull double[][] corrections,
+                @NonNull double[][] correctionUncertainties) {
+            if (corrections.length != correctionUncertainties.length
+                    || corrections[0].length != correctionUncertainties[0].length) {
+                throw new IllegalArgumentException("Correction and correction uncertainty arrays "
+                        + "must have the same dimensions.");
+            }
+
+            mNumRows = corrections.length;
+            if (mNumRows < 1) {
+                throw new IllegalArgumentException("Arrays must have at least one row.");
+            }
+
+            mNumColumns = corrections[0].length;
+            if (mNumColumns < 2) {
+                throw new IllegalArgumentException("Arrays must have at least two columns.");
+            }
+
+            mCorrections = corrections;
+            mCorrectionUncertainties = correctionUncertainties;
+            mDeltaTheta = 360.0d / mNumRows;
+            mDeltaPhi = 180.0d / (mNumColumns - 1);
+        }
+
+        SphericalCorrections(Parcel in) {
+            int numRows = in.readInt();
+            int numColumns = in.readInt();
+
+            double[][] corrections =
+                    new double[numRows][numColumns];
+            double[][] correctionUncertainties =
+                    new double[numRows][numColumns];
+
+            for (int row = 0; row < numRows; row++) {
+                in.readDoubleArray(corrections[row]);
+            }
+
+            for (int row = 0; row < numRows; row++) {
+                in.readDoubleArray(correctionUncertainties[row]);
+            }
+
+            mNumRows = corrections.length;
+            mNumColumns = corrections[0].length;
+            mCorrections = corrections;
+            mCorrectionUncertainties = correctionUncertainties;
+            mDeltaTheta = 360.0d / mNumRows;
+            mDeltaPhi = 180.0d / (mNumColumns - 1);
+        }
+
+        private double getCorrectionAt(int row, int column) {
+            return mCorrections[row][column];
+        }
+
+        private double getCorrectionUncertaintyAt(int row, int column) {
+            return mCorrectionUncertainties[row][column];
+        }
+
+        @NonNull
+        private double[][] getRawCorrectionsArray() {
+            return mCorrections;
+        }
+
+        @NonNull
+        private double[][] getRawCorrectionUncertaintiesArray() {
+            return mCorrectionUncertainties;
+        }
+
+        private int getNumRows() {
+            return mNumRows;
+        }
+
+        private int getNumColumns() {
+            return mNumColumns;
+        }
+
+        /**
+         * The fixed theta angle separation between successive rows.
+         */
+        private double getDeltaTheta() {
+            return mDeltaTheta;
+        }
+
+        /**
+         * The fixed phi angle separation between successive columns.
+         */
+        private double getDeltaPhi() {
+            return mDeltaPhi;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeInt(mNumRows);
+            dest.writeInt(mNumColumns);
+            for (double[] row: mCorrections) {
+                dest.writeDoubleArray(row);
+            }
+            for (double[] row: mCorrectionUncertainties) {
+                dest.writeDoubleArray(row);
+            }
+        }
+
+        private String arrayToString(double[][] array) {
+            StringBuilder builder = new StringBuilder();
+            for (int row = 0; row < mNumRows; row++) {
+                builder.append("[ ");
+                for (int column = 0; column < mNumColumns - 1; column++) {
+                    builder.append(array[row][column] + ", ");
+                }
+                builder.append(array[row][mNumColumns - 1] + " ]\n");
+            }
+            return builder.toString();
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder();
+            builder.append("DeltaTheta: " + mDeltaTheta + "\n");
+            builder.append("DeltaPhi: " + mDeltaPhi + "\n");
+            builder.append("CorrectionsArray:\n");
+            builder.append(arrayToString(mCorrections));
+            builder.append("CorrectionUncertaintiesArray:\n");
+            builder.append(arrayToString(mCorrectionUncertainties));
+            return builder.toString();
+        }
+    }
+
+    @VisibleForTesting
+    public GnssAntennaInfo(
+            double carrierFrequencyMHz,
+            @NonNull PhaseCenterOffsetCoordinates phaseCenterOffsetCoordinates,
+            @Nullable PhaseCenterVariationCorrections phaseCenterVariationCorrections,
+            @Nullable SignalGainCorrections signalGainCorrectionDbi) {
+        if (phaseCenterOffsetCoordinates == null) {
+            throw new IllegalArgumentException("Phase Center Offset Coordinates cannot be null.");
+        }
+        mCarrierFrequencyMHz = carrierFrequencyMHz;
+        mPhaseCenterOffsetCoordinates = phaseCenterOffsetCoordinates;
+        mPhaseCenterVariationCorrections = phaseCenterVariationCorrections;
+        mSignalGainCorrections = signalGainCorrectionDbi;
+    }
+
+    public double getCarrierFrequencyMHz() {
+        return mCarrierFrequencyMHz;
+    }
+
+    @NonNull
+    public PhaseCenterOffsetCoordinates getPhaseCenterOffsetCoordinates() {
+        return mPhaseCenterOffsetCoordinates;
+    }
+
+    @Nullable
+    public PhaseCenterVariationCorrections getPhaseCenterVariationCorrections() {
+        return mPhaseCenterVariationCorrections;
+    }
+
+    @Nullable
+    public SignalGainCorrections getSignalGainCorrections() {
+        return mSignalGainCorrections;
+    }
+
+    public static final @android.annotation.NonNull
+                    Creator<GnssAntennaInfo> CREATOR = new Creator<GnssAntennaInfo>() {
+                            @Override
+                            public GnssAntennaInfo createFromParcel(Parcel in) {
+                                double carrierFrequencyMHz = in.readDouble();
+
+                                ClassLoader classLoader = getClass().getClassLoader();
+                                PhaseCenterOffsetCoordinates phaseCenterOffsetCoordinates =
+                                        in.readParcelable(classLoader);
+                                PhaseCenterVariationCorrections phaseCenterVariationCorrections =
+                                        in.readParcelable(classLoader);
+                                SignalGainCorrections signalGainCorrections =
+                                        in.readParcelable(classLoader);
+
+                                return new GnssAntennaInfo(carrierFrequencyMHz,
+                                        phaseCenterOffsetCoordinates,
+                                        phaseCenterVariationCorrections, signalGainCorrections);
+                            }
+
+                            @Override
+                            public GnssAntennaInfo[] newArray(int size) {
+                                return new GnssAntennaInfo[size];
+                            }
+                    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel parcel, int flags) {
+        parcel.writeDouble(mCarrierFrequencyMHz);
+
+        // Write Phase Center Offset
+        parcel.writeParcelable(mPhaseCenterOffsetCoordinates, flags);
+
+        // Write Phase Center Variation Corrections
+        parcel.writeParcelable(mPhaseCenterVariationCorrections, flags);
+
+        // Write Signal Gain Corrections
+        parcel.writeParcelable(mSignalGainCorrections, flags);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder("[ GnssAntennaInfo:\n");
+        builder.append("CarrierFrequencyMHz: " + mCarrierFrequencyMHz + "\n");
+        builder.append(mPhaseCenterOffsetCoordinates.toString());
+        builder.append(mPhaseCenterVariationCorrections == null
+                ? "PhaseCenterVariationCorrections: null\n"
+                : mPhaseCenterVariationCorrections.toString());
+        builder.append(mSignalGainCorrections == null
+                ? "SignalGainCorrections: null\n"
+                : mSignalGainCorrections.toString());
+        builder.append("]");
+        return builder.toString();
+    }
+}
diff --git a/location/java/android/location/GnssCapabilities.java b/location/java/android/location/GnssCapabilities.java
index 2e2f984..930180c 100644
--- a/location/java/android/location/GnssCapabilities.java
+++ b/location/java/android/location/GnssCapabilities.java
@@ -82,6 +82,12 @@
      */
     public static final long MEASUREMENT_CORRECTIONS_REFLECTING_PLANE           = 1L << 8;
 
+    /**
+     * Bit mask indicating GNSS chipset supports GNSS antenna info.
+     * @hide
+     */
+    public static final long ANTENNA_INFO                                       = 1L << 9;
+
     /** @hide */
     public static final long INVALID_CAPABILITIES = -1;
 
@@ -165,6 +171,13 @@
         return hasCapability(MEASUREMENT_CORRECTIONS_REFLECTING_PLANE);
     }
 
+    /**
+     * Returns {@code true} if GNSS chipset supports antenna info, {@code false} otherwise.
+     */
+    public boolean hasGnssAntennaInfo() {
+        return hasCapability(ANTENNA_INFO);
+    }
+
     @NonNull
     @Override
     public String toString() {
@@ -172,6 +185,7 @@
         if (hasLowPowerMode()) sb.append("LOW_POWER_MODE ");
         if (hasSatelliteBlacklist()) sb.append("SATELLITE_BLACKLIST ");
         if (hasGeofencing()) sb.append("GEOFENCING ");
+        if (hasGnssAntennaInfo()) sb.append("ANTENNA_INFO ");
         if (hasMeasurements()) sb.append("MEASUREMENTS ");
         if (hasNavMessages()) sb.append("NAV_MESSAGES ");
         if (hasMeasurementCorrections()) sb.append("MEASUREMENT_CORRECTIONS ");
diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java
index f17fa39..4d01cdc 100644
--- a/location/java/android/location/GnssStatus.java
+++ b/location/java/android/location/GnssStatus.java
@@ -24,6 +24,8 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * This class represents the current state of the GNSS engine and is used in conjunction with
@@ -339,6 +341,33 @@
         }
     }
 
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof GnssStatus)) {
+            return false;
+        }
+
+        GnssStatus that = (GnssStatus) o;
+        return mSvCount == that.mSvCount
+                && Arrays.equals(mSvidWithFlags, that.mSvidWithFlags)
+                && Arrays.equals(mCn0DbHzs, that.mCn0DbHzs)
+                && Arrays.equals(mElevations, that.mElevations)
+                && Arrays.equals(mAzimuths, that.mAzimuths)
+                && Arrays.equals(mCarrierFrequencies, that.mCarrierFrequencies)
+                && Arrays.equals(mBasebandCn0DbHzs, that.mBasebandCn0DbHzs);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = Objects.hash(mSvCount);
+        result = 31 * result + Arrays.hashCode(mSvidWithFlags);
+        result = 31 * result + Arrays.hashCode(mCn0DbHzs);
+        return result;
+    }
+
     /**
      * Builder class to help create new GnssStatus instances.
      */
@@ -451,8 +480,8 @@
             mCn0DbHz = cn0DbHz;
             mElevation = elevation;
             mAzimuth = azimuth;
-            mCarrierFrequency = carrierFrequency;
-            mBasebandCn0DbHz = basebandCn0DbHz;
+            mCarrierFrequency = hasCarrierFrequency ? carrierFrequency : 0;
+            mBasebandCn0DbHz = hasBasebandCn0DbHz ? basebandCn0DbHz : 0;
         }
     }
 }
diff --git a/location/java/android/location/GpsClock.java b/location/java/android/location/GpsClock.java
index f123766..58af6ee 100644
--- a/location/java/android/location/GpsClock.java
+++ b/location/java/android/location/GpsClock.java
@@ -25,8 +25,11 @@
  * A class containing a GPS clock timestamp.
  * It represents a measurement of the GPS receiver's clock.
  *
+ * @deprecated use {@link GnssClock} instead.
+ *
  * @hide
  */
+@Deprecated
 @SystemApi
 public class GpsClock implements Parcelable {
 
diff --git a/location/java/android/location/GpsMeasurement.java b/location/java/android/location/GpsMeasurement.java
index 27a8189..c2ab4ab 100644
--- a/location/java/android/location/GpsMeasurement.java
+++ b/location/java/android/location/GpsMeasurement.java
@@ -24,8 +24,11 @@
 /**
  * A class representing a GPS satellite measurement, containing raw and computed information.
  *
+ * @deprecated use {@link GnssMeasurement} instead.
+ *
  * @hide
  */
+@Deprecated
 @SystemApi
 public class GpsMeasurement implements Parcelable {
     private int mFlags;
diff --git a/location/java/android/location/GpsMeasurementsEvent.java b/location/java/android/location/GpsMeasurementsEvent.java
index d69158d..f3feb7a 100644
--- a/location/java/android/location/GpsMeasurementsEvent.java
+++ b/location/java/android/location/GpsMeasurementsEvent.java
@@ -30,8 +30,11 @@
  * A class implementing a container for data associated with a measurement event.
  * Events are delivered to registered instances of {@link Listener}.
  *
+ * @deprecated use {@link GnssMeasurementsEvent} instead.
+ *
  * @hide
  */
+@Deprecated
 @SystemApi
 public class GpsMeasurementsEvent implements Parcelable {
 
diff --git a/location/java/android/location/GpsNavigationMessage.java b/location/java/android/location/GpsNavigationMessage.java
index 6eeea26..dc1e99f 100644
--- a/location/java/android/location/GpsNavigationMessage.java
+++ b/location/java/android/location/GpsNavigationMessage.java
@@ -26,8 +26,11 @@
 /**
  * A class containing a GPS satellite Navigation Message.
  *
+ * @deprecated use {@link GnssNavigationMessage} instead.
+ *
  * @hide
  */
+@Deprecated
 @SystemApi
 public class GpsNavigationMessage implements Parcelable {
 
diff --git a/location/java/android/location/GpsNavigationMessageEvent.java b/location/java/android/location/GpsNavigationMessageEvent.java
index f60e5c7..2d5d6eb 100644
--- a/location/java/android/location/GpsNavigationMessageEvent.java
+++ b/location/java/android/location/GpsNavigationMessageEvent.java
@@ -27,8 +27,11 @@
  * A class implementing a container for data associated with a navigation message event.
  * Events are delivered to registered instances of {@link Listener}.
  *
+ * @deprecated use {@link GnssNavigationMessage} instead.
+ *
  * @hide
  */
+@Deprecated
 @SystemApi
 public class GpsNavigationMessageEvent implements Parcelable {
 
diff --git a/location/java/android/location/IGnssAntennaInfoListener.aidl b/location/java/android/location/IGnssAntennaInfoListener.aidl
new file mode 100644
index 0000000..30bf5467
--- /dev/null
+++ b/location/java/android/location/IGnssAntennaInfoListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.location.GnssAntennaInfo;
+
+/**
+ * {@hide}
+ */
+oneway interface IGnssAntennaInfoListener {
+    void onGnssAntennaInfoReceived(in List<GnssAntennaInfo> gnssAntennaInfo);
+    void onStatusChanged(in int status);
+}
\ No newline at end of file
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 6a5c0ec..8600dc4 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -22,7 +22,9 @@
 import android.location.GeocoderParams;
 import android.location.Geofence;
 import android.location.GnssMeasurementCorrections;
+import android.location.GnssRequest;
 import android.location.IBatchedLocationCallback;
+import android.location.IGnssAntennaInfoListener;
 import android.location.IGnssMeasurementsListener;
 import android.location.IGnssStatusListener;
 import android.location.IGnssNavigationMessageListener;
@@ -69,13 +71,19 @@
         double upperRightLatitude, double upperRightLongitude, int maxResults,
         in GeocoderParams params, out List<Address> addrs);
 
-    boolean addGnssMeasurementsListener(in IGnssMeasurementsListener listener,
-             String packageName, String featureId, String listenerIdentifier);
+    boolean addGnssMeasurementsListener(in GnssRequest request,
+            in IGnssMeasurementsListener listener,
+            String packageName, String featureId,
+            String listenerIdentifier);
     void injectGnssMeasurementCorrections(in GnssMeasurementCorrections corrections,
             in String packageName);
     long getGnssCapabilities(in String packageName);
     void removeGnssMeasurementsListener(in IGnssMeasurementsListener listener);
 
+    boolean addGnssAntennaInfoListener(in IGnssAntennaInfoListener listener,
+             String packageName, String featureId, String listenerIdentifier);
+    void removeGnssAntennaInfoListener(in IGnssAntennaInfoListener listener);
+
     boolean addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener,
              String packageName, String featureId, String listenerIdentifier);
     void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener);
@@ -90,7 +98,7 @@
     boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName);
     void flushGnssBatch(String packageName);
     boolean stopGnssBatch();
-    boolean injectLocation(in Location location);
+    void injectLocation(in Location location);
 
     @UnsupportedAppUsage
     List<String> getAllProviders();
@@ -107,6 +115,7 @@
 
     boolean isProviderEnabledForUser(String provider, int userId);
     boolean isLocationEnabledForUser(int userId);
+    void setLocationEnabledForUser(boolean enabled, int userId);
     void addTestProvider(String name, in ProviderProperties properties, String opPackageName);
     void removeTestProvider(String provider, String opPackageName);
     void setTestProviderLocation(String provider, in Location loc, String opPackageName);
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index eb76c29..6724324 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -64,23 +64,18 @@
     public static final int FORMAT_SECONDS = 2;
 
     /**
-     * Bundle key for a version of the location that has been fed through
-     * LocationFudger. Allows location providers to flag locations as being
-     * safe for use with ACCESS_COARSE_LOCATION permission.
-     *
-     * @hide
-     */
-    public static final String EXTRA_COARSE_LOCATION = "coarseLocation";
-
-    /**
      * Bundle key for a version of the location containing no GPS data.
      * Allows location providers to flag locations as being safe to
      * feed to LocationFudger.
      *
      * @hide
+     * @deprecated As of Android R, this extra is longer in use, since it is not necessary to keep
+     * gps locations separate from other locations for coarsening. Providers that do not need to
+     * support platforms below Android R should not use this constant.
      */
     @TestApi
     @SystemApi
+    @Deprecated
     public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
 
     /**
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 7e6486c..3d0765b 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -251,7 +251,7 @@
      * @hide
      */
     public static final String HIGH_POWER_REQUEST_CHANGE_ACTION =
-        "android.location.HIGH_POWER_REQUEST_CHANGE";
+            "android.location.HIGH_POWER_REQUEST_CHANGE";
 
     /**
      * Broadcast intent action for Settings app to inject a footer at the bottom of location
@@ -310,6 +310,8 @@
             new GnssMeasurementsListenerManager();
     private final GnssNavigationMessageListenerManager mGnssNavigationMessageListenerTransport =
             new GnssNavigationMessageListenerManager();
+    private final GnssAntennaInfoListenerManager mGnssAntennaInfoListenerManager =
+            new GnssAntennaInfoListenerManager();
 
     /**
      * @hide
@@ -478,13 +480,11 @@
     @TestApi
     @RequiresPermission(WRITE_SECURE_SETTINGS)
     public void setLocationEnabledForUser(boolean enabled, @NonNull UserHandle userHandle) {
-        Settings.Secure.putIntForUser(
-                mContext.getContentResolver(),
-                Settings.Secure.LOCATION_MODE,
-                enabled
-                        ? Settings.Secure.LOCATION_MODE_ON
-                        : Settings.Secure.LOCATION_MODE_OFF,
-                userHandle.getIdentifier());
+        try {
+            mService.setLocationEnabledForUser(enabled, userHandle.getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -1217,7 +1217,7 @@
      * the first fix.
      *
      * @param location newly available {@link Location} object
-     * @return true if the location was successfully injected, false otherwise
+     * @return true if the location was injected, false otherwise
      *
      * @throws IllegalArgumentException if location is null
      * @throws SecurityException if permissions are not present
@@ -1231,7 +1231,8 @@
                 "incomplete location object, missing timestamp or accuracy?");
 
         try {
-            return mService.injectLocation(location);
+            mService.injectLocation(location);
+            return true;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2199,7 +2200,7 @@
      * Registers a GNSS Measurement callback.
      *
      * @param request  extra parameters to pass to GNSS measurement provider. For example, if {@link
-     *                 GnssRequest#isFullTrackingEnabled()} is true, GNSS chipset switches off duty
+     *                 GnssRequest#isFullTracking()} is true, GNSS chipset switches off duty
      *                 cycling.
      * @param executor the executor that the callback runs on.
      * @param callback a {@link GnssMeasurementsEvent.Callback} object to register.
@@ -2216,7 +2217,12 @@
             @NonNull GnssRequest request,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull GnssMeasurementsEvent.Callback callback) {
-        throw new RuntimeException();
+        Preconditions.checkArgument(request != null, "invalid null request");
+        try {
+            return mGnssMeasurementsListenerManager.addListener(request, callback, executor);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -2257,6 +2263,41 @@
     }
 
     /**
+     * Registers a Gnss Antenna Info callback.
+     *
+     * @param executor the executor that the callback runs on.
+     * @param callback a {@link GnssAntennaInfo.Callback} object to register.
+     * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+     *
+     * @throws IllegalArgumentException if executor is null
+     * @throws IllegalArgumentException if callback is null
+     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+     */
+    @RequiresPermission(ACCESS_FINE_LOCATION)
+    public boolean registerAntennaInfoCallback(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull GnssAntennaInfo.Callback callback) {
+        try {
+            return mGnssAntennaInfoListenerManager.addListener(callback, executor);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unregisters a GNSS Antenna Info callback.
+     *
+     * @param callback a {@link GnssAntennaInfo.Callback} object to remove.
+     */
+    public void unregisterAntennaInfoCallback(@NonNull GnssAntennaInfo.Callback callback) {
+        try {
+            mGnssAntennaInfoListenerManager.removeListener(callback);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * No-op method to keep backward-compatibility.
      *
      * @hide
@@ -2763,8 +2804,7 @@
     }
 
     private class GnssStatusListenerManager extends
-            AbstractListenerManager<GnssStatus.Callback> {
-
+            AbstractListenerManager<Void, GnssStatus.Callback> {
         @Nullable
         private IGnssStatusListener mListenerTransport;
 
@@ -2782,19 +2822,19 @@
 
         public boolean addListener(@NonNull GpsStatus.Listener listener, @NonNull Executor executor)
                 throws RemoteException {
-            return addInternal(listener, executor);
+            return addInternal(null, listener, executor);
         }
 
         public boolean addListener(@NonNull OnNmeaMessageListener listener,
                 @NonNull Handler handler)
                 throws RemoteException {
-            return addInternal(listener, handler);
+            return addInternal(null, listener, handler);
         }
 
         public boolean addListener(@NonNull OnNmeaMessageListener listener,
                 @NonNull Executor executor)
                 throws RemoteException {
-            return addInternal(listener, executor);
+            return addInternal(null, listener, executor);
         }
 
         @Override
@@ -2833,7 +2873,7 @@
         }
 
         @Override
-        protected boolean registerService() throws RemoteException {
+        protected boolean registerService(Void ignored) throws RemoteException {
             Preconditions.checkState(mListenerTransport == null);
 
             GnssStatusListener transport = new GnssStatusListener();
@@ -2893,17 +2933,17 @@
     }
 
     private class GnssMeasurementsListenerManager extends
-            AbstractListenerManager<GnssMeasurementsEvent.Callback> {
+            AbstractListenerManager<GnssRequest, GnssMeasurementsEvent.Callback> {
 
         @Nullable
         private IGnssMeasurementsListener mListenerTransport;
 
         @Override
-        protected boolean registerService() throws RemoteException {
+        protected boolean registerService(GnssRequest request) throws RemoteException {
             Preconditions.checkState(mListenerTransport == null);
 
             GnssMeasurementsListener transport = new GnssMeasurementsListener();
-            if (mService.addGnssMeasurementsListener(transport, mContext.getPackageName(),
+            if (mService.addGnssMeasurementsListener(request, transport, mContext.getPackageName(),
                     mContext.getFeatureId(), "gnss measurement callback")) {
                 mListenerTransport = transport;
                 return true;
@@ -2920,6 +2960,18 @@
             mListenerTransport = null;
         }
 
+        @Override
+        @Nullable
+        protected GnssRequest merge(@NonNull GnssRequest[] requests) {
+            Preconditions.checkArgument(requests.length > 0);
+            for (GnssRequest request : requests) {
+                if (request.isFullTracking()) {
+                    return request;
+                }
+            }
+            return requests[0];
+        }
+
         private class GnssMeasurementsListener extends IGnssMeasurementsListener.Stub {
             @Override
             public void onGnssMeasurementsReceived(final GnssMeasurementsEvent event) {
@@ -2934,13 +2986,13 @@
     }
 
     private class GnssNavigationMessageListenerManager extends
-            AbstractListenerManager<GnssNavigationMessage.Callback> {
+            AbstractListenerManager<Void, GnssNavigationMessage.Callback> {
 
         @Nullable
         private IGnssNavigationMessageListener mListenerTransport;
 
         @Override
-        protected boolean registerService() throws RemoteException {
+        protected boolean registerService(Void ignored) throws RemoteException {
             Preconditions.checkState(mListenerTransport == null);
 
             GnssNavigationMessageListener transport = new GnssNavigationMessageListener();
@@ -2974,19 +3026,61 @@
         }
     }
 
+    private class GnssAntennaInfoListenerManager extends
+            AbstractListenerManager<Void, GnssAntennaInfo.Callback> {
+
+        @Nullable
+        private IGnssAntennaInfoListener mListenerTransport;
+
+        @Override
+        protected boolean registerService(Void ignored) throws RemoteException {
+            Preconditions.checkState(mListenerTransport == null);
+
+            GnssAntennaInfoListener transport = new GnssAntennaInfoListener();
+            if (mService.addGnssAntennaInfoListener(transport, mContext.getPackageName(),
+                    mContext.getFeatureId(), "gnss antenna info callback")) {
+                mListenerTransport = transport;
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        protected void unregisterService() throws RemoteException {
+            Preconditions.checkState(mListenerTransport != null);
+
+            mService.removeGnssAntennaInfoListener(mListenerTransport);
+            mListenerTransport = null;
+        }
+
+        private class GnssAntennaInfoListener extends IGnssAntennaInfoListener.Stub {
+            @Override
+            public void onGnssAntennaInfoReceived(final List<GnssAntennaInfo> gnssAntennaInfos) {
+                execute((callback) -> callback.onGnssAntennaInfoReceived(gnssAntennaInfos));
+            }
+
+            @Override
+            public void onStatusChanged(int status) {
+                execute((listener) -> listener.onStatusChanged(status));
+            }
+        }
+
+    }
+
     private class BatchedLocationCallbackManager extends
-            AbstractListenerManager<BatchedLocationCallback> {
+            AbstractListenerManager<Void, BatchedLocationCallback> {
 
         @Nullable
         private IBatchedLocationCallback mListenerTransport;
 
         @Override
-        protected boolean registerService() throws RemoteException {
+        protected boolean registerService(Void ignored) throws RemoteException {
             Preconditions.checkState(mListenerTransport == null);
 
             BatchedLocationCallback transport = new BatchedLocationCallback();
             if (mService.addGnssBatchingCallback(transport, mContext.getPackageName(),
-                     mContext.getFeatureId(), "batched location callback")) {
+                    mContext.getFeatureId(), "batched location callback")) {
                 mListenerTransport = transport;
                 return true;
             } else {
diff --git a/location/java/android/location/LocationManagerInternal.java b/location/java/android/location/LocationManagerInternal.java
index 69162ba..085602c 100644
--- a/location/java/android/location/LocationManagerInternal.java
+++ b/location/java/android/location/LocationManagerInternal.java
@@ -43,6 +43,15 @@
     public abstract void requestSetProviderAllowed(@NonNull String provider, boolean allowed);
 
     /**
+     * Returns true if the given provider is enabled for the given user.
+     *
+     * @param provider A location provider as listed by {@link LocationManager#getAllProviders()}
+     * @param userId   The user id to check
+     * @return True if the provider is enabled, false otherwise
+     */
+    public abstract boolean isProviderEnabledForUser(@NonNull String provider, int userId);
+
+    /**
      * Returns true if the given package belongs to a location provider, and so should be afforded
      * some special privileges.
      *
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index f67d08e..bd29d8a 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -224,6 +224,19 @@
     public void reportLocation(Location location) {
         ILocationProviderManager manager = mManager;
         if (manager != null) {
+            // remove deprecated extras to save on serialization
+            Bundle extras = location.getExtras();
+            if (extras != null && (extras.containsKey("noGPSLocation")
+                    || extras.containsKey("coarseLocation"))) {
+                location = new Location(location);
+                extras = location.getExtras();
+                extras.remove("noGPSLocation");
+                extras.remove("coarseLocation");
+                if (extras.isEmpty()) {
+                    location.setExtras(null);
+                }
+            }
+
             try {
                 manager.onReportLocation(location);
             } catch (RemoteException | RuntimeException e) {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 4a1088b..112bb9c 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -17,6 +17,7 @@
 
 package android.media;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -1649,6 +1650,179 @@
         }
     }
 
+    /**
+     * @hide
+     * Interface to be notified of changes in the preferred audio device set for a given audio
+     * strategy.
+     * @see #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDevice)
+     * @see #removePreferredDeviceForStrategy(AudioProductStrategy)
+     * @see #getPreferredDeviceForStrategy(AudioProductStrategy)
+     */
+    @SystemApi
+    public interface OnPreferredDeviceForStrategyChangedListener {
+        /**
+         * Called on the listener to indicate that the preferred audio device for the given
+         * strategy has changed.
+         * @param strategy the {@link AudioProductStrategy} whose preferred device changed
+         * @param device <code>null</code> if the preferred device was removed, or the newly set
+         *              preferred audio device
+         */
+        void onPreferredDeviceForStrategyChanged(@NonNull AudioProductStrategy strategy,
+                @Nullable AudioDevice device);
+    }
+
+    /**
+     * @hide
+     * Adds a listener for being notified of changes to the strategy-preferred audio device.
+     * @param executor
+     * @param listener
+     * @throws SecurityException if the caller doesn't hold the required permission
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public void addOnPreferredDeviceForStrategyChangedListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnPreferredDeviceForStrategyChangedListener listener)
+            throws SecurityException {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(listener);
+        synchronized (mPrefDevListenerLock) {
+            if (hasPrefDevListener(listener)) {
+                throw new IllegalArgumentException(
+                        "attempt to call addOnPreferredDeviceForStrategyChangedListener() "
+                                + "on a previously registered listener");
+            }
+            // lazy initialization of the list of strategy-preferred device listener
+            if (mPrefDevListeners == null) {
+                mPrefDevListeners = new ArrayList<>();
+            }
+            final int oldCbCount = mPrefDevListeners.size();
+            mPrefDevListeners.add(new PrefDevListenerInfo(listener, executor));
+            if (oldCbCount == 0 && mPrefDevListeners.size() > 0) {
+                // register binder for callbacks
+                if (mPrefDevDispatcherStub == null) {
+                    mPrefDevDispatcherStub = new StrategyPreferredDeviceDispatcherStub();
+                }
+                try {
+                    getService().registerStrategyPreferredDeviceDispatcher(mPrefDevDispatcherStub);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+        }
+    }
+
+    /**
+     * @hide
+     * Removes a previously added listener of changes to the strategy-preferred audio device.
+     * @param listener
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public void removeOnPreferredDeviceForStrategyChangedListener(
+            @NonNull OnPreferredDeviceForStrategyChangedListener listener) {
+        Objects.requireNonNull(listener);
+        synchronized (mPrefDevListenerLock) {
+            if (!removePrefDevListener(listener)) {
+                throw new IllegalArgumentException(
+                        "attempt to call removeOnPreferredDeviceForStrategyChangedListener() "
+                                + "on an unregistered listener");
+            }
+            if (mPrefDevListeners.size() == 0) {
+                // unregister binder for callbacks
+                try {
+                    getService().unregisterStrategyPreferredDeviceDispatcher(
+                            mPrefDevDispatcherStub);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                } finally {
+                    mPrefDevDispatcherStub = null;
+                    mPrefDevListeners = null;
+                }
+            }
+        }
+    }
+
+
+    private final Object mPrefDevListenerLock = new Object();
+    /**
+     * List of listeners for preferred device for strategy and their associated Executor.
+     * List is lazy-initialized on first registration
+     */
+    @GuardedBy("mPrefDevListenerLock")
+    private @Nullable ArrayList<PrefDevListenerInfo> mPrefDevListeners;
+
+    private static class PrefDevListenerInfo {
+        final @NonNull OnPreferredDeviceForStrategyChangedListener mListener;
+        final @NonNull Executor mExecutor;
+        PrefDevListenerInfo(OnPreferredDeviceForStrategyChangedListener listener, Executor exe) {
+            mListener = listener;
+            mExecutor = exe;
+        }
+    }
+
+    @GuardedBy("mPrefDevListenerLock")
+    private StrategyPreferredDeviceDispatcherStub mPrefDevDispatcherStub;
+
+    private final class StrategyPreferredDeviceDispatcherStub
+            extends IStrategyPreferredDeviceDispatcher.Stub {
+
+        @Override
+        public void dispatchPrefDeviceChanged(int strategyId, @Nullable AudioDevice device) {
+            // make a shallow copy of listeners so callback is not executed under lock
+            final ArrayList<PrefDevListenerInfo> prefDevListeners;
+            synchronized (mPrefDevListenerLock) {
+                if (mPrefDevListeners == null || mPrefDevListeners.size() == 0) {
+                    return;
+                }
+                prefDevListeners = (ArrayList<PrefDevListenerInfo>) mPrefDevListeners.clone();
+            }
+            final AudioProductStrategy strategy =
+                    AudioProductStrategy.getAudioProductStrategyWithId(strategyId);
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                for (PrefDevListenerInfo info : prefDevListeners) {
+                    info.mExecutor.execute(() ->
+                            info.mListener.onPreferredDeviceForStrategyChanged(strategy, device));
+                }
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @GuardedBy("mPrefDevListenerLock")
+    private @Nullable PrefDevListenerInfo getPrefDevListenerInfo(
+            OnPreferredDeviceForStrategyChangedListener listener) {
+        if (mPrefDevListeners == null) {
+            return null;
+        }
+        for (PrefDevListenerInfo info : mPrefDevListeners) {
+            if (info.mListener == listener) {
+                return info;
+            }
+        }
+        return null;
+    }
+
+    @GuardedBy("mPrefDevListenerLock")
+    private boolean hasPrefDevListener(OnPreferredDeviceForStrategyChangedListener listener) {
+        return getPrefDevListenerInfo(listener) != null;
+    }
+
+    @GuardedBy("mPrefDevListenerLock")
+    /**
+     * @return true if the listener was removed from the list
+     */
+    private boolean removePrefDevListener(OnPreferredDeviceForStrategyChangedListener listener) {
+        final PrefDevListenerInfo infoToRemove = getPrefDevListenerInfo(listener);
+        if (infoToRemove != null) {
+            mPrefDevListeners.remove(infoToRemove);
+            return true;
+        }
+        return false;
+    }
+
     //====================================================================
     // Offload query
     /**
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 0fbc0d2..27bf3fe 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -29,6 +29,7 @@
 import android.media.IPlaybackConfigDispatcher;
 import android.media.IRecordingConfigDispatcher;
 import android.media.IRingtonePlayer;
+import android.media.IStrategyPreferredDeviceDispatcher;
 import android.media.IVolumeController;
 import android.media.IVolumeController;
 import android.media.PlayerBase;
@@ -286,6 +287,11 @@
 
     int getAllowedCapturePolicy();
 
+    void registerStrategyPreferredDeviceDispatcher(IStrategyPreferredDeviceDispatcher dispatcher);
+
+    oneway void unregisterStrategyPreferredDeviceDispatcher(
+            IStrategyPreferredDeviceDispatcher dispatcher);
+
     // WARNING: read warning at top of file, new methods that need to be used by native
     // code via IAudioManager.h need to be added to the top section.
 }
diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl
index 9131f3b..0c64564 100644
--- a/media/java/android/media/IMediaRoute2Provider.aidl
+++ b/media/java/android/media/IMediaRoute2Provider.aidl
@@ -36,5 +36,6 @@
     void transferToRoute(String sessionId, String routeId);
 
     void notifyControlRequestSent(String id, in Intent request);
-    void requestSetVolume(String id, int volume);
+    void setRouteVolume(String routeId, int volume);
+    void setSessionVolume(String sessionId, int volume);
 }
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 6fef468..f919dce 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -51,7 +51,8 @@
     void unregisterClient2(IMediaRouter2Client client);
     void sendControlRequest(IMediaRouter2Client client, in MediaRoute2Info route,
             in Intent request);
-    void requestSetVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int volume);
+    void setRouteVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int volume);
+    void setSessionVolume2(IMediaRouter2Client client, String sessionId, int volume);
 
     void requestCreateSession(IMediaRouter2Client client, in MediaRoute2Info route, int requestId,
             in @nullable Bundle sessionHints);
@@ -67,8 +68,10 @@
     void requestCreateClientSession(IMediaRouter2Manager manager, String packageName,
         in @nullable MediaRoute2Info route, int requestId);
 
-    void requestSetVolume2Manager(IMediaRouter2Manager manager,
+    void setRouteVolume2Manager(IMediaRouter2Manager manager,
             in MediaRoute2Info route, int volume);
+    void setSessionVolume2Manager(IMediaRouter2Manager manager,
+            String sessionId, int volume);
 
     List<RoutingSessionInfo> getActiveSessions(IMediaRouter2Manager manager);
     void selectClientRoute(IMediaRouter2Manager manager,
diff --git a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl b/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
new file mode 100644
index 0000000..6db9e52
--- /dev/null
+++ b/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioDevice;
+
+/**
+ * AIDL for AudioService to signal audio strategy-preferred device updates.
+ *
+ * {@hide}
+ */
+oneway interface IStrategyPreferredDeviceDispatcher {
+
+    void dispatchPrefDeviceChanged(int strategyId, in AudioDevice device);
+
+}
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 20a59bba5..aac195d 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -122,16 +122,25 @@
      * @hide
      */
     //TODO: Discuss what to use for request (e.g., Intent? Request class?)
-    public abstract void onControlRequest(@NonNull String routeId, @NonNull Intent request);
+    public void onControlRequest(@NonNull String routeId, @NonNull Intent request) {}
 
     /**
-     * Called when requestSetVolume is called on a route of the provider.
+     * Called when a volume setting is requested on a route of the provider
      *
      * @param routeId the id of the route
      * @param volume the target volume
      * @see MediaRoute2Info#getVolumeMax()
      */
-    public abstract void onSetVolume(@NonNull String routeId, int volume);
+    public abstract void onSetRouteVolume(@NonNull String routeId, int volume);
+
+    /**
+     * Called when {@link MediaRouter2.RoutingController#setVolume(int)} is called on
+     * a routing session of the provider
+     *
+     * @param sessionId the id of the routing session
+     * @param volume the target volume
+     */
+    public abstract void onSetSessionVolume(@NonNull String sessionId, int volume);
 
     /**
      * Gets information of the session with the given id.
@@ -513,12 +522,21 @@
         }
 
         @Override
-        public void requestSetVolume(String routeId, int volume) {
+        public void setRouteVolume(String routeId, int volume) {
             if (!checkCallerisSystem()) {
                 return;
             }
-            mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetVolume,
+            mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetRouteVolume,
                     MediaRoute2ProviderService.this, routeId, volume));
         }
+
+        @Override
+        public void setSessionVolume(String sessionId, int volume) {
+            if (!checkCallerisSystem()) {
+                return;
+            }
+            mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetSessionVolume,
+                    MediaRoute2ProviderService.this, sessionId, volume));
+        }
     }
 }
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 6418610..0e6ade5 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -446,7 +446,7 @@
      * @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}.
      * @hide
      */
-    public void requestSetVolume(@NonNull MediaRoute2Info route, int volume) {
+    public void setRouteVolume(@NonNull MediaRoute2Info route, int volume) {
         Objects.requireNonNull(route, "route must not be null");
 
         Client2 client;
@@ -455,7 +455,7 @@
         }
         if (client != null) {
             try {
-                mMediaRouterService.requestSetVolume2(client, route, volume);
+                mMediaRouterService.setRouteVolume2(client, route, volume);
             } catch (RemoteException ex) {
                 Log.e(TAG, "Unable to send control request.", ex);
             }
@@ -885,6 +885,43 @@
         }
 
         /**
+         * Gets information about how volume is handled on the session.
+         *
+         * @return {@link MediaRoute2Info#PLAYBACK_VOLUME_FIXED} or
+         * {@link MediaRoute2Info#PLAYBACK_VOLUME_VARIABLE}
+         */
+        @MediaRoute2Info.PlaybackVolume
+        public int getVolumeHandling() {
+            synchronized (mControllerLock) {
+                return mSessionInfo.getVolumeHandling();
+            }
+        }
+
+        /**
+         * Gets the maximum volume of the session.
+         */
+        public int getVolumeMax() {
+            synchronized (mControllerLock) {
+                return mSessionInfo.getVolumeMax();
+            }
+        }
+
+        /**
+         * Gets the current volume of the session.
+         * <p>
+         * When it's available, it represents the volume of routing session, which is a group
+         * of selected routes. To get the volume of a route,
+         * use {@link MediaRoute2Info#getVolume()}.
+         * </p>
+         * @see MediaRoute2Info#getVolume()
+         */
+        public int getVolume() {
+            synchronized (mControllerLock) {
+                return mSessionInfo.getVolume();
+            }
+        }
+
+        /**
          * Returns true if this controller is released, false otherwise.
          * If it is released, then all other getters from this instance may return invalid values.
          * Also, any operations to this instance will be ignored once released.
@@ -1040,6 +1077,42 @@
         }
 
         /**
+         * Requests a volume change for the remote session asynchronously.
+         *
+         * @param volume The new volume value between 0 and {@link RoutingController#getVolumeMax}
+         *               (inclusive).
+         * @see #getVolume()
+         */
+        public void setVolume(int volume) {
+            if (getVolumeHandling() == MediaRoute2Info.PLAYBACK_VOLUME_FIXED) {
+                Log.w(TAG, "setVolume: the routing session has fixed volume. Ignoring.");
+                return;
+            }
+            if (volume < 0 || volume > getVolumeMax()) {
+                Log.w(TAG, "setVolume: the target volume is out of range. Ignoring");
+                return;
+            }
+
+            synchronized (mControllerLock) {
+                if (mIsReleased) {
+                    Log.w(TAG, "setVolume is called on released controller. Ignoring.");
+                    return;
+                }
+            }
+            Client2 client;
+            synchronized (sRouterLock) {
+                client = mClient;
+            }
+            if (client != null) {
+                try {
+                    mMediaRouterService.setSessionVolume2(client, getId(), volume);
+                } catch (RemoteException ex) {
+                    Log.e(TAG, "setVolume: Failed to deliver request.", ex);
+                }
+            }
+        }
+
+        /**
          * Release this controller and corresponding session.
          * Any operations on this controller after calling this method will be ignored.
          * The devices that are playing media will stop playing it.
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 2c1fdab..5ce291c 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -22,6 +22,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.media.session.MediaController;
+import android.media.session.MediaSessionManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.RemoteException;
@@ -53,6 +55,8 @@
     @GuardedBy("sLock")
     private static MediaRouter2Manager sInstance;
 
+    private final MediaSessionManager mMediaSessionManager;
+
     final String mPackageName;
 
     private Context mContext;
@@ -89,6 +93,8 @@
         mContext = context.getApplicationContext();
         mMediaRouterService = IMediaRouterService.Stub.asInterface(
                 ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE));
+        mMediaSessionManager = (MediaSessionManager) context
+                .getSystemService(Context.MEDIA_SESSION_SERVICE);
         mPackageName = mContext.getPackageName();
         mHandler = new Handler(context.getMainLooper());
     }
@@ -150,6 +156,23 @@
         }
     }
 
+    /**
+     * Gets a {@link android.media.session.MediaController} associated with the
+     * given routing session.
+     * If there is no matching media session, {@code null} is returned.
+     */
+    @Nullable
+    public MediaController getMediaControllerForRoutingSession(
+            @NonNull RoutingSessionInfo sessionInfo) {
+        for (MediaController controller : mMediaSessionManager.getActiveSessions(null)) {
+            String volumeControlId = controller.getPlaybackInfo().getVolumeControlId();
+            if (TextUtils.equals(sessionInfo.getId(), volumeControlId)) {
+                return controller;
+            }
+        }
+        return null;
+    }
+
     //TODO: Use cache not to create array. For now, it's unclear when to purge the cache.
     //Do this when we finalize how to set control categories.
     /**
@@ -178,24 +201,24 @@
     }
 
     /**
-     * Gets routing controllers of an application with the given package name.
+     * Gets routing sessions of an application with the given package name.
      * The first element of the returned list is the system routing controller.
      *
      * @see MediaRouter2#getSystemController()
      */
     @NonNull
-    public List<RoutingController> getRoutingControllers(@NonNull String packageName) {
+    public List<RoutingSessionInfo> getRoutingSessions(@NonNull String packageName) {
         Objects.requireNonNull(packageName, "packageName must not be null");
 
-        List<RoutingController> controllers = new ArrayList<>();
+        List<RoutingSessionInfo> sessions = new ArrayList<>();
 
         for (RoutingSessionInfo sessionInfo : getActiveSessions()) {
             if (sessionInfo.isSystemSession()
                     || TextUtils.equals(sessionInfo.getClientPackageName(), packageName)) {
-                controllers.add(new RoutingController(sessionInfo));
+                sessions.add(sessionInfo);
             }
         }
-        return controllers;
+        return sessions;
     }
 
     /**
@@ -250,13 +273,12 @@
         boolean transferred = false;
         //TODO: instead of release all controllers, add an API to specify controllers that
         // should be released (or is the system controller).
-        for (RoutingController controller : getRoutingControllers(packageName)) {
-            if (!transferred && controller.getSessionInfo().getTransferrableRoutes()
-                    .contains(route.getId())) {
-                controller.transferToRoute(route);
+        for (RoutingSessionInfo sessionInfo : getRoutingSessions(packageName)) {
+            if (!transferred && sessionInfo.getTransferrableRoutes().contains(route.getId())) {
+                new RoutingController(sessionInfo).transferToRoute(route);
                 transferred = true;
-            } else if (!controller.getSessionInfo().isSystemSession()) {
-                controller.release();
+            } else if (!sessionInfo.isSystemSession()) {
+                new RoutingController(sessionInfo).release();
             }
         }
 
@@ -282,22 +304,72 @@
 
     /**
      * Requests a volume change for a route asynchronously.
+     */
+    //TODO: remove this.
+    public void requestSetVolume(MediaRoute2Info route, int volume) {
+        setRouteVolume(route, volume);
+    }
+
+    /**
+     * Requests a volume change for a route asynchronously.
      * <p>
      * It may have no effect if the route is currently not selected.
      * </p>
      *
-     * @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}.
+     * @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}
+     *               (inclusive).
      */
-    public void requestSetVolume(@NonNull MediaRoute2Info route, int volume) {
+    public void setRouteVolume(@NonNull MediaRoute2Info route, int volume) {
         Objects.requireNonNull(route, "route must not be null");
 
+        if (route.getVolumeHandling() == MediaRoute2Info.PLAYBACK_VOLUME_FIXED) {
+            Log.w(TAG, "setRouteVolume: the route has fixed volume. Ignoring.");
+            return;
+        }
+        if (volume < 0 || volume > route.getVolumeMax()) {
+            Log.w(TAG, "setRouteVolume: the target volume is out of range. Ignoring");
+            return;
+        }
+
         Client client;
         synchronized (sLock) {
             client = mClient;
         }
         if (client != null) {
             try {
-                mMediaRouterService.requestSetVolume2Manager(client, route, volume);
+                mMediaRouterService.setRouteVolume2Manager(client, route, volume);
+            } catch (RemoteException ex) {
+                Log.e(TAG, "Unable to send control request.", ex);
+            }
+        }
+    }
+
+    /**
+     * Requests a volume change for a routing session asynchronously.
+     *
+     * @param volume The new volume value between 0 and {@link RoutingSessionInfo#getVolumeMax}
+     *               (inclusive).
+     */
+    public void setSessionVolume(@NonNull RoutingSessionInfo sessionInfo, int volume) {
+        Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
+
+        if (sessionInfo.getVolumeHandling() == MediaRoute2Info.PLAYBACK_VOLUME_FIXED) {
+            Log.w(TAG, "setSessionVolume: the route has fixed volume. Ignoring.");
+            return;
+        }
+        if (volume < 0 || volume > sessionInfo.getVolumeMax()) {
+            Log.w(TAG, "setSessionVolume: the target volume is out of range. Ignoring");
+            return;
+        }
+
+        Client client;
+        synchronized (sLock) {
+            client = mClient;
+        }
+        if (client != null) {
+            try {
+                mMediaRouterService.setSessionVolume2Manager(
+                        client, sessionInfo.getId(), volume);
             } catch (RemoteException ex) {
                 Log.e(TAG, "Unable to send control request.", ex);
             }
diff --git a/media/java/android/media/RoutingSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java
index 5383ea2..0d4e666 100644
--- a/media/java/android/media/RoutingSessionInfo.java
+++ b/media/java/android/media/RoutingSessionInfo.java
@@ -56,6 +56,11 @@
     final List<String> mSelectableRoutes;
     final List<String> mDeselectableRoutes;
     final List<String> mTransferrableRoutes;
+
+    final int mVolumeHandling;
+    final int mVolumeMax;
+    final int mVolume;
+
     @Nullable
     final Bundle mControlHints;
     final boolean mIsSystemSession;
@@ -77,6 +82,10 @@
         mTransferrableRoutes = Collections.unmodifiableList(
                 convertToUniqueRouteIds(builder.mTransferrableRoutes));
 
+        mVolumeHandling = builder.mVolumeHandling;
+        mVolumeMax = builder.mVolumeMax;
+        mVolume = builder.mVolume;
+
         mControlHints = builder.mControlHints;
         mIsSystemSession = builder.mIsSystemSession;
     }
@@ -93,6 +102,10 @@
         mDeselectableRoutes = ensureList(src.createStringArrayList());
         mTransferrableRoutes = ensureList(src.createStringArrayList());
 
+        mVolumeHandling = src.readInt();
+        mVolumeMax = src.readInt();
+        mVolume = src.readInt();
+
         mControlHints = src.readBundle();
         mIsSystemSession = src.readBoolean();
     }
@@ -188,6 +201,36 @@
     }
 
     /**
+     * Gets information about how volume is handled on the session.
+     *
+     * @return {@link MediaRoute2Info#PLAYBACK_VOLUME_FIXED} or
+     * {@link MediaRoute2Info#PLAYBACK_VOLUME_VARIABLE}.
+     */
+    @MediaRoute2Info.PlaybackVolume
+    public int getVolumeHandling() {
+        return mVolumeHandling;
+    }
+
+    /**
+     * Gets the maximum volume of the session.
+     */
+    public int getVolumeMax() {
+        return mVolumeMax;
+    }
+
+    /**
+     * Gets the current volume of the session.
+     * <p>
+     * When it's available, it represents the volume of routing session, which is a group
+     * of selected routes. To get the volume of each route, use {@link MediaRoute2Info#getVolume()}.
+     * </p>
+     * @see MediaRoute2Info#getVolume()
+     */
+    public int getVolume() {
+        return mVolume;
+    }
+
+    /**
      * Gets the control hints
      */
     @Nullable
@@ -218,6 +261,9 @@
         dest.writeStringList(mSelectableRoutes);
         dest.writeStringList(mDeselectableRoutes);
         dest.writeStringList(mTransferrableRoutes);
+        dest.writeInt(mVolumeHandling);
+        dest.writeInt(mVolumeMax);
+        dest.writeInt(mVolume);
         dest.writeBundle(mControlHints);
         dest.writeBoolean(mIsSystemSession);
     }
@@ -238,13 +284,17 @@
                 && Objects.equals(mSelectedRoutes, other.mSelectedRoutes)
                 && Objects.equals(mSelectableRoutes, other.mSelectableRoutes)
                 && Objects.equals(mDeselectableRoutes, other.mDeselectableRoutes)
-                && Objects.equals(mTransferrableRoutes, other.mTransferrableRoutes);
+                && Objects.equals(mTransferrableRoutes, other.mTransferrableRoutes)
+                && (mVolumeHandling == other.mVolumeHandling)
+                && (mVolumeMax == other.mVolumeMax)
+                && (mVolume == other.mVolume);
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mId, mClientPackageName, mProviderId,
-                mSelectedRoutes, mSelectableRoutes, mDeselectableRoutes, mTransferrableRoutes);
+                mSelectedRoutes, mSelectableRoutes, mDeselectableRoutes, mTransferrableRoutes,
+                mVolumeMax, mVolumeHandling, mVolume);
     }
 
     @Override
@@ -264,6 +314,9 @@
                 .append(", transferrableRoutes={")
                 .append(String.join(",", mTransferrableRoutes))
                 .append("}")
+                .append(", volumeHandling=").append(getVolumeHandling())
+                .append(", volumeMax=").append(getVolumeMax())
+                .append(", volume=").append(getVolume())
                 .append(" }");
         return result.toString();
     }
@@ -298,6 +351,9 @@
         final List<String> mSelectableRoutes;
         final List<String> mDeselectableRoutes;
         final List<String> mTransferrableRoutes;
+        int mVolumeHandling = MediaRoute2Info.PLAYBACK_VOLUME_FIXED;
+        int mVolumeMax;
+        int mVolume;
         Bundle mControlHints;
         boolean mIsSystemSession;
 
@@ -346,6 +402,10 @@
             mDeselectableRoutes = new ArrayList<>(sessionInfo.mDeselectableRoutes);
             mTransferrableRoutes = new ArrayList<>(sessionInfo.mTransferrableRoutes);
 
+            mVolumeHandling = sessionInfo.mVolumeHandling;
+            mVolumeMax = sessionInfo.mVolumeMax;
+            mVolume = sessionInfo.mVolume;
+
             mControlHints = sessionInfo.mControlHints;
             mIsSystemSession = sessionInfo.mIsSystemSession;
         }
@@ -497,6 +557,36 @@
         }
 
         /**
+         * Sets the session's volume handling.
+         * {@link MediaRoute2Info#PLAYBACK_VOLUME_FIXED} or
+         * {@link MediaRoute2Info#PLAYBACK_VOLUME_VARIABLE}.
+         */
+        @NonNull
+        public RoutingSessionInfo.Builder setVolumeHandling(
+                @MediaRoute2Info.PlaybackVolume int volumeHandling) {
+            mVolumeHandling = volumeHandling;
+            return this;
+        }
+
+        /**
+         * Sets the session's maximum volume, or 0 if unknown.
+         */
+        @NonNull
+        public RoutingSessionInfo.Builder setVolumeMax(int volumeMax) {
+            mVolumeMax = volumeMax;
+            return this;
+        }
+
+        /**
+         * Sets the session's current volume, or 0 if unknown.
+         */
+        @NonNull
+        public RoutingSessionInfo.Builder setVolume(int volume) {
+            mVolume = volume;
+            return this;
+        }
+
+        /**
          * Sets control hints.
          */
         @NonNull
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index dd9877a..61113bc 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -192,6 +192,16 @@
         return mRule.isAffectingUsage(usage);
     }
 
+    /**
+      * Returns {@code true} if the rule associated with this mix contains a
+      * RULE_MATCH_ATTRIBUTE_USAGE criterion for the given usage
+      *
+      * @hide
+      */
+    public boolean containsMatchAttributeRuleForUsage(int usage) {
+        return mRule.containsMatchAttributeRuleForUsage(usage);
+    }
+
     /** @hide */
     public boolean isRoutedToDevice(int deviceType, @NonNull String deviceAddress) {
         if ((mRouteFlags & ROUTE_FLAG_RENDER) != ROUTE_FLAG_RENDER) {
diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java
index bca3fa7..68c9593 100644
--- a/media/java/android/media/audiopolicy/AudioMixingRule.java
+++ b/media/java/android/media/audiopolicy/AudioMixingRule.java
@@ -47,10 +47,12 @@
 public class AudioMixingRule {
 
     private AudioMixingRule(int mixType, ArrayList<AudioMixMatchCriterion> criteria,
-                            boolean allowPrivilegedPlaybackCapture) {
+                            boolean allowPrivilegedPlaybackCapture,
+                            boolean voiceCommunicationCaptureAllowed) {
         mCriteria = criteria;
         mTargetMixType = mixType;
         mAllowPrivilegedPlaybackCapture = allowPrivilegedPlaybackCapture;
+        mVoiceCommunicationCaptureAllowed = voiceCommunicationCaptureAllowed;
     }
 
     /**
@@ -171,6 +173,23 @@
         return false;
     }
 
+    /**
+      * Returns {@code true} if this rule contains a RULE_MATCH_ATTRIBUTE_USAGE criterion for
+      * the given usage
+      *
+      * @hide
+      */
+    boolean containsMatchAttributeRuleForUsage(int usage) {
+        for (AudioMixMatchCriterion criterion : mCriteria) {
+            if (criterion.mRule == RULE_MATCH_ATTRIBUTE_USAGE
+                    && criterion.mAttr != null
+                    && criterion.mAttr.getUsage() == usage) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private static boolean areCriteriaEquivalent(ArrayList<AudioMixMatchCriterion> cr1,
             ArrayList<AudioMixMatchCriterion> cr2) {
         if (cr1 == null || cr2 == null) return false;
@@ -188,6 +207,8 @@
     public ArrayList<AudioMixMatchCriterion> getCriteria() { return mCriteria; }
     @UnsupportedAppUsage
     private boolean mAllowPrivilegedPlaybackCapture = false;
+    @UnsupportedAppUsage
+    private boolean mVoiceCommunicationCaptureAllowed = false;
 
     /** @hide */
     public boolean allowPrivilegedPlaybackCapture() {
@@ -195,6 +216,16 @@
     }
 
     /** @hide */
+    public boolean voiceCommunicationCaptureAllowed() {
+        return mVoiceCommunicationCaptureAllowed;
+    }
+
+    /** @hide */
+    public void setVoiceCommunicationCaptureAllowed(boolean allowed) {
+        mVoiceCommunicationCaptureAllowed = allowed;
+    }
+
+    /** @hide */
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
@@ -203,12 +234,18 @@
         final AudioMixingRule that = (AudioMixingRule) o;
         return (this.mTargetMixType == that.mTargetMixType)
                 && (areCriteriaEquivalent(this.mCriteria, that.mCriteria)
-                && this.mAllowPrivilegedPlaybackCapture == that.mAllowPrivilegedPlaybackCapture);
+                && this.mAllowPrivilegedPlaybackCapture == that.mAllowPrivilegedPlaybackCapture
+                && this.mVoiceCommunicationCaptureAllowed
+                    == that.mVoiceCommunicationCaptureAllowed);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mTargetMixType, mCriteria, mAllowPrivilegedPlaybackCapture);
+        return Objects.hash(
+            mTargetMixType,
+            mCriteria,
+            mAllowPrivilegedPlaybackCapture,
+            mVoiceCommunicationCaptureAllowed);
     }
 
     private static boolean isValidSystemApiRule(int rule) {
@@ -276,6 +313,8 @@
         private ArrayList<AudioMixMatchCriterion> mCriteria;
         private int mTargetMixType = AudioMix.MIX_TYPE_INVALID;
         private boolean mAllowPrivilegedPlaybackCapture = false;
+        // This value should be set internally according to a permission check
+        private boolean mVoiceCommunicationCaptureAllowed = false;
 
         /**
          * Constructs a new Builder with no rules.
@@ -401,6 +440,23 @@
         }
 
         /**
+         * Set if the caller of the rule is able to capture voice communication output.
+         * A system app can capture voice communication output only if it is granted with the.
+         * CAPTURE_VOICE_COMMUNICATION_OUTPUT permission.
+         *
+         * Note that this method is for internal use only and should not be called by the app that
+         * creates the rule.
+         *
+         * @return the same Builder instance.
+         *
+         * @hide
+         */
+        public @NonNull Builder voiceCommunicationCaptureAllowed(boolean allowed) {
+            mVoiceCommunicationCaptureAllowed = allowed;
+            return this;
+        }
+
+        /**
          * Add or exclude a rule for the selection of which streams are mixed together.
          * Does error checking on the parameters.
          * @param rule
@@ -583,7 +639,8 @@
          * @return a new {@link AudioMixingRule} object
          */
         public AudioMixingRule build() {
-            return new AudioMixingRule(mTargetMixType, mCriteria, mAllowPrivilegedPlaybackCapture);
+            return new AudioMixingRule(mTargetMixType, mCriteria,
+                mAllowPrivilegedPlaybackCapture, mVoiceCommunicationCaptureAllowed);
         }
     }
 }
diff --git a/media/java/android/media/audiopolicy/AudioPolicyConfig.java b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
index b048158..91b9bb3 100644
--- a/media/java/android/media/audiopolicy/AudioPolicyConfig.java
+++ b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
@@ -98,6 +98,8 @@
             dest.writeInt(mix.getFormat().getChannelMask());
             // write opt-out respect
             dest.writeBoolean(mix.getRule().allowPrivilegedPlaybackCapture());
+            // write voice communication capture allowed flag
+            dest.writeBoolean(mix.getRule().voiceCommunicationCaptureAllowed());
             // write mix rules
             final ArrayList<AudioMixMatchCriterion> criteria = mix.getRule().getCriteria();
             dest.writeInt(criteria.size());
@@ -128,8 +130,10 @@
             mixBuilder.setFormat(format);
 
             AudioMixingRule.Builder ruleBuilder = new AudioMixingRule.Builder();
-            // write opt-out respect
+            // read opt-out respect
             ruleBuilder.allowPrivilegedPlaybackCapture(in.readBoolean());
+            // read voice capture allowed flag
+            ruleBuilder.voiceCommunicationCaptureAllowed(in.readBoolean());
             // read mix rules
             int nbRules = in.readInt();
             for (int j = 0 ; j < nbRules ; j++) {
@@ -169,6 +173,8 @@
             textDump += Integer.toHexString(mix.getFormat().getChannelMask()).toUpperCase() + "\n";
             textDump += "  ignore playback capture opt out="
                     + mix.getRule().allowPrivilegedPlaybackCapture() + "\n";
+            textDump += "  allow voice communication capture="
+                    + mix.getRule().voiceCommunicationCaptureAllowed() + "\n";
             // write mix rules
             final ArrayList<AudioMixMatchCriterion> criteria = mix.getRule().getCriteria();
             for (AudioMixMatchCriterion criterion : criteria) {
diff --git a/media/java/android/media/audiopolicy/AudioProductStrategy.java b/media/java/android/media/audiopolicy/AudioProductStrategy.java
index 60b3fc6..f9dbc50 100644
--- a/media/java/android/media/audiopolicy/AudioProductStrategy.java
+++ b/media/java/android/media/audiopolicy/AudioProductStrategy.java
@@ -83,6 +83,27 @@
 
     /**
      * @hide
+     * Return the AudioProductStrategy object for the given strategy ID.
+     * @param id the ID of the strategy to find
+     * @return an AudioProductStrategy on which getId() would return id, null if no such strategy
+     *     exists.
+     */
+    public static @Nullable AudioProductStrategy getAudioProductStrategyWithId(int id) {
+        synchronized (sLock) {
+            if (sAudioProductStrategies == null) {
+                sAudioProductStrategies = initializeAudioProductStrategies();
+            }
+            for (AudioProductStrategy strategy : sAudioProductStrategies) {
+                if (strategy.getId() == id) {
+                    return strategy;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * @hide
      * Create an invalid AudioProductStrategy instance for testing
      * @param id the ID for the invalid strategy, always use a different one than in use
      * @return an invalid instance that cannot successfully be used for volume groups or routing
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 3561f83..9b183a3 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -244,7 +244,7 @@
      *
      * <p>
      * Tuner events are started when {@link #tune(FrontendSettings)} is called and end when {@link
-     * #stopTune()} is called.
+     * #cancelTuning()} is called.
      *
      * @param eventListener receives tune events.
      * @throws SecurityException if the caller does not have appropriate permissions.
@@ -309,7 +309,7 @@
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
     @Result
-    public int stopTune() {
+    public int cancelTuning() {
         TunerUtils.checkTunerPermission(mContext);
         return nativeStopTune();
     }
@@ -322,8 +322,8 @@
      * @param settings A {@link FrontendSettings} to configure the frontend.
      * @param scanType The scan type.
      * @throws SecurityException     if the caller does not have appropriate permissions.
-     * @throws IllegalStateException if {@code scan} is called again before {@link #stopScan()} is
-     *                               called.
+     * @throws IllegalStateException if {@code scan} is called again before
+     *                               {@link #cancelScanning()} is called.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
     @Result
@@ -354,7 +354,7 @@
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
     @Result
-    public int stopScan() {
+    public int cancelScanning() {
         TunerUtils.checkTunerPermission(mContext);
         int retVal = nativeStopScan();
         mScanCallback = null;
diff --git a/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java
index f186de6..6a8b6da 100644
--- a/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java
@@ -17,7 +17,6 @@
 package android.media.tv.tuner.filter;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.content.Context;
@@ -43,13 +42,6 @@
     }
 
     /**
-     * Gets the {@link Settings} object of this filter configuration.
-     */
-    @Nullable
-    public Settings getSettings() {
-        return mSettings;
-    }
-    /**
      * Gets Tag Protocol ID.
      */
     public int getTpid() {
@@ -71,25 +63,13 @@
     /**
      * Builder for {@link TsFilterConfiguration}.
      */
-    public static class Builder {
-        private Settings mSettings;
+    public static class Builder extends FilterConfiguration.Builder<Builder> {
         private int mTpid;
 
         private Builder() {
         }
 
         /**
-         * Sets filter settings.
-         *
-         * @param settings the filter settings.
-         */
-        @NonNull
-        public Builder setSettings(@NonNull Settings settings) {
-            mSettings = settings;
-            return this;
-        }
-
-        /**
          * Sets Tag Protocol ID.
          *
          * @param tpid the Tag Protocol ID.
@@ -107,5 +87,10 @@
         public TsFilterConfiguration build() {
             return new TsFilterConfiguration(mSettings, mTpid);
         }
+
+        @Override
+        Builder self() {
+            return this;
+        }
     }
 }
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index f10e5eb..3ffb951 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -44,6 +44,7 @@
 import android.media.MediaRouter2.RouteCallback;
 import android.media.MediaRouter2.RoutingControllerCallback;
 import android.media.MediaRouter2Manager;
+import android.media.MediaRouter2Utils;
 import android.media.RouteDiscoveryPreference;
 import android.media.RoutingSessionInfo;
 import android.support.test.InstrumentationRegistry;
@@ -57,6 +58,7 @@
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -225,22 +227,22 @@
             }
         });
 
-        assertEquals(1, mManager.getRoutingControllers(mPackageName).size());
+        assertEquals(1, mManager.getRoutingSessions(mPackageName).size());
 
         mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1));
         latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
 
-        List<MediaRouter2Manager.RoutingController> controllers =
-                mManager.getRoutingControllers(mPackageName);
+        List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName);
 
-        assertEquals(2, controllers.size());
+        assertEquals(2, sessions.size());
 
-        MediaRouter2Manager.RoutingController routingController = controllers.get(1);
+        MediaRouter2Manager.RoutingController routingController =
+                mManager.getControllerForSession(sessions.get(1));
         awaitOnRouteChangedManager(
                 () -> routingController.release(),
                 ROUTE_ID1,
                 route -> TextUtils.equals(route.getClientPackageName(), null));
-        assertEquals(1, mManager.getRoutingControllers(mPackageName).size());
+        assertEquals(1, mManager.getRoutingSessions(mPackageName).size());
     }
 
     /**
@@ -266,11 +268,11 @@
                 route -> TextUtils.equals(route.getClientPackageName(), mPackageName));
         assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
-        List<MediaRouter2Manager.RoutingController> controllers =
-                mManager.getRoutingControllers(mPackageName);
+        List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName);
 
-        assertEquals(2, controllers.size());
-        MediaRouter2Manager.RoutingController routingController = controllers.get(1);
+        assertEquals(2, sessions.size());
+        MediaRouter2Manager.RoutingController routingController =
+                mManager.getControllerForSession(sessions.get(1));
 
         awaitOnRouteChangedManager(
                 () -> mManager.selectRoute(mPackageName, routes.get(ROUTE_ID5_TO_TRANSFER_TO)),
@@ -284,7 +286,33 @@
     }
 
     @Test
-    public void testControlVolumeWithManager() throws Exception {
+    public void testSetSystemRouteVolume() throws Exception {
+        // ensure client
+        addManagerCallback(new MediaRouter2Manager.Callback());
+        String selectedSystemRouteId =
+                MediaRouter2Utils.getOriginalId(
+                mManager.getActiveSessions().get(0).getSelectedRoutes().get(0));
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(Collections.emptyList());
+        MediaRoute2Info volRoute = routes.get(selectedSystemRouteId);
+        assertNotNull(volRoute);
+
+        int originalVolume = volRoute.getVolume();
+        int targetVolume = originalVolume == volRoute.getVolumeMax()
+                ? originalVolume - 1 : originalVolume + 1;
+
+        awaitOnRouteChangedManager(
+                () -> mManager.setRouteVolume(volRoute, targetVolume),
+                selectedSystemRouteId,
+                (route -> route.getVolume() == targetVolume));
+
+        awaitOnRouteChangedManager(
+                () -> mManager.setRouteVolume(volRoute, originalVolume),
+                selectedSystemRouteId,
+                (route -> route.getVolume() == originalVolume));
+    }
+
+    @Test
+    public void testSetRouteVolume() throws Exception {
         Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
         MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
 
@@ -293,17 +321,81 @@
                 ? originalVolume - 1 : originalVolume + 1;
 
         awaitOnRouteChangedManager(
-                () -> mManager.requestSetVolume(volRoute, targetVolume),
+                () -> mManager.setRouteVolume(volRoute, targetVolume),
                 ROUTE_ID_VARIABLE_VOLUME,
                 (route -> route.getVolume() == targetVolume));
 
         awaitOnRouteChangedManager(
-                () -> mManager.requestSetVolume(volRoute, originalVolume),
+                () -> mManager.setRouteVolume(volRoute, originalVolume),
                 ROUTE_ID_VARIABLE_VOLUME,
                 (route -> route.getVolume() == originalVolume));
     }
 
     @Test
+    public void testSetSessionVolume() throws Exception {
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
+        addRouterCallback(new RouteCallback());
+
+        CountDownLatch onSessionCreatedLatch = new CountDownLatch(1);
+        CountDownLatch volumeChangedLatch = new CountDownLatch(2);
+
+        // create a controller
+        addManagerCallback(new MediaRouter2Manager.Callback() {
+            @Override
+            public void onSessionCreated(MediaRouter2Manager.RoutingController controller) {
+                assertNotNull(controller);
+                onSessionCreatedLatch.countDown();
+            }
+        });
+
+        mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1));
+        assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+        List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName);
+        assertEquals(2, sessions.size());
+
+        // test setSessionVolume
+        RoutingSessionInfo sessionInfo = sessions.get(1);
+        int currentVolume = sessionInfo.getVolume();
+        int targetVolume = (currentVolume == 0) ? 1 : (currentVolume - 1);
+
+        RoutingControllerCallback routingControllerCallback = new RoutingControllerCallback() {
+            @Override
+            public void onControllerUpdated(MediaRouter2.RoutingController controller) {
+                if (!TextUtils.equals(sessionInfo.getId(), controller.getId())) {
+                    return;
+                }
+                if (controller.getVolume() == targetVolume) {
+                    volumeChangedLatch.countDown();
+                }
+            }
+        };
+        mRouter2.registerControllerCallback(mExecutor, routingControllerCallback);
+
+        addManagerCallback(new MediaRouter2Manager.Callback() {
+            @Override
+            public void onSessionsUpdated() {
+                List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName);
+                if (sessions.size() != 2) {
+                    return;
+                }
+
+                if (sessions.get(1).getVolume() == targetVolume) {
+                    volumeChangedLatch.countDown();
+                }
+            }
+        });
+
+        mManager.setSessionVolume(sessionInfo, targetVolume);
+
+        try {
+            assertTrue(volumeChangedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        } finally {
+            mRouter2.unregisterControllerCallback(routingControllerCallback);
+        }
+    }
+
+    @Test
     public void testVolumeHandling() throws Exception {
         Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
 
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
index 1a866ca..267927f 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
@@ -18,6 +18,7 @@
 
 import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_SPEAKER;
 import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_TV;
+import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
 
 import android.annotation.Nullable;
 import android.content.Intent;
@@ -51,6 +52,8 @@
     public static final String ROUTE_NAME_SPECIAL_FEATURE = "Special Feature Route";
 
     public static final int VOLUME_MAX = 100;
+    public static final int SESSION_VOLUME_MAX = 50;
+    public static final int SESSION_VOLUME_INITIAL = 20;
     public static final String ROUTE_ID_FIXED_VOLUME = "route_fixed_volume";
     public static final String ROUTE_NAME_FIXED_VOLUME = "Fixed Volume Route";
     public static final String ROUTE_ID_VARIABLE_VOLUME = "route_variable_volume";
@@ -141,12 +144,12 @@
     }
 
     @Override
-    public void onSetVolume(String routeId, int volume) {
+    public void onSetRouteVolume(String routeId, int volume) {
         MediaRoute2Info route = mRoutes.get(routeId);
         if (route == null) {
             return;
         }
-        volume = Math.min(volume, Math.max(0, route.getVolumeMax()));
+        volume = Math.max(0, Math.min(volume, route.getVolumeMax()));
         mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
                 .setVolume(volume)
                 .build());
@@ -154,6 +157,19 @@
     }
 
     @Override
+    public void onSetSessionVolume(String sessionId, int volume) {
+        RoutingSessionInfo sessionInfo = getSessionInfo(sessionId);
+        if (sessionInfo == null) {
+            return;
+        }
+        volume = Math.max(0, Math.min(volume, sessionInfo.getVolumeMax()));
+        RoutingSessionInfo newSessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
+                .setVolume(volume)
+                .build();
+        notifySessionUpdated(newSessionInfo);
+    }
+
+    @Override
     public void onCreateSession(String packageName, String routeId, long requestId,
             @Nullable Bundle sessionHints) {
         MediaRoute2Info route = mRoutes.get(routeId);
@@ -176,6 +192,9 @@
                 .addSelectedRoute(routeId)
                 .addSelectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
                 .addTransferrableRoute(ROUTE_ID5_TO_TRANSFER_TO)
+                .setVolumeHandling(PLAYBACK_VOLUME_VARIABLE)
+                .setVolumeMax(SESSION_VOLUME_MAX)
+                .setVolume(SESSION_VOLUME_INITIAL)
                 // Set control hints with given sessionHints
                 .setControlHints(sessionHints)
                 .build();
diff --git a/media/tests/TunerTest/OWNERS b/media/tests/TunerTest/OWNERS
new file mode 100644
index 0000000..73ea663
--- /dev/null
+++ b/media/tests/TunerTest/OWNERS
@@ -0,0 +1,4 @@
+amyjojo@google.com
+nchalko@google.com
+quxiangfang@google.com
+shubang@google.com
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index 2e4d214..d1946b0 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -28,6 +28,7 @@
 #include <utils/Color.h>
 
 #include <fcntl.h>
+#include <limits>
 #include <optional>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -70,6 +71,14 @@
         return ResultToErrorCode(result);
     }
 
+    // AImageDecoderHeaderInfo_getWidth/Height return an int32_t. Ensure that
+    // the conversion is safe.
+    const auto& info = androidCodec->getInfo();
+    if (info.width() > std::numeric_limits<int32_t>::max()
+        || info.height() > std::numeric_limits<int32_t>::max()) {
+        return ANDROID_IMAGE_DECODER_INVALID_INPUT;
+    }
+
     *outDecoder = reinterpret_cast<AImageDecoder*>(new ImageDecoder(std::move(androidCodec)));
     return ANDROID_IMAGE_DECODER_SUCCESS;
 }
diff --git a/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp b/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp
index 01150b7..6bd5a69 100644
--- a/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp
+++ b/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp
@@ -261,6 +261,11 @@
         mReadLogFd.reset();
     }
 
+    // Installation callback
+    bool onPrepareImage(const android::dataloader::DataLoaderInstallationFiles& addedFiles) final {
+        return true;
+    }
+
     // IFS callbacks.
     void onPendingReads(const android::dataloader::PendingReads& pendingReads) final {
         std::lock_guard lock{mMapsMutex};
@@ -520,6 +525,6 @@
 
 int JNI_OnLoad(JavaVM* jvm, void* /* reserved */) {
   android::dataloader::DataLoader::initialize(
-            [](auto) { return std::make_unique<AdbDataLoader>(); });
+            [](auto, auto) { return std::make_unique<AdbDataLoader>(); });
     return JNI_VERSION_1_6;
 }
diff --git a/packages/Incremental/NativeAdbDataLoader/src/com/android/incremental/nativeadb/NativeAdbDataLoaderService.java b/packages/Incremental/NativeAdbDataLoader/src/com/android/incremental/nativeadb/NativeAdbDataLoaderService.java
index bd5b795..c4e41c8 100644
--- a/packages/Incremental/NativeAdbDataLoader/src/com/android/incremental/nativeadb/NativeAdbDataLoaderService.java
+++ b/packages/Incremental/NativeAdbDataLoader/src/com/android/incremental/nativeadb/NativeAdbDataLoaderService.java
@@ -16,6 +16,8 @@
 
 package com.android.incremental.nativeadb;
 
+import android.annotation.NonNull;
+import android.content.pm.DataLoaderParams;
 import android.service.dataloader.DataLoaderService;
 
 /** This code is used for testing only. */
@@ -26,7 +28,7 @@
     }
 
     @Override
-    public DataLoader onCreateDataLoader() {
+    public DataLoader onCreateDataLoader(@NonNull DataLoaderParams dataLoaderParams) {
         return null;
     }
 }
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 804e0cb..59881e7 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1069,17 +1069,17 @@
     <string name="power_remaining_only_more_than_subtext">More than <xliff:g id="time_remaining">%1$s</xliff:g> remaining</string>
 
     <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
-    <string name="power_remaining_duration_only_shutdown_imminent" product="default">Phone may shutdown soon</string>
+    <string name="power_remaining_duration_only_shutdown_imminent" product="default">Phone may shut down soon</string>
     <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
-    <string name="power_remaining_duration_only_shutdown_imminent" product="tablet">Tablet may shutdown soon</string>
+    <string name="power_remaining_duration_only_shutdown_imminent" product="tablet">Tablet may shut down soon</string>
     <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
-    <string name="power_remaining_duration_only_shutdown_imminent" product="device">Device may shutdown soon</string>
+    <string name="power_remaining_duration_only_shutdown_imminent" product="device">Device may shut down soon</string>
     <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
-    <string name="power_remaining_duration_shutdown_imminent" product="default">Phone may shutdown soon (<xliff:g id="level">%1$s</xliff:g>)</string>
+    <string name="power_remaining_duration_shutdown_imminent" product="default">Phone may shut down soon (<xliff:g id="level">%1$s</xliff:g>)</string>
     <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
-    <string name="power_remaining_duration_shutdown_imminent" product="tablet">Tablet may shutdown soon (<xliff:g id="level">%1$s</xliff:g>)</string>
+    <string name="power_remaining_duration_shutdown_imminent" product="tablet">Tablet may shut down soon (<xliff:g id="level">%1$s</xliff:g>)</string>
     <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
-    <string name="power_remaining_duration_shutdown_imminent" product="device">Device may shutdown soon (<xliff:g id="level">%1$s</xliff:g>)</string>
+    <string name="power_remaining_duration_shutdown_imminent" product="device">Device may shut down soon (<xliff:g id="level">%1$s</xliff:g>)</string>
 
     <!-- [CHAR_LIMIT=40] Label for battery level chart when charging -->
     <string name="power_charging"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="state">%2$s</xliff:g></string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 1ebe917..d03a747 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -216,12 +216,12 @@
     }
 
     public boolean supportsHighQualityAudio(BluetoothDevice device) {
-        int support = mService.supportsOptionalCodecs(device);
+        int support = mService.isOptionalCodecsSupported(device);
         return support == BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED;
     }
 
     public boolean isHighQualityAudioEnabled(BluetoothDevice device) {
-        int enabled = mService.getOptionalCodecsEnabled(device);
+        int enabled = mService.isOptionalCodecsEnabled(device);
         if (enabled != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN) {
             return enabled == BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED;
         } else if (getConnectionStatus(device) != BluetoothProfile.STATE_CONNECTED &&
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 3aa35cb..5d1e4cb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -41,7 +41,6 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
 
@@ -71,10 +70,10 @@
     short mRssi;
     // mProfiles and mRemovedProfiles does not do swap() between main and sub device. It is
     // because current sub device is only for HearingAid and its profile is the same.
-    private final List<LocalBluetoothProfile> mProfiles = new ArrayList<>();
+    private final Collection<LocalBluetoothProfile> mProfiles = new CopyOnWriteArrayList<>();
 
     // List of profiles that were previously in mProfiles, but have been removed
-    private final List<LocalBluetoothProfile> mRemovedProfiles = new ArrayList<>();
+    private final Collection<LocalBluetoothProfile> mRemovedProfiles = new CopyOnWriteArrayList<>();
 
     // Device supports PANU but not NAP: remove PanProfile after device disconnects from NAP
     private boolean mLocalNapRoleConnected;
@@ -717,7 +716,7 @@
     }
 
     public List<LocalBluetoothProfile> getProfiles() {
-        return Collections.unmodifiableList(mProfiles);
+        return new ArrayList<>(mProfiles);
     }
 
     public List<LocalBluetoothProfile> getConnectableProfiles() {
@@ -734,7 +733,7 @@
     }
 
     public List<LocalBluetoothProfile> getRemovedProfiles() {
-        return mRemovedProfiles;
+        return new ArrayList<>(mRemovedProfiles);
     }
 
     public void registerCallback(Callback callback) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index c72efb7..35bbbc0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -48,6 +48,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 
 /**
@@ -232,7 +233,7 @@
     }
 
     private final Collection<ServiceListener> mServiceListeners =
-            new ArrayList<ServiceListener>();
+            new CopyOnWriteArrayList<ServiceListener>();
 
     private void addProfile(LocalBluetoothProfile profile,
             String profileName, String stateChangedAction) {
@@ -361,14 +362,18 @@
 
     // not synchronized: use only from UI thread! (TODO: verify)
     void callServiceConnectedListeners() {
-        for (ServiceListener l : mServiceListeners) {
+        final Collection<ServiceListener> listeners = new ArrayList<>(mServiceListeners);
+
+        for (ServiceListener l : listeners) {
             l.onServiceConnected();
         }
     }
 
     // not synchronized: use only from UI thread! (TODO: verify)
     void callServiceDisconnectedListeners() {
-        for (ServiceListener listener : mServiceListeners) {
+        final Collection<ServiceListener> listeners = new ArrayList<>(mServiceListeners);
+
+        for (ServiceListener listener : listeners) {
             listener.onServiceDisconnected();
         }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/display/BrightnessUtils.java b/packages/SettingsLib/src/com/android/settingslib/display/BrightnessUtils.java
index 55723f9..57d9594 100644
--- a/packages/SettingsLib/src/com/android/settingslib/display/BrightnessUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/display/BrightnessUtils.java
@@ -20,7 +20,8 @@
 
 public class BrightnessUtils {
 
-    public static final int GAMMA_SPACE_MAX = 1023;
+    public static final int GAMMA_SPACE_MIN = 0;
+    public static final int GAMMA_SPACE_MAX = 65535;
 
     // Hybrid Log Gamma constant values
     private static final float R = 0.5f;
@@ -51,7 +52,7 @@
      * @return The corresponding setting value.
      */
     public static final int convertGammaToLinear(int val, int min, int max) {
-        final float normalizedVal = MathUtils.norm(0, GAMMA_SPACE_MAX, val);
+        final float normalizedVal = MathUtils.norm(GAMMA_SPACE_MIN, GAMMA_SPACE_MAX, val);
         final float ret;
         if (normalizedVal <= R) {
             ret = MathUtils.sq(normalizedVal / R);
@@ -65,6 +66,29 @@
     }
 
     /**
+     * Version of {@link #convertGammaToLinear} that takes and returns float values.
+     * TODO: brightnessfloat Merge with above method later.
+     *
+     * @param val The slider value.
+     * @param min The minimum acceptable value for the setting.
+     * @param max The maximum acceptable value for the setting.
+     * @return The corresponding setting value.
+     */
+    public static final float convertGammaToLinearFloat(int val, float min, float max) {
+        final float normalizedVal = MathUtils.norm(GAMMA_SPACE_MIN, GAMMA_SPACE_MAX, val);
+        final float ret;
+        if (normalizedVal <= R) {
+            ret = MathUtils.sq(normalizedVal / R);
+        } else {
+            ret = MathUtils.exp((normalizedVal - C) / A) + B;
+        }
+
+        // HLG is normalized to the range [0, 12], so we need to re-normalize to the range [0, 1]
+        // in order to derive the correct setting value.
+        return MathUtils.lerp(min, max, ret / 12);
+    }
+
+    /**
      * A function for converting from the linear space that the setting works in to the
      * gamma space that the slider works in.
      *
@@ -96,6 +120,27 @@
             ret = A * MathUtils.log(normalizedVal - B) + C;
         }
 
-        return Math.round(MathUtils.lerp(0, GAMMA_SPACE_MAX, ret));
+        return Math.round(MathUtils.lerp(GAMMA_SPACE_MIN, GAMMA_SPACE_MAX, ret));
+    }
+
+    /**
+     * Version of {@link #convertLinearToGamma} that takes float values.
+     * TODO: brightnessfloat merge with above method(?)
+     * @param val The brightness setting value.
+     * @param min The minimum acceptable value for the setting.
+     * @param max The maximum acceptable value for the setting.
+     * @return The corresponding slider value
+     */
+    public static final int convertLinearToGammaFloat(float val, float min, float max) {
+        // For some reason, HLG normalizes to the range [0, 12] rather than [0, 1]
+        final float normalizedVal = MathUtils.norm(min, max, val) * 12;
+        final float ret;
+        if (normalizedVal <= 1f) {
+            ret = MathUtils.sqrt(normalizedVal) * R;
+        } else {
+            ret = A * MathUtils.log(normalizedVal - B) + C;
+        }
+
+        return Math.round(MathUtils.lerp(GAMMA_SPACE_MIN, GAMMA_SPACE_MAX, ret));
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index b9081f2..b725ba5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -16,12 +16,9 @@
 package com.android.settingslib.media;
 
 import android.content.Context;
-import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
 import android.media.MediaRoute2Info;
 import android.media.MediaRouter2Manager;
-import android.text.TextUtils;
-import android.util.Log;
 
 import com.android.settingslib.R;
 import com.android.settingslib.bluetooth.BluetoothUtils;
@@ -62,46 +59,6 @@
         return MediaDeviceUtils.getId(mRouteInfo);
     }
 
-    @Override
-    public void requestSetVolume(int volume) {
-        mRouterManager.requestSetVolume(mRouteInfo, volume);
-    }
-
-    @Override
-    public int getMaxVolume() {
-        return mRouteInfo.getVolumeMax();
-    }
-
-    @Override
-    public int getCurrentVolume() {
-        return mRouteInfo.getVolume();
-    }
-
-    @Override
-    public String getClientPackageName() {
-        return mRouteInfo.getClientPackageName();
-    }
-
-    @Override
-    public String getClientAppLabel() {
-        final String packageName = mRouteInfo.getClientPackageName();
-        if (TextUtils.isEmpty(packageName)) {
-            Log.d(TAG, "Client package name is empty");
-            return mContext.getResources().getString(R.string.unknown);
-        }
-        try {
-            final PackageManager packageManager = mContext.getPackageManager();
-            final String appLabel = packageManager.getApplicationLabel(
-                    packageManager.getApplicationInfo(packageName, 0)).toString();
-            if (!TextUtils.isEmpty(appLabel)) {
-                return appLabel;
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.e(TAG, "unable to find " + packageName);
-        }
-        return mContext.getResources().getString(R.string.unknown);
-    }
-
     public boolean isConnected() {
         return true;
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 580e086..33c3d7e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -16,14 +16,18 @@
 package com.android.settingslib.media;
 
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
 import android.media.MediaRoute2Info;
 import android.media.MediaRouter2Manager;
 import android.text.TextUtils;
+import android.util.Log;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.settingslib.R;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -113,7 +117,9 @@
      *
      * @param volume is the new value.
      */
+
     public void requestSetVolume(int volume) {
+        mRouterManager.requestSetVolume(mRouteInfo, volume);
     }
 
     /**
@@ -122,7 +128,7 @@
      * @return max volume.
      */
     public int getMaxVolume() {
-        return 100;
+        return mRouteInfo.getVolumeMax();
     }
 
     /**
@@ -131,7 +137,7 @@
      * @return current volume.
      */
     public int getCurrentVolume() {
-        return 0;
+        return mRouteInfo.getVolume();
     }
 
     /**
@@ -140,7 +146,7 @@
      * @return package name.
      */
     public String getClientPackageName() {
-        return null;
+        return mRouteInfo.getClientPackageName();
     }
 
     /**
@@ -149,7 +155,22 @@
      * @return application label.
      */
     public String getClientAppLabel() {
-        return null;
+        final String packageName = mRouteInfo.getClientPackageName();
+        if (TextUtils.isEmpty(packageName)) {
+            Log.d(TAG, "Client package name is empty");
+            return mContext.getResources().getString(R.string.unknown);
+        }
+        try {
+            final PackageManager packageManager = mContext.getPackageManager();
+            final String appLabel = packageManager.getApplicationLabel(
+                    packageManager.getApplicationInfo(packageName, 0)).toString();
+            if (!TextUtils.isEmpty(appLabel)) {
+                return appLabel;
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "unable to find " + packageName);
+        }
+        return mContext.getResources().getString(R.string.unknown);
     }
 
     /**
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
index c555cbe..cb9092e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
@@ -73,26 +73,26 @@
 
     @Test
     public void supportsHighQualityAudio() {
-        when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
+        when(mBluetoothA2dp.isOptionalCodecsSupported(any())).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED);
         assertThat(mProfile.supportsHighQualityAudio(mDevice)).isTrue();
 
-        when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
+        when(mBluetoothA2dp.isOptionalCodecsSupported(any())).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED);
         assertThat(mProfile.supportsHighQualityAudio(mDevice)).isFalse();
 
-        when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
+        when(mBluetoothA2dp.isOptionalCodecsSupported(any())).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN);
         assertThat(mProfile.supportsHighQualityAudio(mDevice)).isFalse();
     }
 
     @Test
     public void isHighQualityAudioEnabled() {
-        when(mBluetoothA2dp.getOptionalCodecsEnabled(any())).thenReturn(
+        when(mBluetoothA2dp.isOptionalCodecsEnabled(any())).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED);
         assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isTrue();
 
-        when(mBluetoothA2dp.getOptionalCodecsEnabled(any())).thenReturn(
+        when(mBluetoothA2dp.isOptionalCodecsEnabled(any())).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED);
         assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isFalse();
 
@@ -100,16 +100,16 @@
         // then isHighQualityAudioEnabled() should return true or false based on whether optional
         // codecs are supported. If the device is connected then we should ask it directly, but if
         // the device isn't connected then rely on the stored pref about such support.
-        when(mBluetoothA2dp.getOptionalCodecsEnabled(any())).thenReturn(
+        when(mBluetoothA2dp.isOptionalCodecsEnabled(any())).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN);
         when(mBluetoothA2dp.getConnectionState(any())).thenReturn(
                 BluetoothProfile.STATE_DISCONNECTED);
 
-        when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
+        when(mBluetoothA2dp.isOptionalCodecsSupported(any())).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED);
         assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isFalse();
 
-        when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
+        when(mBluetoothA2dp.isOptionalCodecsSupported(any())).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED);
         assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isTrue();
 
@@ -151,14 +151,14 @@
 
         // Most tests want to simulate optional codecs being supported by the device, so do that
         // by default here.
-        when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
+        when(mBluetoothA2dp.isOptionalCodecsSupported(any())).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED);
     }
 
     @Test
     public void getLableCodecsNotSupported() {
         setupLabelTest();
-        when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
+        when(mBluetoothA2dp.isOptionalCodecsSupported(any())).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED);
         assertThat(mProfile.getHighQualityAudioOptionLabel(mDevice)).isEqualTo(UNKNOWN_CODEC_LABEL);
     }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
index 04ceb21..77a67c2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
@@ -21,9 +21,6 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageStats;
 import android.media.MediaRoute2Info;
 import android.media.MediaRouter2Manager;
 
@@ -36,14 +33,11 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
-import org.robolectric.Shadows;
-import org.robolectric.shadows.ShadowPackageManager;
 
 @RunWith(RobolectricTestRunner.class)
 public class InfoMediaDeviceTest {
 
     private static final String TEST_PACKAGE_NAME = "com.test.packagename";
-    private static final String TEST_PACKAGE_NAME2 = "com.test.packagename2";
     private static final String TEST_ID = "test_id";
     private static final String TEST_NAME = "test_name";
 
@@ -52,27 +46,13 @@
     @Mock
     private MediaRoute2Info mRouteInfo;
 
-
     private Context mContext;
     private InfoMediaDevice mInfoMediaDevice;
-    private ShadowPackageManager mShadowPackageManager;
-    private ApplicationInfo mAppInfo;
-    private PackageInfo mPackageInfo;
-    private PackageStats mPackageStats;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mContext = RuntimeEnvironment.application;
-        mShadowPackageManager = Shadows.shadowOf(mContext.getPackageManager());
-        mAppInfo = new ApplicationInfo();
-        mAppInfo.flags = ApplicationInfo.FLAG_INSTALLED;
-        mAppInfo.packageName = TEST_PACKAGE_NAME;
-        mAppInfo.name = TEST_NAME;
-        mPackageInfo = new PackageInfo();
-        mPackageInfo.packageName = TEST_PACKAGE_NAME;
-        mPackageInfo.applicationInfo = mAppInfo;
-        mPackageStats = new PackageStats(TEST_PACKAGE_NAME);
 
         mInfoMediaDevice = new InfoMediaDevice(mContext, mRouterManager, mRouteInfo,
                 TEST_PACKAGE_NAME);
@@ -106,32 +86,4 @@
 
         assertThat(mInfoMediaDevice.getId()).isEqualTo(TEST_ID);
     }
-
-    @Test
-    public void getClientPackageName_returnPackageName() {
-        when(mRouteInfo.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
-
-        assertThat(mInfoMediaDevice.getClientPackageName()).isEqualTo(TEST_PACKAGE_NAME);
-    }
-
-    @Test
-    public void getClientAppLabel_matchedPackageName_returnLabel() {
-        when(mRouteInfo.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
-
-        assertThat(mInfoMediaDevice.getClientAppLabel()).isEqualTo(
-                mContext.getResources().getString(R.string.unknown));
-
-        mShadowPackageManager.addPackage(mPackageInfo, mPackageStats);
-
-        assertThat(mInfoMediaDevice.getClientAppLabel()).isEqualTo(TEST_NAME);
-    }
-
-    @Test
-    public void getClientAppLabel_noMatchedPackageName_returnDefault() {
-        mShadowPackageManager.addPackage(mPackageInfo, mPackageStats);
-        when(mRouteInfo.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME2);
-
-        assertThat(mInfoMediaDevice.getClientAppLabel()).isEqualTo(
-                mContext.getResources().getString(R.string.unknown));
-    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
index fb8b78b..3f29b72 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -23,9 +23,13 @@
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageStats;
 import android.media.MediaRoute2Info;
 import android.media.MediaRouter2Manager;
 
+import com.android.settingslib.R;
 import com.android.settingslib.bluetooth.A2dpProfile;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.HearingAidProfile;
@@ -39,6 +43,8 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.Shadows;
+import org.robolectric.shadows.ShadowPackageManager;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -58,6 +64,8 @@
     private static final String ROUTER_ID_2 = "RouterId_2";
     private static final String ROUTER_ID_3 = "RouterId_3";
     private static final String TEST_PACKAGE_NAME = "com.test.playmusic";
+    private static final String TEST_PACKAGE_NAME2 = "com.test.playmusic2";
+    private static final String TEST_APPLICATION_LABEL = "playmusic";
     private final BluetoothClass mHeadreeClass =
             new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
     private final BluetoothClass mCarkitClass =
@@ -111,6 +119,10 @@
     private InfoMediaDevice mInfoMediaDevice3;
     private List<MediaDevice> mMediaDevices = new ArrayList<>();
     private PhoneMediaDevice mPhoneMediaDevice;
+    private ShadowPackageManager mShadowPackageManager;
+    private ApplicationInfo mAppInfo;
+    private PackageInfo mPackageInfo;
+    private PackageStats mPackageStats;
 
     @Before
     public void setUp() {
@@ -394,4 +406,46 @@
 
         verify(mMediaRouter2Manager).selectRoute(TEST_PACKAGE_NAME, mRouteInfo1);
     }
+
+    @Test
+    public void getClientPackageName_returnPackageName() {
+        when(mRouteInfo1.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+        assertThat(mInfoMediaDevice1.getClientPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+    }
+
+    private void initPackage() {
+        mShadowPackageManager = Shadows.shadowOf(mContext.getPackageManager());
+        mAppInfo = new ApplicationInfo();
+        mAppInfo.flags = ApplicationInfo.FLAG_INSTALLED;
+        mAppInfo.packageName = TEST_PACKAGE_NAME;
+        mAppInfo.name = TEST_APPLICATION_LABEL;
+        mPackageInfo = new PackageInfo();
+        mPackageInfo.packageName = TEST_PACKAGE_NAME;
+        mPackageInfo.applicationInfo = mAppInfo;
+        mPackageStats = new PackageStats(TEST_PACKAGE_NAME);
+    }
+
+    @Test
+    public void getClientAppLabel_matchedPackageName_returnLabel() {
+        initPackage();
+        when(mRouteInfo1.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+        assertThat(mInfoMediaDevice1.getClientAppLabel()).isEqualTo(
+                mContext.getResources().getString(R.string.unknown));
+
+        mShadowPackageManager.addPackage(mPackageInfo, mPackageStats);
+
+        assertThat(mInfoMediaDevice1.getClientAppLabel()).isEqualTo(TEST_APPLICATION_LABEL);
+    }
+
+    @Test
+    public void getClientAppLabel_noMatchedPackageName_returnDefault() {
+        initPackage();
+        mShadowPackageManager.addPackage(mPackageInfo, mPackageStats);
+        when(mRouteInfo1.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME2);
+
+        assertThat(mInfoMediaDevice1.getClientAppLabel()).isEqualTo(
+                mContext.getResources().getString(R.string.unknown));
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
index 61fdbd5..ed308c8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
@@ -120,9 +120,9 @@
                 true /* basedOnUsage */);
 
         // additional battery percentage in this string
-        assertThat(info).isEqualTo("Phone may shutdown soon (10%)");
+        assertThat(info).isEqualTo("Phone may shut down soon (10%)");
         // shortened string should not have percentage
-        assertThat(info2).isEqualTo("Phone may shutdown soon");
+        assertThat(info2).isEqualTo("Phone may shut down soon");
     }
 
     @Test
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index de4817c..2431381 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -37,6 +37,7 @@
         Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
         Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
         Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
+        Settings.Secure.ADAPTIVE_SLEEP,
         Settings.Secure.AUTOFILL_SERVICE,
         Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
         Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index 3f5b0da..c4330e1 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -46,7 +46,7 @@
         Settings.System.SCREEN_BRIGHTNESS_MODE,
         Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
         Settings.System.SCREEN_BRIGHTNESS_FOR_VR,
-        Settings.System.ADAPTIVE_SLEEP,
+        Settings.System.ADAPTIVE_SLEEP,             // moved to secure
         Settings.System.VIBRATE_INPUT_DEVICES,
         Settings.System.MODE_RINGER_STREAMS_AFFECTED,
         Settings.System.TEXT_AUTO_REPLACE,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 849f22f..5553469 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -59,6 +59,7 @@
         VALIDATORS.put(Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(
                 Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.ADAPTIVE_SLEEP, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.AUTOFILL_SERVICE, NULLABLE_COMPONENT_NAME_VALIDATOR);
         VALIDATORS.put(
                 Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index c5d4fa9..8037266 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -126,7 +126,6 @@
         VALIDATORS.put(System.SCREEN_OFF_TIMEOUT, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(System.SCREEN_BRIGHTNESS_FOR_VR, new InclusiveIntegerRangeValidator(0, 255));
         VALIDATORS.put(System.SCREEN_BRIGHTNESS_MODE, BOOLEAN_VALIDATOR);
-        VALIDATORS.put(System.ADAPTIVE_SLEEP, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.MODE_RINGER_STREAMS_AFFECTED, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(System.MUTE_STREAMS_AFFECTED, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(System.VIBRATE_ON, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 78b9f16..d677687 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1813,6 +1813,12 @@
                 SecureSettingsProto.Accessibility.ACCESSIBILITY_MAGNIFICATION_MODE);
         p.end(accessibilityToken);
 
+        final long adaptiveSleepToken = p.start(SecureSettingsProto.ADAPTIVE_SLEEP);
+        dumpSetting(s, p,
+                Settings.Secure.ADAPTIVE_SLEEP,
+                SecureSettingsProto.AdaptiveSleep.ENABLED);
+        p.end(adaptiveSleepToken);
+
         dumpSetting(s, p,
                 Settings.Secure.ALLOWED_GEOLOCATION_ORIGINS,
                 SecureSettingsProto.ALLOWED_GEOLOCATION_ORIGINS);
@@ -2727,6 +2733,12 @@
         dumpSetting(s, p,
                 Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
                 SystemSettingsProto.Screen.AUTO_BRIGHTNESS_ADJ);
+        dumpSetting(s, p,
+                Settings.System.SCREEN_BRIGHTNESS_FLOAT,
+                SystemSettingsProto.Screen.BRIGHTNESS_FLOAT);
+        dumpSetting(s, p,
+                Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT,
+                SystemSettingsProto.Screen.BRIGHTNESS_FOR_VR_FLOAT);
         p.end(screenToken);
 
         dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index c969bfd..aad46e9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2193,7 +2193,7 @@
         if (prefix == null) {
             return;
         }
-        String callingPackage = getCallingPackage();
+        String callingPackage = resolveCallingPackage();
         String namespace = prefix.replace("/", "");
         if (DeviceConfig.getPublicNamespaces().contains(namespace)) {
             return;
@@ -3426,7 +3426,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 187;
+            private static final int SETTINGS_VERSION = 188;
 
             private final int mUserId;
 
@@ -4732,6 +4732,23 @@
                     currentVersion = 187;
                 }
 
+                if (currentVersion == 187) {
+                    // Migrate adaptive sleep setting from System to Secure.
+                    if (userId == UserHandle.USER_OWNER) {
+                        // Remove from the system settings.
+                        SettingsState systemSettings = getSystemSettingsLocked(userId);
+                        String name = Settings.System.ADAPTIVE_SLEEP;
+                        Setting setting = systemSettings.getSettingLocked(name);
+                        systemSettings.deleteSettingLocked(name);
+
+                        // Add to the secure settings.
+                        SettingsState secureSettings = getSecureSettingsLocked(userId);
+                        secureSettings.insertSettingLocked(name, setting.getValue(), null /* tag */,
+                                false /* makeDefault */, SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+                    currentVersion = 188;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index dee1d7e..bf817b1 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -99,7 +99,9 @@
                     Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug?
                     Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only
                     Settings.System.MIN_REFRESH_RATE, // depends on hardware capabilities
-                    Settings.System.PEAK_REFRESH_RATE // depends on hardware capabilities
+                    Settings.System.PEAK_REFRESH_RATE, // depends on hardware capabilities
+                    Settings.System.SCREEN_BRIGHTNESS_FLOAT,
+                    Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT
                     );
 
     private static final Set<String> BACKUP_BLACKLISTED_GLOBAL_SETTINGS =
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 5946f21..772f6e4 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -139,6 +139,7 @@
     <uses-permission android:name="android.permission.GET_APP_OPS_STATS" />
     <uses-permission android:name="android.permission.MANAGE_APP_OPS_MODES" />
     <uses-permission android:name="android.permission.VIBRATE" />
+    <uses-permission android:name="android.permission.ACCESS_VIBRATOR_STATE" />
     <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
     <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
     <uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 1fe967b..5458676e 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -646,16 +646,17 @@
                   android:label="Controls Providers"
                   android:theme="@style/Theme.ControlsManagement"
                   android:showForAllUsers="true"
+                  android:clearTaskOnLaunch="true"
                   android:excludeFromRecents="true"
                   android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
                   android:visibleToInstantApps="true">
         </activity>
 
         <activity android:name=".controls.management.ControlsFavoritingActivity"
-                  android:parentActivityName=".controls.management.ControlsProviderSelectorActivity"
                   android:theme="@style/Theme.ControlsManagement"
                   android:excludeFromRecents="true"
                   android:showForAllUsers="true"
+                  android:finishOnTaskLaunch="true"
                   android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
                   android:visibleToInstantApps="true">
         </activity>
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
index 0a2dd6c..bcff634 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
@@ -32,7 +32,7 @@
 public interface FalsingManager {
     int VERSION = 3;
 
-    void onSucccessfulUnlock();
+    void onSuccessfulUnlock();
 
     void onNotificationActive();
 
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index 01811e9..e586c38 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -14,6 +14,7 @@
 
 package com.android.systemui.plugins.qs;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.metrics.LogMaker;
@@ -107,6 +108,12 @@
         public int getPadding() {
             return 0;
         }
+
+        @Override
+        @NonNull
+        public String toString() {
+            return "Icon";
+        }
     }
 
     @ProvidesInterface(version = State.VERSION)
@@ -118,6 +125,7 @@
         public CharSequence label;
         public CharSequence secondaryLabel;
         public CharSequence contentDescription;
+        public CharSequence stateDescription;
         public CharSequence dualLabelContentDescription;
         public boolean disabledByPolicy;
         public boolean dualTarget = false;
@@ -135,6 +143,7 @@
                     || !Objects.equals(other.label, label)
                     || !Objects.equals(other.secondaryLabel, secondaryLabel)
                     || !Objects.equals(other.contentDescription, contentDescription)
+                    || !Objects.equals(other.stateDescription, stateDescription)
                     || !Objects.equals(other.dualLabelContentDescription,
                             dualLabelContentDescription)
                     || !Objects.equals(other.expandedAccessibilityClassName,
@@ -151,6 +160,7 @@
             other.label = label;
             other.secondaryLabel = secondaryLabel;
             other.contentDescription = contentDescription;
+            other.stateDescription = stateDescription;
             other.dualLabelContentDescription = dualLabelContentDescription;
             other.expandedAccessibilityClassName = expandedAccessibilityClassName;
             other.disabledByPolicy = disabledByPolicy;
@@ -177,6 +187,7 @@
             sb.append(",label=").append(label);
             sb.append(",secondaryLabel=").append(secondaryLabel);
             sb.append(",contentDescription=").append(contentDescription);
+            sb.append(",stateDescription=").append(stateDescription);
             sb.append(",dualLabelContentDescription=").append(dualLabelContentDescription);
             sb.append(",expandedAccessibilityClassName=").append(expandedAccessibilityClassName);
             sb.append(",disabledByPolicy=").append(disabledByPolicy);
diff --git a/packages/SystemUI/res-keyguard/layout/controls_management.xml b/packages/SystemUI/res-keyguard/layout/controls_management.xml
deleted file mode 100644
index 8330258..0000000
--- a/packages/SystemUI/res-keyguard/layout/controls_management.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:gravity="center_horizontal"
-    android:paddingTop="@dimen/controls_management_top_padding"
-    android:paddingStart="@dimen/controls_management_side_padding"
-    android:paddingEnd="@dimen/controls_management_side_padding" >
-
-    <TextView
-        android:id="@+id/title"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textAppearance="?android:attr/textAppearanceLarge"
-        android:textSize="@dimen/controls_title_size"
-        android:textAlignment="center" />
-
-    <TextView
-        android:id="@+id/subtitle"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/controls_management_titles_margin"
-        android:textAppearance="?android:attr/textAppearanceSmall"
-        android:textAlignment="center" />
-
-    <androidx.recyclerview.widget.RecyclerView
-        android:id="@+id/list"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/controls_management_list_margin" />
-
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/controls_zone_header.xml b/packages/SystemUI/res-keyguard/layout/controls_zone_header.xml
new file mode 100644
index 0000000..7b43a03
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/controls_zone_header.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:textAppearance="@style/TextAppearance.Control.Title"
+    android:textColor="?android:attr/colorPrimary"
+    android:layout_marginStart="12dp"
+    android:layout_marginEnd="2dp"
+    android:layout_marginTop="8dp"
+    android:layout_marginBottom="4dp">
+
+</TextView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_important.xml b/packages/SystemUI/res/drawable/ic_important.xml
new file mode 100644
index 0000000..d7439e1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_important.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2020 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M4,18.99h11c0.67,0 1.27,-0.32 1.63,-0.83L21,12l-4.37,-6.16C16.27,5.33 15.67,5 15,5H4l5,7 -5,6.99z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_important_outline.xml b/packages/SystemUI/res/drawable/ic_important_outline.xml
new file mode 100644
index 0000000..7a628bb
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_important_outline.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2020 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M15,19L3,19l4.5,-7L3,5h12c0.65,0 1.26,0.31 1.63,0.84L21,12l-4.37,6.16c-0.37,0.52 -0.98,0.84 -1.63,0.84zM6.5,17L15,17l3.5,-5L15,7L6.5,7l3.5,5 -3.5,5z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_star.xml b/packages/SystemUI/res/drawable/ic_star.xml
deleted file mode 100644
index 4a731b3..0000000
--- a/packages/SystemUI/res/drawable/ic_star.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-  Copyright (C) 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License
-  -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:fillColor="@android:color/white"
-        android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27z"/>
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_star_border.xml b/packages/SystemUI/res/drawable/ic_star_border.xml
deleted file mode 100644
index 9ede40b..0000000
--- a/packages/SystemUI/res/drawable/ic_star_border.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-  Copyright (C) 2020 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License
-  -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:fillColor="@android:color/white"
-        android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/controls_base_item.xml b/packages/SystemUI/res/layout/controls_base_item.xml
index 68c8246..823bbcd 100644
--- a/packages/SystemUI/res/layout/controls_base_item.xml
+++ b/packages/SystemUI/res/layout/controls_base_item.xml
@@ -72,8 +72,8 @@
     <CheckBox
         android:id="@+id/favorite"
         android:visibility="gone"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintBottom_toBottomOf="parent"/>
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/controls_detail_dialog.xml b/packages/SystemUI/res/layout/controls_detail_dialog.xml
new file mode 100644
index 0000000..f2de45a
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_detail_dialog.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/controls_activity_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" />
+
diff --git a/packages/SystemUI/res/layout/controls_management.xml b/packages/SystemUI/res/layout/controls_management.xml
new file mode 100644
index 0000000..a7379be
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_management.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center_horizontal"
+    android:paddingTop="@dimen/controls_management_top_padding"
+    android:paddingStart="@dimen/controls_management_side_padding"
+    android:paddingEnd="@dimen/controls_management_side_padding" >
+
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+        android:textSize="@dimen/controls_title_size"
+        android:textAlignment="center" />
+
+    <TextView
+        android:id="@+id/subtitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/controls_management_titles_margin"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textAlignment="center" />
+
+    <androidx.core.widget.NestedScrollView
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:orientation="vertical"
+        android:layout_marginTop="@dimen/controls_management_list_margin">
+
+        <ViewStub
+            android:id="@+id/stub"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"/>
+
+    </androidx.core.widget.NestedScrollView>
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="64dp">
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/controls_app_divider_height"
+            android:layout_gravity="center_horizontal|top"
+            android:background="?android:attr/listDivider" />
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:padding="4dp">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:text="See other apps"
+                android:textAppearance="@style/TextAppearance.Control.Title"
+                android:textColor="?android:attr/colorPrimary"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <Button
+                android:id="@+id/done"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:text="Done"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"/>
+        </androidx.constraintlayout.widget.ConstraintLayout>
+    </FrameLayout>
+
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/controls_management_apps.xml b/packages/SystemUI/res/layout/controls_management_apps.xml
new file mode 100644
index 0000000..2bab433
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_management_apps.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<androidx.recyclerview.widget.RecyclerView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/list"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    >
+
+</androidx.recyclerview.widget.RecyclerView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/controls_management_favorites.xml b/packages/SystemUI/res/layout/controls_management_favorites.xml
new file mode 100644
index 0000000..a36dd12
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_management_favorites.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/text_favorites"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="FAVORITES"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/divider1"
+        app:layout_constraintTop_toTopOf="parent"
+        />
+
+    <View
+        android:id="@+id/divider1"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/controls_app_divider_height"
+        android:layout_gravity="center_horizontal|top"
+        android:background="?android:attr/listDivider"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/listFavorites"
+        app:layout_constraintTop_toBottomOf="@id/text_favorites"
+        />
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/listFavorites"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginTop="@dimen/controls_management_list_margin"
+        android:nestedScrollingEnabled="false"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/text_all"
+        app:layout_constraintTop_toBottomOf="@id/divider1"/>
+
+    <TextView
+        android:id="@+id/text_all"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/controls_management_list_margin"
+        android:text="ALL"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/divider2"
+        app:layout_constraintTop_toBottomOf="@id/listFavorites"
+        />
+
+    <View
+        android:id="@+id/divider2"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/controls_app_divider_height"
+        android:layout_gravity="center_horizontal|top"
+        android:background="?android:attr/listDivider"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/listAll"
+        app:layout_constraintTop_toBottomOf="@id/text_all"
+        />
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/listAll"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginTop="@dimen/controls_management_list_margin"
+        android:nestedScrollingEnabled="false"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/divider2"/>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/controls_with_favorites.xml b/packages/SystemUI/res/layout/controls_with_favorites.xml
index 40b2476..2cd9505 100644
--- a/packages/SystemUI/res/layout/controls_with_favorites.xml
+++ b/packages/SystemUI/res/layout/controls_with_favorites.xml
@@ -1,3 +1,18 @@
+<!--
+  ~ 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.
+  -->
 <merge
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto">
diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
index d685528..0dd3352 100644
--- a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
+++ b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
@@ -17,7 +17,7 @@
   -->
 
 <!-- LinearLayout -->
-<com.android.systemui.qs.tiles.UserDetailItemView
+<com.android.systemui.statusbar.policy.KeyguardUserDetailItemView
         xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:sysui="http://schemas.android.com/apk/res-auto"
         android:layout_width="wrap_content"
@@ -34,7 +34,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginEnd="13dp"
-            android:textAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher.UserName"
+            android:textAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher"
             />
     <com.android.systemui.statusbar.phone.UserAvatarView android:id="@+id/user_picture"
             android:layout_width="@dimen/framed_avatar_size"
@@ -47,4 +47,4 @@
             sysui:badgeDiameter="18dp"
             sysui:badgeMargin="1dp"
             sysui:frameColor="@color/qs_user_detail_avatar_frame" />
-</com.android.systemui.qs.tiles.UserDetailItemView>
+</com.android.systemui.statusbar.policy.KeyguardUserDetailItemView>
diff --git a/packages/SystemUI/res/layout/notification_conversation_info.xml b/packages/SystemUI/res/layout/notification_conversation_info.xml
index a9d6e35..8460612 100644
--- a/packages/SystemUI/res/layout/notification_conversation_info.xml
+++ b/packages/SystemUI/res/layout/notification_conversation_info.xml
@@ -28,7 +28,7 @@
     android:paddingStart="@*android:dimen/notification_content_margin_start">
 
     <!-- Package Info -->
-    <RelativeLayout
+    <LinearLayout
         android:id="@+id/header"
         android:layout_width="match_parent"
         android:layout_height="@dimen/notification_guts_conversation_header_height"
@@ -41,16 +41,20 @@
             android:layout_height="@dimen/notification_guts_conversation_icon_size"
             android:layout_centerVertical="true"
             android:layout_alignParentStart="true"
-            android:layout_marginEnd="6dp" />
+            android:layout_marginEnd="15dp" />
         <LinearLayout
             android:id="@+id/names"
+            android:layout_weight="1"
+            android:layout_width="0dp"
             android:orientation="vertical"
-            android:layout_width="wrap_content"
+
             android:layout_height="wrap_content"
             android:minHeight="@dimen/notification_guts_conversation_icon_size"
             android:layout_centerVertical="true"
             android:gravity="center_vertical"
-            android:layout_toEndOf="@id/conversation_icon">
+            android:layout_alignEnd="@id/conversation_icon"
+            android:layout_toEndOf="@id/conversation_icon"
+            android:layout_alignStart="@id/mute">
             <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
@@ -107,67 +111,40 @@
                     android:layout_weight="1"
                     style="@style/TextAppearance.NotificationImportanceChannel"/>
             </LinearLayout>
+            <TextView
+                android:id="@+id/delegate_name"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_centerVertical="true"
+                style="@style/TextAppearance.NotificationImportanceHeader"
+                android:layout_marginStart="2dp"
+                android:layout_marginEnd="2dp"
+                android:ellipsize="end"
+                android:text="@string/notification_delegate_header"
+                android:layout_toEndOf="@id/pkg_divider"
+                android:maxLines="1" />
 
         </LinearLayout>
 
-        <TextView
-            android:id="@+id/pkg_divider"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_centerVertical="true"
-            style="@style/TextAppearance.NotificationImportanceHeader"
-            android:layout_marginStart="2dp"
-            android:layout_marginEnd="2dp"
-            android:layout_toEndOf="@id/name"
-            android:text="@*android:string/notification_header_divider_symbol" />
-        <TextView
-            android:id="@+id/delegate_name"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_centerVertical="true"
-            style="@style/TextAppearance.NotificationImportanceHeader"
-            android:layout_marginStart="2dp"
-            android:layout_marginEnd="2dp"
-            android:ellipsize="end"
-            android:text="@string/notification_delegate_header"
-            android:layout_toEndOf="@id/pkg_divider"
-            android:maxLines="1" />
-
         <!-- end aligned fields -->
         <ImageButton
-            android:id="@+id/demote"
+            android:id="@+id/mute"
             android:layout_width="@dimen/notification_importance_toggle_size"
             android:layout_height="@dimen/notification_importance_toggle_size"
             android:layout_centerVertical="true"
             android:background="@drawable/ripple_drawable"
-            android:contentDescription="@string/demote"
-            android:src="@drawable/ic_demote_conversation"
-            android:layout_toStartOf="@id/app_settings"
-            android:tint="@color/notification_guts_link_icon_tint"/>
-        <!-- Optional link to app. Only appears if the channel is not disabled and the app
-asked for it -->
-        <ImageButton
-            android:id="@+id/app_settings"
-            android:layout_width="@dimen/notification_importance_toggle_size"
-            android:layout_height="@dimen/notification_importance_toggle_size"
-            android:layout_centerVertical="true"
-            android:visibility="gone"
-            android:background="@drawable/ripple_drawable"
-            android:contentDescription="@string/notification_app_settings"
-            android:src="@drawable/ic_info"
-            android:layout_toStartOf="@id/info"
+            android:layout_toStartOf="@id/fave"
             android:tint="@color/notification_guts_link_icon_tint"/>
         <ImageButton
-            android:id="@+id/info"
+            android:id="@+id/fave"
             android:layout_width="@dimen/notification_importance_toggle_size"
             android:layout_height="@dimen/notification_importance_toggle_size"
             android:layout_centerVertical="true"
             android:background="@drawable/ripple_drawable"
-            android:contentDescription="@string/notification_more_settings"
-            android:src="@drawable/ic_settings"
             android:layout_alignParentEnd="true"
             android:tint="@color/notification_guts_link_icon_tint"/>
-    </RelativeLayout>
+
+    </LinearLayout>
 
     <LinearLayout
         android:id="@+id/actions"
@@ -182,6 +159,23 @@
             android:layout_width="match_parent"
             android:layout_height="0.5dp"
             android:background="@color/GM2_grey_300" />
+
+        <Button
+            android:id="@+id/snooze"
+            android:layout_height="@dimen/notification_guts_conversation_action_height"
+            android:layout_width="match_parent"
+            style="?android:attr/borderlessButtonStyle"
+            android:text="@string/notification_menu_snooze_action"
+            android:gravity="left|center_vertical"
+            android:drawableStart="@drawable/ic_snooze"
+            android:drawablePadding="@dimen/notification_guts_conversation_action_text_padding_start"
+            android:drawableTint="@color/notification_guts_link_icon_tint"/>
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="0.5dp"
+            android:background="@color/GM2_grey_300" />
+
         <Button
             android:id="@+id/bubble"
             android:layout_height="@dimen/notification_guts_conversation_action_height"
@@ -212,42 +206,15 @@
             android:layout_width="match_parent"
             android:layout_height="0.5dp"
             android:background="@color/GM2_grey_300" />
-        <Button
-            android:id="@+id/fave"
-            android:layout_height="@dimen/notification_guts_conversation_action_height"
-            android:layout_width="match_parent"
-            style="?android:attr/borderlessButtonStyle"
-            android:gravity="left|center_vertical"
-            android:drawablePadding="@dimen/notification_guts_conversation_action_text_padding_start"
-            android:drawableTint="@color/notification_guts_link_icon_tint"/>
 
-        <View
-            android:layout_width="match_parent"
-            android:layout_height="0.5dp"
-            android:background="@color/GM2_grey_300" />
         <Button
-            android:id="@+id/snooze"
+            android:id="@+id/info"
             android:layout_height="@dimen/notification_guts_conversation_action_height"
             android:layout_width="match_parent"
             style="?android:attr/borderlessButtonStyle"
-            android:text="@string/notification_menu_snooze_action"
+            android:drawableStart="@drawable/ic_settings"
+            android:text="@string/notification_menu_settings_action"
             android:gravity="left|center_vertical"
-            android:drawableStart="@drawable/ic_snooze"
-            android:drawablePadding="@dimen/notification_guts_conversation_action_text_padding_start"
-            android:drawableTint="@color/notification_guts_link_icon_tint"/>
-
-        <View
-            android:layout_width="match_parent"
-            android:layout_height="0.5dp"
-            android:background="@color/GM2_grey_300" />
-        <Button
-            android:id="@+id/mute"
-            android:layout_height="@dimen/notification_guts_conversation_action_height"
-            android:layout_width="match_parent"
-            style="?android:attr/borderlessButtonStyle"
-            android:text="@string/notification_conversation_mute"
-            android:gravity="left|center_vertical"
-            android:drawableStart="@drawable/ic_notifications_silence"
             android:drawablePadding="@dimen/notification_guts_conversation_action_text_padding_start"
             android:drawableTint="@color/notification_guts_link_icon_tint"/>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0db7d67..ec56c1f 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -977,6 +977,9 @@
          Equal to pip_action_size - pip_action_padding. -->
     <dimen name="pip_expand_container_edge_margin">30dp</dimen>
 
+    <!-- The touchable/draggable edge size for PIP resize. -->
+    <dimen name="pip_resize_edge_size">30dp</dimen>
+
     <dimen name="default_gear_space">18dp</dimen>
     <dimen name="cell_overlay_padding">18dp</dimen>
 
@@ -1225,4 +1228,6 @@
     <!-- Screen Record -->
     <dimen name="screenrecord_dialog_padding">18dp</dimen>
     <dimen name="screenrecord_logo_size">24dp</dimen>
+
+    <dimen name="kg_user_switcher_text_size">16sp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b85b51e..d596a5d 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -487,6 +487,9 @@
     <!-- Content description of the data connection type 5Ge. [CHAR LIMIT=NONE] -->
     <string name="data_connection_5ge" translatable="false">5Ge</string>
 
+    <!-- Content description of the data connection type 5Ge with HTML styling. DO NOT TRANSLATE [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5ge_html" translate="false"> &lt;i>5G &lt;small>E&lt;/small>&lt;/i> </string>
+
     <!-- Content description of the data connection type 5G. [CHAR LIMIT=NONE] -->
     <string name="data_connection_5g" translatable="false">5G</string>
 
@@ -1195,6 +1198,9 @@
     <!-- The text for the manage notifications link. [CHAR LIMIT=40] -->
     <string name="manage_notifications_text">Manage</string>
 
+    <!-- The text for the notification history link. [CHAR LIMIT=40] -->
+    <string name="manage_notifications_history_text">History</string>
+
     <!-- Section title for notifications that do not vibrate or make noise. [CHAR LIMIT=40] -->
     <string name="notification_section_header_gentle">Silent notifications</string>
 
@@ -1829,23 +1835,23 @@
     <!-- Notification: Conversation: control panel, label for button that demotes notification from conversation to normal notification -->
     <string name="demote">Mark this notification as not a conversation</string>
 
-    <!-- [CHAR LIMIT=100] Mark this conversation as a favorite -->
-    <string name="notification_conversation_favorite">Mark as important</string>
+    <!-- [CHAR LIMIT=100] This conversation is marked as important -->
+    <string name="notification_conversation_favorite">Important conversation</string>
 
-    <!-- [CHAR LIMIT=100] Unmark this conversation as a favorite -->
-    <string name="notification_conversation_unfavorite">Mark as unimportant</string>
+    <!-- [CHAR LIMIT=100] This conversation is not marked as important -->
+    <string name="notification_conversation_unfavorite">Not an important conversation</string>
 
-    <!-- [CHAR LIMIT=100] Mute this conversation -->
-    <string name="notification_conversation_mute">Silence</string>
+    <!-- [CHAR LIMIT=100] This conversation is silenced (will not make sound or vibrate)-->
+    <string name="notification_conversation_mute">Silenced</string>
 
-    <!-- [CHAR LIMIT=100] Umute this conversation -->
+    <!-- [CHAR LIMIT=100] This conversation is alerting (may make sound and/or vibrate)-->
     <string name="notification_conversation_unmute">Alerting</string>
 
     <!-- [CHAR LIMIT=100] Show notification as bubble -->
-    <string name="notification_conversation_bubble">Show as bubble</string>
+    <string name="notification_conversation_bubble">Show bubble</string>
 
     <!-- [CHAR LIMIT=100] Turn off bubbles for notification -->
-    <string name="notification_conversation_unbubble">Turn off bubbles</string>
+    <string name="notification_conversation_unbubble">Remove bubbles</string>
 
     <!-- [CHAR LIMIT=100] Add this conversation to home screen -->
     <string name="notification_conversation_home_screen">Add to home screen</string>
@@ -1860,7 +1866,10 @@
     <string name="notification_menu_snooze_description">notification snooze options</string>
 
     <!-- Notification: Menu row: Label for the snooze action shown in local context menu. [CHAR LIMIT=NONE] -->
-    <string name="notification_menu_snooze_action">Snooze</string>
+    <string name="notification_menu_snooze_action">Remind me</string>
+
+    <!-- Notification: Menu row: Label for the snooze action shown in local context menu. [CHAR LIMIT=NONE] -->
+    <string name="notification_menu_settings_action">Settings</string>
 
     <!-- Notification: Snooze panel: Snooze undo button label. [CHAR LIMIT=50]-->
     <string name="snooze_undo">UNDO</string>
@@ -2029,6 +2038,9 @@
     <!-- Label for feature switch [CHAR LIMIT=30] -->
     <string name="switch_bar_off">Off</string>
 
+    <!-- The tile in quick settings is unavailable. [CHAR LIMIT=32] -->
+    <string name="tile_unavailable">Unavailable</string>
+
     <!-- SysUI Tuner: Button that leads to the navigation bar customization screen [CHAR LIMIT=60] -->
     <string name="nav_bar">Navigation bar</string>
 
@@ -2583,6 +2595,4 @@
     <string name="controls_favorite_default_title">Controls</string>
     <!-- Controls management controls screen subtitle [CHAR LIMIT=NONE] -->
     <string name="controls_favorite_subtitle">Choose controls for quick access</string>
-
-
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index bcffa8d..6fedac0 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -129,11 +129,10 @@
             parent="TextAppearance.StatusBar.Expanded.AboveDateTime" />
 
     <style name="TextAppearance.StatusBar.Expanded.UserSwitcher">
-        <item name="android:textSize">16sp</item>
+        <item name="android:textSize">@dimen/kg_user_switcher_text_size</item>
         <item name="android:textStyle">normal</item>
         <item name="android:textColor">?android:attr/textColorSecondary</item>
     </style>
-    <style name="TextAppearance.StatusBar.Expanded.UserSwitcher.UserName" />
 
     <style name="TextAppearance" />
 
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index e50d08c..03bc738 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -18,12 +18,14 @@
 
 import android.app.ActivityManager;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.HandlerThread;
 import android.os.Trace;
 import android.service.wallpaper.WallpaperService;
 import android.util.Log;
 import android.util.Size;
+import android.view.DisplayInfo;
 import android.view.SurfaceHolder;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -93,14 +95,20 @@
         private StatusBarStateController mController;
         private final Runnable mFinishRenderingTask = this::finishRendering;
         private final boolean mNeedTransition;
+        private boolean mShouldStopTransition;
+        @VisibleForTesting
+        final boolean mIsHighEndGfx;
+        private final boolean mDisplayNeedsBlanking;
+        private final DisplayInfo mDisplayInfo = new DisplayInfo();
         private final Object mMonitor = new Object();
         private boolean mNeedRedraw;
         // This variable can only be accessed in synchronized block.
         private boolean mWaitingForRendering;
 
         GLEngine(Context context, DozeParameters dozeParameters) {
-            mNeedTransition = ActivityManager.isHighEndGfx()
-                    && !dozeParameters.getDisplayNeedsBlanking();
+            mIsHighEndGfx = ActivityManager.isHighEndGfx();
+            mDisplayNeedsBlanking = dozeParameters.getDisplayNeedsBlanking();
+            mNeedTransition = mIsHighEndGfx && !mDisplayNeedsBlanking;
 
             // We will preserve EGL context when we are in lock screen or aod
             // to avoid janking in following transition, we need to release when back to home.
@@ -112,14 +120,23 @@
 
         @Override
         public void onCreate(SurfaceHolder surfaceHolder) {
-            mEglHelper = new EglHelper();
+            mEglHelper = getEglHelperInstance();
             // Deferred init renderer because we need to get wallpaper by display context.
-            mRenderer = new ImageWallpaperRenderer(getDisplayContext(), this /* SurfaceProxy */);
+            mRenderer = getRendererInstance();
+            getDisplayContext().getDisplay().getDisplayInfo(mDisplayInfo);
             setFixedSizeAllowed(true);
             setOffsetNotificationsEnabled(true);
             updateSurfaceSize();
         }
 
+        EglHelper getEglHelperInstance() {
+            return new EglHelper();
+        }
+
+        ImageWallpaperRenderer getRendererInstance() {
+            return new ImageWallpaperRenderer(getDisplayContext(), this /* SurfaceProxy */);
+        }
+
         private void updateSurfaceSize() {
             SurfaceHolder holder = getSurfaceHolder();
             Size frameSize = mRenderer.reportSurfaceSize();
@@ -128,6 +145,26 @@
             holder.setFixedSize(width, height);
         }
 
+        /**
+         * Check if necessary to stop transition with current wallpaper on this device. <br/>
+         * This should only be invoked after {@link #onSurfaceCreated(SurfaceHolder)}}
+         * is invoked since it needs display context and surface frame size.
+         * @return true if need to stop transition.
+         */
+        @VisibleForTesting
+        boolean checkIfShouldStopTransition() {
+            int orientation = getDisplayContext().getResources().getConfiguration().orientation;
+            Rect frame = getSurfaceHolder().getSurfaceFrame();
+            Rect display = new Rect();
+            if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+                display.set(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
+            } else {
+                display.set(0, 0, mDisplayInfo.logicalHeight, mDisplayInfo.logicalWidth);
+            }
+            return mNeedTransition
+                    && (frame.width() < display.width() || frame.height() < display.height());
+        }
+
         @Override
         public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep,
                 float yOffsetStep, int xPixelOffset, int yPixelOffset) {
@@ -137,12 +174,14 @@
         @Override
         public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
             if (!mNeedTransition) return;
+            final long duration = mShouldStopTransition ? 0 : animationDuration;
             if (DEBUG) {
                 Log.d(TAG, "onAmbientModeChanged: inAmbient=" + inAmbientMode
-                        + ", duration=" + animationDuration);
+                        + ", duration=" + duration
+                        + ", mShouldStopTransition=" + mShouldStopTransition);
             }
             mWorker.getThreadHandler().post(
-                    () -> mRenderer.updateAmbientMode(inAmbientMode, animationDuration));
+                    () -> mRenderer.updateAmbientMode(inAmbientMode, duration));
             if (inAmbientMode && animationDuration == 0) {
                 // This means that we are transiting from home to aod, to avoid
                 // race condition between window visibility and transition,
@@ -183,6 +222,7 @@
 
         @Override
         public void onSurfaceCreated(SurfaceHolder holder) {
+            mShouldStopTransition = checkIfShouldStopTransition();
             mWorker.getThreadHandler().post(() -> {
                 mEglHelper.init(holder, needSupportWideColorGamut());
                 mRenderer.onSurfaceCreated();
@@ -348,15 +388,13 @@
         protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
             super.dump(prefix, fd, out, args);
             out.print(prefix); out.print("Engine="); out.println(this);
-
-            boolean isHighEndGfx = ActivityManager.isHighEndGfx();
-            out.print(prefix); out.print("isHighEndGfx="); out.println(isHighEndGfx);
-
+            out.print(prefix); out.print("isHighEndGfx="); out.println(mIsHighEndGfx);
             out.print(prefix); out.print("displayNeedsBlanking=");
-            out.println(
-                    mDozeParameters != null ? mDozeParameters.getDisplayNeedsBlanking() : "null");
-
+            out.println(mDisplayNeedsBlanking);
+            out.print(prefix); out.print("displayInfo="); out.print(mDisplayInfo);
             out.print(prefix); out.print("mNeedTransition="); out.println(mNeedTransition);
+            out.print(prefix); out.print("mShouldStopTransition=");
+            out.println(mShouldStopTransition);
             out.print(prefix); out.print("StatusBarState=");
             out.println(mController != null ? mController.getState() : "null");
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index bea55c8..2d55a1d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -146,7 +146,6 @@
             int viewType) {
         BadgedImageView view = (BadgedImageView) LayoutInflater.from(parent.getContext())
                 .inflate(R.layout.bubble_view, parent, false);
-        view.setPadding(15, 15, 15, 15);
         return new ViewHolder(view);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
index 099909d..e105795a 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
@@ -35,7 +35,7 @@
     private boolean mIsReportingEnabled;
 
     @Override
-    public void onSucccessfulUnlock() {
+    public void onSuccessfulUnlock() {
 
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
index d6faed5..6a64c83 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
@@ -375,7 +375,7 @@
         sessionExitpoint(false /* force */);
     }
 
-    public void onSucccessfulUnlock() {
+    public void onSuccessfulUnlock() {
         if (FalsingLog.ENABLED) {
             FalsingLog.i("onSucccessfulUnlock", "");
         }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index b2131e7..0db9ffc 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -25,9 +25,12 @@
 import android.util.DisplayMetrics;
 import android.view.MotionEvent;
 
+import androidx.annotation.NonNull;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.Dependency;
+import com.android.systemui.DumpController;
+import com.android.systemui.Dumpable;
 import com.android.systemui.classifier.brightline.BrightLineFalsingManager;
 import com.android.systemui.classifier.brightline.FalsingDataProvider;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -40,6 +43,7 @@
 import com.android.systemui.util.DeviceConfigProxy;
 import com.android.systemui.util.sensors.ProximitySensor;
 
+import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.concurrent.Executor;
 
@@ -52,7 +56,7 @@
  * {@link FalsingManagerImpl} is used when a Plugin is not loaded.
  */
 @Singleton
-public class FalsingManagerProxy implements FalsingManager {
+public class FalsingManagerProxy implements FalsingManager, Dumpable {
 
     private static final String PROXIMITY_SENSOR_TAG = "FalsingManager";
 
@@ -63,16 +67,20 @@
     private final DeviceConfigProxy mDeviceConfig;
     private boolean mBrightlineEnabled;
     private final DockManager mDockManager;
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private Executor mUiBgExecutor;
 
     @Inject
     FalsingManagerProxy(Context context, PluginManager pluginManager, @Main Executor executor,
             DisplayMetrics displayMetrics, ProximitySensor proximitySensor,
             DeviceConfigProxy deviceConfig, DockManager dockManager,
+            KeyguardUpdateMonitor keyguardUpdateMonitor,
+            DumpController dumpController,
             @UiBackground Executor uiBgExecutor) {
         mDisplayMetrics = displayMetrics;
         mProximitySensor = proximitySensor;
         mDockManager = dockManager;
+        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mUiBgExecutor = uiBgExecutor;
         mProximitySensor.setTag(PROXIMITY_SENSOR_TAG);
         mProximitySensor.setSensorDelay(SensorManager.SENSOR_DELAY_GAME);
@@ -101,6 +109,8 @@
         };
 
         pluginManager.addPluginListener(mPluginListener, FalsingPlugin.class);
+
+        dumpController.registerDumpable("FalsingManager", this);
     }
 
     private void onDeviceConfigPropertiesChanged(Context context, String namespace) {
@@ -130,7 +140,7 @@
         } else {
             mInternalFalsingManager = new BrightLineFalsingManager(
                     new FalsingDataProvider(mDisplayMetrics),
-                    Dependency.get(KeyguardUpdateMonitor.class),
+                    mKeyguardUpdateMonitor,
                     mProximitySensor,
                     mDeviceConfig,
                     mDockManager
@@ -147,8 +157,8 @@
     }
 
     @Override
-    public void onSucccessfulUnlock() {
-        mInternalFalsingManager.onSucccessfulUnlock();
+    public void onSuccessfulUnlock() {
+        mInternalFalsingManager.onSuccessfulUnlock();
     }
 
     @Override
@@ -322,6 +332,11 @@
     }
 
     @Override
+    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+        mInternalFalsingManager.dump(pw);
+    }
+
+    @Override
     public void dump(PrintWriter pw) {
         mInternalFalsingManager.dump(pw);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
index b2e61a2..2f3e336 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
@@ -21,6 +21,7 @@
 
 import android.hardware.biometrics.BiometricSourceType;
 import android.net.Uri;
+import android.os.Build;
 import android.util.Log;
 import android.view.MotionEvent;
 
@@ -40,15 +41,19 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Queue;
+import java.util.StringJoiner;
+import java.util.stream.Collectors;
 
 /**
  * FalsingManager designed to make clear why a touch was rejected.
  */
 public class BrightLineFalsingManager implements FalsingManager {
 
-    static final boolean DEBUG = false;
     private static final String TAG = "FalsingManager";
-    private static final int RECENT_INFO_LOG_SIZE = 20;
+    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private static final int RECENT_INFO_LOG_SIZE = 40;
+    private static final int RECENT_SWIPE_LOG_SIZE = 20;
 
     private final FalsingDataProvider mDataProvider;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -62,6 +67,8 @@
     private boolean mJustUnlockedWithFace;
     private static final Queue<String> RECENT_INFO_LOG =
             new ArrayDeque<>(RECENT_INFO_LOG_SIZE + 1);
+    private static final Queue<DebugSwipeRecord> RECENT_SWIPES =
+            new ArrayDeque<>(RECENT_SWIPE_LOG_SIZE + 1);
 
     private final List<FalsingClassifier> mClassifiers;
 
@@ -78,6 +85,7 @@
                     }
                 }
             };
+    private boolean mPreviousResult = false;
 
     public BrightLineFalsingManager(FalsingDataProvider falsingDataProvider,
             KeyguardUpdateMonitor keyguardUpdateMonitor, ProximitySensor proximitySensor,
@@ -138,7 +146,7 @@
 
     private void updateInteractionType(@Classifier.InteractionType int type) {
         logDebug("InteractionType: " + type);
-        mClassifiers.forEach((classifier) -> classifier.setInteractionType(type));
+        mDataProvider.setInteractionType(type);
     }
 
     @Override
@@ -148,7 +156,11 @@
 
     @Override
     public boolean isFalseTouch() {
-        boolean r = !mJustUnlockedWithFace && !mDockManager.isDocked()
+        if (!mDataProvider.isDirty()) {
+            return mPreviousResult;
+        }
+
+        mPreviousResult = !mJustUnlockedWithFace && !mDockManager.isDocked()
                 && mClassifiers.stream().anyMatch(falsingClassifier -> {
                     boolean result = falsingClassifier.isFalseTouch();
                     if (result) {
@@ -167,9 +179,26 @@
                     return result;
                 });
 
-        logDebug("Is false touch? " + r);
+        logDebug("Is false touch? " + mPreviousResult);
 
-        return r;
+        if (Build.IS_ENG || Build.IS_USERDEBUG) {
+            // Copy motion events, as the passed in list gets emptied out elsewhere in the code.
+            RECENT_SWIPES.add(new DebugSwipeRecord(
+                    mPreviousResult,
+                    mDataProvider.getInteractionType(),
+                    mDataProvider.getRecentMotionEvents().stream().map(
+                            motionEvent -> new XYDt(
+                                    (int) motionEvent.getX(),
+                                    (int) motionEvent.getY(),
+                                    (int) (motionEvent.getEventTime() - motionEvent.getDownTime())))
+                            .collect(Collectors.toList())));
+            while (RECENT_SWIPES.size() > RECENT_INFO_LOG_SIZE) {
+                DebugSwipeRecord record = RECENT_SWIPES.remove();
+            }
+
+        }
+
+        return mPreviousResult;
     }
 
     @Override
@@ -187,7 +216,7 @@
     }
 
     @Override
-    public void onSucccessfulUnlock() {
+    public void onSuccessfulUnlock() {
         if (mIsFalseTouchCalls != 0) {
             mMetricsLogger.histogram(FALSING_SUCCESS, mIsFalseTouchCalls);
             mIsFalseTouchCalls = 0;
@@ -212,7 +241,6 @@
     @Override
     public void onNotificatonStartDraggingDown() {
         updateInteractionType(Classifier.NOTIFICATION_DRAG_DOWN);
-
     }
 
     @Override
@@ -235,7 +263,12 @@
     }
 
     @Override
-    public void setQsExpanded(boolean b) {
+    public void setQsExpanded(boolean expanded) {
+        if (expanded) {
+            unregisterSensors();
+        } else if (mSessionStarted) {
+            registerSensors();
+        }
     }
 
     @Override
@@ -338,25 +371,49 @@
 
     @Override
     public void onBouncerShown() {
+        unregisterSensors();
     }
 
     @Override
     public void onBouncerHidden() {
+        if (mSessionStarted) {
+            registerSensors();
+        }
     }
 
     @Override
     public void dump(PrintWriter pw) {
         IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
         ipw.println("BRIGHTLINE FALSING MANAGER");
-        ipw.print("classifierEnabled="); pw.println(isClassifierEnabled() ? 1 : 0);
-        ipw.print("mJustUnlockedWithFace="); pw.println(mJustUnlockedWithFace ? 1 : 0);
-        ipw.print("isDocked="); pw.println(mDockManager.isDocked() ? 1 : 0);
+        ipw.print("classifierEnabled=");
+        ipw.println(isClassifierEnabled() ? 1 : 0);
+        ipw.print("mJustUnlockedWithFace=");
+        ipw.println(mJustUnlockedWithFace ? 1 : 0);
+        ipw.print("isDocked=");
+        ipw.println(mDockManager.isDocked() ? 1 : 0);
+        ipw.print("width=");
+        ipw.println(mDataProvider.getWidthPixels());
+        ipw.print("height=");
+        ipw.println(mDataProvider.getHeightPixels());
+        ipw.println();
+        if (RECENT_SWIPES.size() != 0) {
+            ipw.println("Recent swipes:");
+            ipw.increaseIndent();
+            for (DebugSwipeRecord record : RECENT_SWIPES) {
+                ipw.println(record.getString());
+                ipw.println();
+            }
+            ipw.decreaseIndent();
+        } else {
+            ipw.println("No recent swipes");
+        }
         ipw.println();
         ipw.println("Recent falsing info:");
         ipw.increaseIndent();
         for (String msg : RECENT_INFO_LOG) {
             ipw.println(msg);
         }
+        ipw.println();
     }
 
     @Override
@@ -386,4 +443,46 @@
     static void logError(String msg) {
         Log.e(TAG, msg);
     }
+
+    private static class DebugSwipeRecord {
+        private static final byte VERSION = 1;  // opaque version number indicating format of data.
+        private final boolean mIsFalse;
+        private final int mInteractionType;
+        private final List<XYDt> mRecentMotionEvents;
+
+        DebugSwipeRecord(boolean isFalse, int interactionType,
+                List<XYDt> recentMotionEvents) {
+            mIsFalse = isFalse;
+            mInteractionType = interactionType;
+            mRecentMotionEvents = recentMotionEvents;
+        }
+
+        String getString() {
+            StringJoiner sj = new StringJoiner(",");
+            sj.add(Integer.toString(VERSION))
+                    .add(mIsFalse ? "1" : "0")
+                    .add(Integer.toString(mInteractionType));
+            for (XYDt event : mRecentMotionEvents) {
+                sj.add(event.toString());
+            }
+            return sj.toString();
+        }
+    }
+
+    private static class XYDt {
+        private final int mX;
+        private final int mY;
+        private final int mDT;
+
+        XYDt(int x, int y, int dT) {
+            mX = x;
+            mY = y;
+            mDT = dT;
+        }
+
+        @Override
+        public String toString() {
+            return mX + "," + mY + "," + mDT;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
index 7555051..cf08821 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
@@ -85,10 +85,6 @@
         return mDataProvider.getInteractionType();
     }
 
-    final void setInteractionType(@Classifier.InteractionType int interactionType) {
-        mDataProvider.setInteractionType(interactionType);
-    }
-
     /**
      * Called whenever a MotionEvent occurs.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
index 8b11ceb..5494c64 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
@@ -113,6 +113,10 @@
         this.mInteractionType = interactionType;
     }
 
+    public boolean isDirty() {
+        return mDirty;
+    }
+
     final int getInteractionType() {
         return mInteractionType;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt
index 53841e2..49a16d8 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt
@@ -20,6 +20,6 @@
 
 data class ControlStatus(
     val control: Control,
-    val favorite: Boolean,
+    var favorite: Boolean,
     val removed: Boolean = false
 )
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
index b3ba2b2..fce5041 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -26,10 +26,18 @@
     val available: Boolean
 
     fun getFavoriteControls(): List<ControlInfo>
-    fun loadForComponent(componentName: ComponentName, callback: (List<ControlStatus>) -> Unit)
+    fun loadForComponent(
+        componentName: ComponentName,
+        callback: (List<ControlStatus>, List<String>) -> Unit
+    )
+
     fun subscribeToFavorites()
     fun changeFavoriteStatus(controlInfo: ControlInfo, state: Boolean)
-    fun countFavoritesForComponent(componentName: ComponentName): Int = 0
+    fun replaceFavoritesForComponent(componentName: ComponentName, favorites: List<ControlInfo>)
+
+    fun getFavoritesForComponent(componentName: ComponentName): List<ControlInfo>
+    fun countFavoritesForComponent(componentName: ComponentName): Int
+
     fun unsubscribe()
     fun action(controlInfo: ControlInfo, action: ControlAction)
     fun refreshStatus(componentName: ComponentName, control: Control)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index 7de1557..e611197 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -70,9 +70,10 @@
     }
 
     // Map of map: ComponentName -> (String -> ControlInfo).
-    // Only for current user
+    //
     @GuardedBy("currentFavorites")
-    private val currentFavorites = ArrayMap<ComponentName, MutableMap<String, ControlInfo>>()
+    private val currentFavorites = ArrayMap<ComponentName, MutableList<ControlInfo>>()
+            .withDefault { mutableListOf() }
 
     private var userChanging: Boolean = true
 
@@ -180,15 +181,14 @@
         val infos = persistenceWrapper.readFavorites()
         synchronized(currentFavorites) {
             infos.forEach {
-                currentFavorites.getOrPut(it.component, { ArrayMap<String, ControlInfo>() })
-                        .put(it.controlId, it)
+                currentFavorites.getOrPut(it.component, { mutableListOf() }).add(it)
             }
         }
     }
 
     override fun loadForComponent(
         componentName: ComponentName,
-        callback: (List<ControlStatus>) -> Unit
+        callback: (List<ControlStatus>, List<String>) -> Unit
     ) {
         if (!confirmAvailability()) {
             if (userChanging) {
@@ -200,29 +200,34 @@
                         TimeUnit.MILLISECONDS
                 )
             } else {
-                callback(emptyList())
+                callback(emptyList(), emptyList())
             }
             return
         }
         bindingController.bindAndLoad(componentName) {
             synchronized(currentFavorites) {
-                val favoritesForComponentKeys: Set<String> =
-                        currentFavorites.get(componentName)?.keys ?: emptySet()
-                val changed = updateFavoritesLocked(componentName, it)
+                val favoritesForComponentKeys: List<String> =
+                        currentFavorites.getValue(componentName).map { it.controlId }
+                val changed = updateFavoritesLocked(componentName, it, favoritesForComponentKeys)
                 if (changed) {
                     persistenceWrapper.storeFavorites(favoritesAsListLocked())
                 }
-                val removed = findRemovedLocked(favoritesForComponentKeys, it)
-                callback(removed.map { currentFavorites.getValue(componentName).getValue(it) }
-                            .map(::createRemovedStatus) +
-                        it.map { ControlStatus(it, it.controlId in favoritesForComponentKeys) })
+                val removed = findRemovedLocked(favoritesForComponentKeys.toSet(), it)
+                val controlsWithFavorite =
+                        it.map { ControlStatus(it, it.controlId in favoritesForComponentKeys) }
+                callback(
+                        currentFavorites.getValue(componentName)
+                                .filter { it.controlId in removed }
+                                .map(::createRemovedStatus) + controlsWithFavorite,
+                        favoritesForComponentKeys
+                )
             }
         }
     }
 
     private fun createRemovedStatus(controlInfo: ControlInfo): ControlStatus {
         val intent = Intent(context, ControlsFavoritingActivity::class.java).apply {
-            putExtra(ControlsFavoritingActivity.EXTRA_COMPONENT, controlInfo.component)
+            putExtra(Intent.EXTRA_COMPONENT_NAME, controlInfo.component)
             flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP
         }
         val pendingIntent = PendingIntent.getActivity(context,
@@ -243,17 +248,24 @@
     }
 
     @GuardedBy("currentFavorites")
-    private fun updateFavoritesLocked(componentName: ComponentName, list: List<Control>): Boolean {
-        val favorites = currentFavorites.get(componentName) ?: mutableMapOf()
-        val favoriteKeys = favorites.keys
+    private fun updateFavoritesLocked(
+        componentName: ComponentName,
+        list: List<Control>,
+        favoriteKeys: List<String>
+    ): Boolean {
+        val favorites = currentFavorites.get(componentName) ?: mutableListOf()
         if (favoriteKeys.isEmpty()) return false // early return
         var changed = false
-        list.forEach {
-            if (it.controlId in favoriteKeys) {
-                val value = favorites.getValue(it.controlId)
-                if (value.controlTitle != it.title || value.deviceType != it.deviceType) {
-                    favorites[it.controlId] = value.copy(controlTitle = it.title,
-                            deviceType = it.deviceType)
+        list.forEach { control ->
+            if (control.controlId in favoriteKeys) {
+                val index = favorites.indexOfFirst { it.controlId == control.controlId }
+                val value = favorites[index]
+                if (value.controlTitle != control.title ||
+                        value.deviceType != control.deviceType) {
+                    favorites[index] = value.copy(
+                            controlTitle = control.title,
+                            deviceType = control.deviceType
+                    )
                     changed = true
                 }
             }
@@ -263,14 +275,14 @@
 
     @GuardedBy("currentFavorites")
     private fun favoritesAsListLocked(): List<ControlInfo> {
-        return currentFavorites.flatMap { it.value.values }
+        return currentFavorites.flatMap { it.value }
     }
 
     override fun subscribeToFavorites() {
         if (!confirmAvailability()) return
         // Make a copy of the favorites list
         val favorites = synchronized(currentFavorites) {
-            currentFavorites.flatMap { it.value.values.toList() }
+            currentFavorites.flatMap { it.value }
         }
         bindingController.subscribe(favorites)
     }
@@ -286,22 +298,19 @@
         val listOfControls = synchronized(currentFavorites) {
             if (state) {
                 if (controlInfo.component !in currentFavorites) {
-                    currentFavorites.put(controlInfo.component, ArrayMap<String, ControlInfo>())
+                    currentFavorites.put(controlInfo.component, mutableListOf())
                     changed = true
                 }
                 val controlsForComponent = currentFavorites.getValue(controlInfo.component)
-                if (controlInfo.controlId !in controlsForComponent) {
-                    controlsForComponent.put(controlInfo.controlId, controlInfo)
+                if (controlsForComponent.firstOrNull {
+                            it.controlId == controlInfo.controlId
+                        } == null) {
+                    controlsForComponent.add(controlInfo)
                     changed = true
-                } else {
-                    if (controlsForComponent.getValue(controlInfo.controlId) != controlInfo) {
-                        controlsForComponent.put(controlInfo.controlId, controlInfo)
-                        changed = true
-                    }
                 }
             } else {
                 changed = currentFavorites.get(controlInfo.component)
-                        ?.remove(controlInfo.controlId) != null
+                        ?.remove(controlInfo) != null
             }
             favoritesAsListLocked()
         }
@@ -310,6 +319,19 @@
         }
     }
 
+    override fun replaceFavoritesForComponent(
+        componentName: ComponentName,
+        favorites: List<ControlInfo>
+    ) {
+        if (!confirmAvailability()) return
+        val filtered = favorites.filter { it.component == componentName }
+        val listOfControls = synchronized(currentFavorites) {
+            currentFavorites.put(componentName, filtered.toMutableList())
+            favoritesAsListLocked()
+        }
+        persistenceWrapper.storeFavorites(listOfControls)
+    }
+
     override fun refreshStatus(componentName: ComponentName, control: Control) {
         if (!confirmAvailability()) {
             Log.d(TAG, "Controls not available")
@@ -317,7 +339,13 @@
         }
         executor.execute {
             synchronized(currentFavorites) {
-                val changed = updateFavoritesLocked(componentName, listOf(control))
+                val favoriteKeysForComponent =
+                        currentFavorites.get(componentName)?.map { it.controlId } ?: emptyList()
+                val changed = updateFavoritesLocked(
+                        componentName,
+                        listOf(control),
+                        favoriteKeysForComponent
+                )
                 if (changed) {
                     persistenceWrapper.storeFavorites(favoritesAsListLocked())
                 }
@@ -361,6 +389,12 @@
         }
     }
 
+    override fun getFavoritesForComponent(componentName: ComponentName): List<ControlInfo> {
+        return synchronized(currentFavorites) {
+            currentFavorites.get(componentName) ?: emptyList()
+        }
+    }
+
     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
         pw.println("ControlsController state:")
         pw.println("  Available: $available")
@@ -368,10 +402,8 @@
         pw.println("  Current user: ${currentUser.identifier}")
         pw.println("  Favorites:")
         synchronized(currentFavorites) {
-            currentFavorites.forEach {
-                it.value.forEach {
-                    pw.println("    ${it.value}")
-                }
+            favoritesAsListLocked().forEach {
+                pw.println("    ${ it }")
             }
         }
         pw.println(bindingController.toString())
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
index b122439..89caace 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
@@ -29,6 +29,7 @@
 import com.android.settingslib.applications.DefaultAppInfo
 import com.android.settingslib.widget.CandidateInfo
 import com.android.systemui.R
+import java.text.Collator
 import java.util.concurrent.Executor
 
 /**
@@ -44,23 +45,27 @@
  * @param onAppSelected a callback to indicate that an app has been selected in the list.
  */
 class AppAdapter(
+    backgroundExecutor: Executor,
     uiExecutor: Executor,
     lifecycle: Lifecycle,
     controlsListingController: ControlsListingController,
     private val layoutInflater: LayoutInflater,
     private val onAppSelected: (ComponentName?) -> Unit = {},
-    private val favoritesRenderer: FavoritesRenderer
+    private val favoritesRenderer: FavoritesRenderer,
+    private val resources: Resources
 ) : RecyclerView.Adapter<AppAdapter.Holder>() {
 
     private var listOfServices = emptyList<CandidateInfo>()
 
     private val callback = object : ControlsListingController.ControlsListingCallback {
         override fun onServicesUpdated(list: List<CandidateInfo>) {
-            uiExecutor.execute {
-                listOfServices = list.sortedBy {
-                    it.loadLabel().toString()
+            backgroundExecutor.execute {
+                val collator = Collator.getInstance(resources.getConfiguration().locale)
+                val localeComparator = compareBy<CandidateInfo, CharSequence>(collator) {
+                    it.loadLabel()
                 }
-                notifyDataSetChanged()
+                listOfServices = list.sortedWith(localeComparator)
+                uiExecutor.execute(::notifyDataSetChanged)
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
index 65dcc2b1..d3cabe6 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
@@ -25,101 +25,150 @@
 import android.widget.CheckBox
 import android.widget.ImageView
 import android.widget.TextView
+import androidx.recyclerview.widget.GridLayoutManager
 import androidx.recyclerview.widget.RecyclerView
 import com.android.systemui.R
-import com.android.systemui.controls.ControlStatus
-import com.android.systemui.controls.controller.ControlInfo
 import com.android.systemui.controls.ui.RenderInfo
 
+private typealias ModelFavoriteChanger = (String, Boolean) -> Unit
+
 /**
  * Adapter for binding [Control] information to views.
  *
+ * The model for this adapter is provided by a [FavoriteModel] that is set using
+ * [changeFavoritesModel]. This allows for updating the model if there's a reload.
+ *
  * @param layoutInflater an inflater for the views in the containing [RecyclerView]
- * @param favoriteCallback a callback to be called when the favorite status of a [Control] is
- *                         changed. The callback will take a [ControlInfo.Builder] that's
- *                         pre-populated with the [Control] information and the new favorite
- *                         status.
+ * @param onlyFavorites set to true to only display favorites instead of all controls
  */
 class ControlAdapter(
     private val layoutInflater: LayoutInflater,
-    private val favoriteCallback: (ControlInfo.Builder, Boolean) -> Unit
-) : RecyclerView.Adapter<ControlAdapter.Holder>() {
+    private val onlyFavorites: Boolean = false
+) : RecyclerView.Adapter<Holder>() {
 
-    var listOfControls = emptyList<ControlStatus>()
-
-    override fun onCreateViewHolder(parent: ViewGroup, i: Int): Holder {
-        return Holder(layoutInflater.inflate(R.layout.controls_base_item, parent, false).apply {
-            layoutParams.apply {
-                width = ViewGroup.LayoutParams.MATCH_PARENT
-            }
-            elevation = 15f
-        })
+    companion object {
+        private const val TYPE_ZONE = 0
+        private const val TYPE_CONTROL = 1
     }
 
-    override fun getItemCount() = listOfControls.size
+    val spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
+        override fun getSpanSize(position: Int): Int {
+            return if (getItemViewType(position) == TYPE_ZONE) 2 else 1
+        }
+    }
+
+    var modelList: List<ElementWrapper> = emptyList()
+    private var favoritesModel: FavoriteModel? = null
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
+        return when (viewType) {
+            TYPE_CONTROL -> {
+                ControlHolder(
+                        layoutInflater.inflate(R.layout.controls_base_item, parent, false).apply {
+                            layoutParams.apply {
+                                width = ViewGroup.LayoutParams.MATCH_PARENT
+                            }
+                            elevation = 15f
+                        },
+                        { id, favorite ->
+                            favoritesModel?.changeFavoriteStatus(id, favorite)
+                        })
+            }
+            TYPE_ZONE -> {
+                ZoneHolder(layoutInflater.inflate(R.layout.controls_zone_header, parent, false))
+            }
+            else -> throw IllegalStateException("Wrong viewType: $viewType")
+        }
+    }
+
+    fun changeFavoritesModel(favoritesModel: FavoriteModel) {
+        this.favoritesModel = favoritesModel
+        if (onlyFavorites) {
+            modelList = favoritesModel.favorites
+        } else {
+            modelList = favoritesModel.all
+        }
+        notifyDataSetChanged()
+    }
+
+    override fun getItemCount() = modelList.size
 
     override fun onBindViewHolder(holder: Holder, index: Int) {
-        holder.bindData(listOfControls[index], favoriteCallback)
+        holder.bindData(modelList[index])
     }
 
+    override fun getItemViewType(position: Int): Int {
+        return when (modelList[position]) {
+            is ZoneNameWrapper -> TYPE_ZONE
+            is ControlWrapper -> TYPE_CONTROL
+        }
+    }
+}
+
+/**
+ * Holder for binding views in the [RecyclerView]-
+ * @param view the [View] for this [Holder]
+ */
+sealed class Holder(view: View) : RecyclerView.ViewHolder(view) {
+
     /**
-     * Holder for binding views in the [RecyclerView]-
+     * Bind the data from the model into the view
      */
-    class Holder(view: View) : RecyclerView.ViewHolder(view) {
-        private val icon: ImageView = itemView.requireViewById(R.id.icon)
-        private val title: TextView = itemView.requireViewById(R.id.title)
-        private val subtitle: TextView = itemView.requireViewById(R.id.subtitle)
-        private val removed: TextView = itemView.requireViewById(R.id.status)
-        private val favorite: CheckBox = itemView.requireViewById<CheckBox>(R.id.favorite).apply {
-            visibility = View.VISIBLE
-        }
+    abstract fun bindData(wrapper: ElementWrapper)
+}
 
-        /**
-         * Bind data to the view
-         * @param data information about the [Control]
-         * @param callback a callback to be called when the favorite status of the [Control] is
-         *                 changed. The callback will take a [ControlInfo.Builder] that's
-         *                 pre-populated with the [Control] information and the new favorite status.
-         */
-        fun bindData(data: ControlStatus, callback: (ControlInfo.Builder, Boolean) -> Unit) {
-            val renderInfo = getRenderInfo(data.control.deviceType, data.favorite)
-            title.text = data.control.title
-            subtitle.text = data.control.subtitle
-            favorite.isChecked = data.favorite
-            removed.text = if (data.removed) "Removed" else ""
-            favorite.setOnClickListener {
-                val infoBuilder = ControlInfo.Builder().apply {
-                    controlId = data.control.controlId
-                    controlTitle = data.control.title
-                    deviceType = data.control.deviceType
-                }
-                callback(infoBuilder, favorite.isChecked)
-            }
-            itemView.setOnClickListener {
-                favorite.performClick()
-            }
-            applyRenderInfo(renderInfo)
-        }
+/**
+ * Holder for using with [ZoneNameWrapper] to display names of zones.
+ */
+private class ZoneHolder(view: View) : Holder(view) {
+    private val zone: TextView = itemView as TextView
 
-        private fun getRenderInfo(
-            @DeviceTypes.DeviceType deviceType: Int,
-            favorite: Boolean
-        ): RenderInfo {
-            return RenderInfo.lookup(deviceType, favorite)
-        }
+    override fun bindData(wrapper: ElementWrapper) {
+        wrapper as ZoneNameWrapper
+        zone.text = wrapper.zoneName
+    }
+}
 
-        private fun applyRenderInfo(ri: RenderInfo) {
-            val context = itemView.context
-            val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme())
-
-            icon.setImageIcon(Icon.createWithResource(context, ri.iconResourceId))
-            icon.setImageTintList(fg)
-        }
+/**
+ * Holder for using with [ControlWrapper] to display names of zones.
+ * @param favoriteCallback this callback will be called whenever the favorite state of the
+ *                         [Control] this view represents changes.
+ */
+private class ControlHolder(view: View, val favoriteCallback: ModelFavoriteChanger) : Holder(view) {
+    private val icon: ImageView = itemView.requireViewById(R.id.icon)
+    private val title: TextView = itemView.requireViewById(R.id.title)
+    private val subtitle: TextView = itemView.requireViewById(R.id.subtitle)
+    private val removed: TextView = itemView.requireViewById(R.id.status)
+    private val favorite: CheckBox = itemView.requireViewById<CheckBox>(R.id.favorite).apply {
+        visibility = View.VISIBLE
     }
 
-    fun setItems(list: List<ControlStatus>) {
-        listOfControls = list
-        notifyDataSetChanged()
+    override fun bindData(wrapper: ElementWrapper) {
+        wrapper as ControlWrapper
+        val data = wrapper.controlStatus
+        val renderInfo = getRenderInfo(data.control.deviceType)
+        title.text = data.control.title
+        subtitle.text = data.control.subtitle
+        favorite.isChecked = data.favorite
+        removed.text = if (data.removed) "Removed" else ""
+        favorite.setOnClickListener {
+            favoriteCallback(data.control.controlId, favorite.isChecked)
+        }
+        applyRenderInfo(renderInfo)
+    }
+
+    private fun getRenderInfo(
+        @DeviceTypes.DeviceType deviceType: Int
+    ): RenderInfo {
+        return RenderInfo.lookup(deviceType, true)
+    }
+
+    private fun applyRenderInfo(ri: RenderInfo) {
+        val context = itemView.context
+        val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme())
+
+        icon.setImageIcon(Icon.createWithResource(context, ri.iconResourceId))
+        icon.setImageTintList(fg)
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index be52583..1e52371 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -18,10 +18,14 @@
 
 import android.app.Activity
 import android.content.ComponentName
+import android.content.Intent
 import android.os.Bundle
 import android.view.LayoutInflater
+import android.view.ViewStub
+import android.widget.Button
 import android.widget.TextView
 import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.ItemTouchHelper
 import androidx.recyclerview.widget.RecyclerView
 import com.android.systemui.R
 import com.android.systemui.broadcast.BroadcastDispatcher
@@ -41,13 +45,36 @@
     companion object {
         private const val TAG = "ControlsFavoritingActivity"
         const val EXTRA_APP = "extra_app_label"
-        const val EXTRA_COMPONENT = "extra_component"
     }
 
-    private lateinit var recyclerView: RecyclerView
-    private lateinit var adapter: ControlAdapter
+    private lateinit var recyclerViewAll: RecyclerView
+    private lateinit var adapterAll: ControlAdapter
+    private lateinit var recyclerViewFavorites: RecyclerView
+    private lateinit var adapterFavorites: ControlAdapter
     private var component: ComponentName? = null
 
+    private var currentModel: FavoriteModel? = null
+    private var itemTouchHelperCallback = object : ItemTouchHelper.SimpleCallback(
+            /* dragDirs */ ItemTouchHelper.UP
+                    or ItemTouchHelper.DOWN
+                    or ItemTouchHelper.LEFT
+                    or ItemTouchHelper.RIGHT,
+            /* swipeDirs */0
+    ) {
+        override fun onMove(
+            recyclerView: RecyclerView,
+            viewHolder: RecyclerView.ViewHolder,
+            target: RecyclerView.ViewHolder
+        ): Boolean {
+            return currentModel?.onMoveItem(
+                    viewHolder.adapterPosition, target.adapterPosition) != null
+        }
+
+        override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {}
+
+        override fun isItemViewSwipeEnabled() = false
+    }
+
     private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
         private val startingUser = controller.currentUserId
 
@@ -62,41 +89,77 @@
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(R.layout.controls_management)
+        requireViewById<ViewStub>(R.id.stub).apply {
+            layoutResource = R.layout.controls_management_favorites
+            inflate()
+        }
 
         val app = intent.getCharSequenceExtra(EXTRA_APP)
-        component = intent.getParcelableExtra<ComponentName>(EXTRA_COMPONENT)
+        component = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME)
 
-        // If we have no component name, there's not much we can do.
-        val callback = component?.let {
-            { infoBuilder: ControlInfo.Builder, status: Boolean ->
-                infoBuilder.componentName = it
-                controller.changeFavoriteStatus(infoBuilder.build(), status)
-            }
-        } ?: { _, _ -> Unit }
-
-        recyclerView = requireViewById(R.id.list)
-        adapter = ControlAdapter(LayoutInflater.from(applicationContext), callback)
-        recyclerView.adapter = adapter
-        recyclerView.layoutManager = GridLayoutManager(applicationContext, 2)
-        val margin = resources.getDimensionPixelSize(R.dimen.controls_card_margin)
-        recyclerView.addItemDecoration(MarginItemDecorator(margin, margin))
+        setUpRecyclerViews()
 
         requireViewById<TextView>(R.id.title).text = app?.let { it }
                 ?: resources.getText(R.string.controls_favorite_default_title)
         requireViewById<TextView>(R.id.subtitle).text =
                 resources.getText(R.string.controls_favorite_subtitle)
 
-        currentUserTracker.startTracking()
-    }
+        requireViewById<Button>(R.id.done).setOnClickListener {
+            if (component == null) return@setOnClickListener
+            val favoritesForStorage = currentModel?.favorites?.map {
+                with(it.controlStatus.control) {
+                    ControlInfo(component!!, controlId, title, deviceType)
+                }
+            }
+            if (favoritesForStorage != null) {
+                controller.replaceFavoritesForComponent(component!!, favoritesForStorage)
+                finishAffinity()
+            }
+        }
 
-    override fun onResume() {
-        super.onResume()
         component?.let {
-            controller.loadForComponent(it) {
+            controller.loadForComponent(it) { allControls, favoriteKeys ->
                 executor.execute {
-                    adapter.setItems(it)
+                    val favoriteModel = FavoriteModel(
+                        allControls,
+                        favoriteKeys,
+                        allAdapter = adapterAll,
+                        favoritesAdapter = adapterFavorites)
+                    adapterAll.changeFavoritesModel(favoriteModel)
+                    adapterFavorites.changeFavoritesModel(favoriteModel)
+                    currentModel = favoriteModel
                 }
             }
         }
+
+        currentUserTracker.startTracking()
+    }
+
+    private fun setUpRecyclerViews() {
+        val margin = resources.getDimensionPixelSize(R.dimen.controls_card_margin)
+        val itemDecorator = MarginItemDecorator(margin, margin)
+        val layoutInflater = LayoutInflater.from(applicationContext)
+
+        adapterAll = ControlAdapter(layoutInflater)
+        recyclerViewAll = requireViewById<RecyclerView>(R.id.listAll).apply {
+            adapter = adapterAll
+            layoutManager = GridLayoutManager(applicationContext, 2).apply {
+                spanSizeLookup = adapterAll.spanSizeLookup
+            }
+            addItemDecoration(itemDecorator)
+        }
+
+        adapterFavorites = ControlAdapter(layoutInflater, true)
+        recyclerViewFavorites = requireViewById<RecyclerView>(R.id.listFavorites).apply {
+            layoutManager = GridLayoutManager(applicationContext, 2)
+            adapter = adapterFavorites
+            addItemDecoration(itemDecorator)
+        }
+        ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(recyclerViewFavorites)
+    }
+
+    override fun onDestroy() {
+        currentUserTracker.stopTracking()
+        super.onDestroy()
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
index 645e929..ad4bdef 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
@@ -20,6 +20,7 @@
 import android.content.Intent
 import android.os.Bundle
 import android.view.LayoutInflater
+import android.view.ViewStub
 import android.widget.TextView
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
@@ -63,11 +64,21 @@
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(R.layout.controls_management)
+        requireViewById<ViewStub>(R.id.stub).apply {
+            layoutResource = R.layout.controls_management_apps
+            inflate()
+        }
 
         recyclerView = requireViewById(R.id.list)
-        recyclerView.adapter = AppAdapter(executor, lifecycle, listingController,
-                LayoutInflater.from(this), ::launchFavoritingActivity,
-                FavoritesRenderer(resources, controlsController::countFavoritesForComponent))
+        recyclerView.adapter = AppAdapter(
+                backExecutor,
+                executor,
+                lifecycle,
+                listingController,
+                LayoutInflater.from(this),
+                ::launchFavoritingActivity,
+                FavoritesRenderer(resources, controlsController::countFavoritesForComponent),
+                resources)
         recyclerView.layoutManager = LinearLayoutManager(applicationContext)
 
         requireViewById<TextView>(R.id.title).text =
@@ -89,11 +100,16 @@
                         .apply {
                     putExtra(ControlsFavoritingActivity.EXTRA_APP,
                             listingController.getAppLabel(it))
-                    putExtra(ControlsFavoritingActivity.EXTRA_COMPONENT, it)
-                    flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP
+                    putExtra(Intent.EXTRA_COMPONENT_NAME, it)
+                    flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
                 }
                 startActivity(intent)
             }
         }
     }
+
+    override fun onDestroy() {
+        currentUserTracker.stopTracking()
+        super.onDestroy()
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/FavoriteModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/FavoriteModel.kt
new file mode 100644
index 0000000..6bade0a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/FavoriteModel.kt
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.management
+
+import android.text.TextUtils
+import android.util.Log
+import com.android.systemui.controls.ControlStatus
+import java.util.Collections
+import java.util.Comparator
+
+/**
+ * Model for keeping track of current favorites and their order.
+ *
+ * This model is to be used with two [ControlAdapter] one that shows only favorites in the current
+ * order and another that shows all controls, separated by zone. When the favorite state of any
+ * control is modified or when the favorites are reordered, the adapters are notified of the change.
+ *
+ * @param listControls list of all the [ControlStatus] to display. This includes controls currently
+ *                     marked as favorites as well as those that have been removed (not returned
+ *                     from load)
+ * @param listFavoritesIds list of the [Control.controlId] for all the favorites, including those
+ *                         that have been removed.
+ * @param favoritesAdapter [ControlAdapter] used by the [RecyclerView] that shows only favorites
+ * @param allAdapter [ControlAdapter] used by the [RecyclerView] that shows all controls
+ */
+class FavoriteModel(
+    private val listControls: List<ControlStatus>,
+    listFavoritesIds: List<String>,
+    private val favoritesAdapter: ControlAdapter,
+    private val allAdapter: ControlAdapter
+) {
+
+    companion object {
+        private const val TAG = "FavoriteModel"
+    }
+
+    /**
+     * List of favorite controls ([ControlWrapper]) in order.
+     *
+     * Initially, this list will give a list of wrappers in the order specified by the constructor
+     * variable `listFavoriteIds`.
+     *
+     * As the favorites are added, removed or moved, this list will keep track of those changes.
+     */
+    val favorites: List<ControlWrapper> = listFavoritesIds.map { id ->
+            ControlWrapper(listControls.first { it.control.controlId == id })
+        }.toMutableList()
+
+    /**
+     * List of all controls by zones.
+     *
+     * Lists all the controls with the zone names interleaved as a flat list. After each zone name,
+     * the controls in that zone are listed. Zones are listed in alphabetical order
+     */
+    val all: List<ElementWrapper> = listControls.groupBy { it.control.zone }
+            .mapKeys { it.key ?: "" } // map null to empty
+            .toSortedMap(CharSequenceComparator())
+            .flatMap {
+                val controls = it.value.map { ControlWrapper(it) }
+                if (!TextUtils.isEmpty(it.key)) {
+                    listOf(ZoneNameWrapper(it.key)) + controls
+                } else {
+                    controls
+                }
+            }
+
+    /**
+     * Change the favorite status of a [Control].
+     *
+     * This can be invoked from any of the [ControlAdapter]. It will change the status of that
+     * control and either add it to the list of favorites (at the end) or remove it from it.
+     *
+     * Removing the favorite status from a Removed control will make it disappear completely if
+     * changes are saved.
+     *
+     * @param controlId the id of the [Control] to change the status
+     * @param favorite `true` if and only if it's set to be a favorite.
+     */
+    fun changeFavoriteStatus(controlId: String, favorite: Boolean) {
+        favorites as MutableList
+        val index = all.indexOfFirst {
+            it is ControlWrapper && it.controlStatus.control.controlId == controlId
+        }
+        val control = (all[index] as ControlWrapper).controlStatus
+        if (control.favorite == favorite) {
+            Log.d(TAG, "Changing favorite to same state for ${control.control.controlId} ")
+            return
+        } else {
+            control.favorite = favorite
+        }
+        allAdapter.notifyItemChanged(index)
+        if (favorite) {
+            favorites.add(all[index] as ControlWrapper)
+            favoritesAdapter.notifyItemInserted(favorites.size - 1)
+        } else {
+            val i = favorites.indexOfFirst { it.controlStatus.control.controlId == controlId }
+            favorites.removeAt(i)
+            favoritesAdapter.notifyItemRemoved(i)
+        }
+    }
+
+    /**
+     * Move items in the model and notify the [favoritesAdapter].
+     */
+    fun onMoveItem(from: Int, to: Int) {
+        if (from < to) {
+            for (i in from until to) {
+                Collections.swap(favorites, i, i + 1)
+            }
+        } else {
+            for (i in from downTo to + 1) {
+                Collections.swap(favorites, i, i - 1)
+            }
+        }
+        favoritesAdapter.notifyItemMoved(from, to)
+    }
+}
+
+/**
+ * Compares [CharSequence] as [String].
+ *
+ * It will have empty strings as the first element
+ */
+class CharSequenceComparator : Comparator<CharSequence> {
+    override fun compare(p0: CharSequence?, p1: CharSequence?): Int {
+        if (p0 == null && p1 == null) return 0
+        else if (p0 == null && p1 != null) return -1
+        else if (p0 != null && p1 == null) return 1
+        return p0.toString().compareTo(p1.toString())
+    }
+}
+
+/**
+ * Wrapper classes for the different types of elements shown in the [RecyclerView]s in
+ * [ControlsFavoritingActivity].
+ */
+sealed class ElementWrapper
+data class ZoneNameWrapper(val zoneName: CharSequence) : ElementWrapper()
+data class ControlWrapper(val controlStatus: ControlStatus) : ElementWrapper()
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
new file mode 100644
index 0000000..eb84a8b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.ui
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.provider.Settings
+import android.service.controls.actions.BooleanAction
+import android.util.Log
+import android.view.HapticFeedbackConstants
+
+object ControlActionCoordinator {
+    public const val MIN_LEVEL = 0
+    public const val MAX_LEVEL = 10000
+
+    private var useDetailDialog: Boolean? = null
+
+    fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean) {
+        cvh.action(BooleanAction(templateId, !isChecked))
+
+        val nextLevel = if (isChecked) MIN_LEVEL else MAX_LEVEL
+        cvh.clipLayer.setLevel(nextLevel)
+    }
+
+    fun longPress(cvh: ControlViewHolder) {
+        // Long press snould only be called when there is valid control state, otherwise ignore
+        cvh.cws.control?.let {
+            if (useDetailDialog == null) {
+                useDetailDialog = Settings.Secure.getInt(cvh.context.getContentResolver(),
+                    "systemui.controls_use_detail_panel", 0) != 0
+            }
+
+            try {
+                cvh.layout.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
+                if (useDetailDialog!!) {
+                    DetailDialog(cvh.context, it.getAppIntent()).show()
+                } else {
+                    it.getAppIntent().send()
+                    val closeDialog = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
+                    cvh.context.sendBroadcast(closeDialog)
+                }
+            } catch (e: PendingIntent.CanceledException) {
+                Log.e(ControlsUiController.TAG, "Error sending pending intent", e)
+                cvh.setTransientStatus("Error opening application")
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 78e0e8b..55c1b6a 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.controls.ui
 
 import android.content.Context
-import android.content.Intent
 import android.graphics.drawable.ClipDrawable
 import android.graphics.drawable.GradientDrawable
 import android.graphics.drawable.Icon
@@ -37,8 +36,6 @@
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.R
 
-const val MIN_LEVEL = 0
-const val MAX_LEVEL = 10000
 private const val UPDATE_DELAY_IN_MILLIS = 3000L
 
 class ControlViewHolder(
@@ -79,12 +76,9 @@
             Pair(Control.STATUS_UNKNOWN, ControlTemplate.NO_TEMPLATE)
         }
 
-        cws.control?.let { c ->
+        cws.control?.let {
             layout.setOnLongClickListener(View.OnLongClickListener() {
-                val closeDialog = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
-                context.sendBroadcast(closeDialog)
-
-                c.getAppIntent().send()
+                ControlActionCoordinator.longPress(this@ControlViewHolder)
                 true
             })
         }
@@ -100,19 +94,23 @@
         }
 
         if (!text.isEmpty()) {
-            val previousText = status.getText()
-            val previousTextExtra = statusExtra.getText()
-
-            cancelUpdate = uiExecutor.executeDelayed({
-                    status.setText(previousText)
-                    statusExtra.setText(previousTextExtra)
-                }, UPDATE_DELAY_IN_MILLIS)
-
-            status.setText(text)
-            statusExtra.setText("")
+            setTransientStatus(text)
         }
     }
 
+    fun setTransientStatus(tempStatus: String) {
+        val previousText = status.getText()
+        val previousTextExtra = statusExtra.getText()
+
+        cancelUpdate = uiExecutor.executeDelayed({
+                status.setText(previousText)
+                statusExtra.setText(previousTextExtra)
+            }, UPDATE_DELAY_IN_MILLIS)
+
+        status.setText(tempStatus)
+        statusExtra.setText("")
+    }
+
     fun action(action: ControlAction) {
         controlsController.action(cws.ci, action)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
index d70c86f..0f10537 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
@@ -24,6 +24,10 @@
 interface ControlsUiController {
     val available: Boolean
 
+    companion object {
+        public const val TAG = "ControlsUiController"
+    }
+
     fun show(parent: ViewGroup)
     fun hide()
     fun onRefreshState(componentName: ComponentName, controls: List<Control>)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index f029dfb..9e6636f 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -51,8 +51,6 @@
 import javax.inject.Inject
 import javax.inject.Singleton
 
-private const val TAG = "ControlsUi"
-
 // TEMP CODE for MOCK
 private const val TOKEN = "https://www.googleapis.com/auth/assistant"
 private const val SCOPE = "oauth2:" + TOKEN
@@ -63,13 +61,13 @@
 
     override fun onServiceConnected(cName: ComponentName, binder: IBinder) {
         Thread({
-            Log.i(TAG, "TokenProviderConnection connected")
+            Log.i(ControlsUiController.TAG, "TokenProviderConnection connected")
             mTokenProvider = TokenProvider.Stub.asInterface(binder)
 
             val mLastAccountName = mTokenProvider?.getAccountName()
 
             if (mLastAccountName == null || mLastAccountName.isEmpty()) {
-                Log.e(TAG, "NO ACCOUNT IS SET. Open HomeMock app")
+                Log.e(ControlsUiController.TAG, "NO ACCOUNT IS SET. Open HomeMock app")
             } else {
                 mTokenProvider?.setAuthToken(getAuthToken(mLastAccountName))
                 cc.subscribeToFavorites()
@@ -85,7 +83,7 @@
         val am = AccountManager.get(context)
         val accounts = am.getAccountsByType("com.google")
         if (accounts == null || accounts.size == 0) {
-            Log.w(TAG, "No com.google accounts found")
+            Log.w(ControlsUiController.TAG, "No com.google accounts found")
             return null
         }
 
@@ -104,7 +102,7 @@
         try {
             return am.blockingGetAuthToken(account!!, SCOPE, true)
         } catch (e: Throwable) {
-            Log.e(TAG, "Error getting auth token", e)
+            Log.e(ControlsUiController.TAG, "Error getting auth token", e)
             return null
         }
     }
@@ -146,7 +144,7 @@
     }
 
     override fun show(parent: ViewGroup) {
-        Log.d(TAG, "show()")
+        Log.d(ControlsUiController.TAG, "show()")
 
         this.parent = parent
 
@@ -218,7 +216,7 @@
         val listView = parent.requireViewById(R.id.global_actions_controls_list) as ViewGroup
         var lastRow: ViewGroup = createRow(inflater, listView)
         controlInfos.forEach {
-            Log.d(TAG, "favorited control id: " + it.controlId)
+            Log.d(ControlsUiController.TAG, "favorited control id: " + it.controlId)
             if (lastRow.getChildCount() == 2) {
                 lastRow = createRow(inflater, listView)
             }
@@ -240,7 +238,7 @@
     }
 
     override fun hide() {
-        Log.d(TAG, "hide()")
+        Log.d(ControlsUiController.TAG, "hide()")
         controlsController.get().unsubscribe()
         context.unbindService(tokenProviderConnection)
         tokenProviderConnection = null
@@ -252,10 +250,10 @@
     }
 
     override fun onRefreshState(componentName: ComponentName, controls: List<Control>) {
-        Log.d(TAG, "onRefreshState()")
+        Log.d(ControlsUiController.TAG, "onRefreshState()")
         controls.forEach { c ->
             controlsById.get(ControlKey(componentName, c.getControlId()))?.let {
-                Log.d(TAG, "onRefreshState() for id: " + c.getControlId())
+                Log.d(ControlsUiController.TAG, "onRefreshState() for id: " + c.getControlId())
                 val cws = ControlWithState(it.ci, c)
                 val key = ControlKey(componentName, c.getControlId())
                 controlsById.put(key, cws)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
new file mode 100644
index 0000000..071198b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.ui
+
+import android.app.ActivityView
+import android.app.ActivityOptions
+import android.app.Dialog
+import android.app.PendingIntent
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.Window
+import android.view.WindowManager
+import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+
+import com.android.systemui.R
+
+/**
+ * A dialog that provides an {@link ActivityView}, allowing the application to provide
+ * additional information and actions pertaining to a {@link android.service.controls.Control}.
+ * The activity being launched is specified by {@link android.service.controls.Control#getAppIntent}.
+ */
+class DetailDialog(
+    val parentContext: Context,
+    val intent: PendingIntent
+) : Dialog(parentContext) {
+
+    lateinit var activityView: ActivityView
+
+    val stateCallback: ActivityView.StateCallback = object : ActivityView.StateCallback() {
+        override fun onActivityViewReady(view: ActivityView) {
+            val fillInIntent = Intent()
+
+            // Apply flags to make behaviour match documentLaunchMode=always.
+            fillInIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+            fillInIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
+            view.startActivity(intent, fillInIntent, ActivityOptions.makeBasic())
+        }
+
+        override fun onActivityViewDestroyed(view: ActivityView) {}
+
+        override fun onTaskCreated(taskId: Int, componentName: ComponentName) {}
+
+        override fun onTaskRemovalStarted(taskId: Int) {}
+    }
+
+    init {
+        val window = getWindow()
+        window.requestFeature(Window.FEATURE_NO_TITLE)
+
+        // Inflate the decor view, so the attributes below are not overwritten by the theme.
+        window.getDecorView()
+        window.getAttributes().systemUiVisibility =
+            (window.getAttributes().systemUiVisibility
+                or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                or View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
+
+        window.setLayout(MATCH_PARENT, MATCH_PARENT)
+        window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+        window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+            or WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
+            or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
+        window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
+        window.getAttributes().setFitInsetsTypes(0 /* types */)
+
+        setContentView(R.layout.controls_detail_dialog)
+
+        activityView = ActivityView(context, null, 0, false)
+        requireViewById<ViewGroup>(R.id.controls_activity_view).apply {
+            addView(activityView)
+        }
+    }
+
+    override fun show() {
+        val attrs = getWindow().getAttributes()
+        attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+        getWindow().setAttributes(attrs)
+
+        activityView.setCallback(stateCallback)
+
+        super.show()
+    }
+
+    override fun dismiss() {
+        activityView.release()
+
+        super.dismiss()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
index ae0ebbb..7661c6f 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
@@ -24,6 +24,8 @@
 import android.widget.TextView
 
 import com.android.systemui.R
+import com.android.systemui.controls.ui.ControlActionCoordinator.MIN_LEVEL
+import com.android.systemui.controls.ui.ControlActionCoordinator.MAX_LEVEL
 
 class TemperatureControlBehavior : Behavior {
     lateinit var clipLayer: Drawable
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
index 7cd3ab7..4c35d26 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
@@ -22,10 +22,11 @@
 import android.view.View
 import android.widget.TextView
 import android.service.controls.Control
-import android.service.controls.actions.BooleanAction
 import android.service.controls.templates.ToggleTemplate
 
 import com.android.systemui.R
+import com.android.systemui.controls.ui.ControlActionCoordinator.MIN_LEVEL
+import com.android.systemui.controls.ui.ControlActionCoordinator.MAX_LEVEL
 
 class ToggleBehavior : Behavior {
     lateinit var clipLayer: Drawable
@@ -42,13 +43,15 @@
 
         status.setText(control.getStatusText())
 
-        cvh.layout.setOnClickListener(View.OnClickListener() { toggle() })
+        template = control.getControlTemplate() as ToggleTemplate
+
+        cvh.layout.setOnClickListener(View.OnClickListener() {
+            ControlActionCoordinator.toggle(cvh, template.getTemplateId(), template.isChecked())
+        })
 
         val ld = cvh.layout.getBackground() as LayerDrawable
         clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
 
-        template = control.getControlTemplate() as ToggleTemplate
-
         val checked = template.isChecked()
         val deviceType = control.getDeviceType()
 
@@ -56,11 +59,4 @@
         cvh.setEnabled(checked)
         cvh.applyRenderInfo(RenderInfo.lookup(deviceType, checked))
     }
-
-    fun toggle() {
-        cvh.action(BooleanAction(template.getTemplateId(), !template.isChecked()))
-
-        val nextLevel = if (template.isChecked()) MIN_LEVEL else MAX_LEVEL
-        clipLayer.setLevel(nextLevel)
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
index a6918f5..8d65ca3 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
@@ -19,17 +19,23 @@
 import android.content.Context
 import android.graphics.drawable.Drawable
 import android.graphics.drawable.LayerDrawable
+import android.util.Log
+import android.view.GestureDetector
+import android.view.GestureDetector.SimpleOnGestureListener
 import android.view.MotionEvent
 import android.view.View
 import android.widget.TextView
 import android.service.controls.Control
-import android.service.controls.actions.BooleanAction
 import android.service.controls.actions.FloatAction
 import android.service.controls.templates.RangeTemplate
 import android.service.controls.templates.ToggleRangeTemplate
 import android.util.TypedValue
 
 import com.android.systemui.R
+import com.android.systemui.controls.ui.ControlActionCoordinator.MIN_LEVEL
+import com.android.systemui.controls.ui.ControlActionCoordinator.MAX_LEVEL
+
+import java.util.IllegalFormatException
 
 class ToggleRangeBehavior : Behavior {
     lateinit var clipLayer: Drawable
@@ -41,6 +47,10 @@
     lateinit var status: TextView
     lateinit var context: Context
 
+    companion object {
+        private const val DEFAULT_FORMAT = "%.1f"
+    }
+
     override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
         this.control = cws.control!!
         this.cvh = cvh
@@ -52,10 +62,25 @@
 
         context = status.getContext()
 
-        cvh.layout.setOnTouchListener(ToggleRangeTouchListener())
+        val gestureListener = ToggleRangeGestureListener(cvh.layout)
+        val gestureDetector = GestureDetector(context, gestureListener)
+        cvh.layout.setOnTouchListener({ v: View, e: MotionEvent ->
+            if (gestureDetector.onTouchEvent(e)) {
+                return@setOnTouchListener true
+            }
+
+            if (e.getAction() == MotionEvent.ACTION_UP && gestureListener.isDragging) {
+                gestureListener.isDragging = false
+                endUpdateRange()
+                return@setOnTouchListener true
+            }
+
+            return@setOnTouchListener false
+        })
 
         val ld = cvh.layout.getBackground() as LayerDrawable
         clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
+        clipLayer.setLevel(MIN_LEVEL)
 
         template = control.getControlTemplate() as ToggleRangeTemplate
         rangeTemplate = template.getRange()
@@ -63,30 +88,31 @@
         val checked = template.isChecked()
         val deviceType = control.getDeviceType()
 
-        updateRange((rangeTemplate.getCurrentValue() / 100.0f), checked)
+        val currentRatio = rangeTemplate.getCurrentValue() /
+                (rangeTemplate.getMaxValue() - rangeTemplate.getMinValue())
+        updateRange(currentRatio, checked)
 
         cvh.setEnabled(checked)
         cvh.applyRenderInfo(RenderInfo.lookup(deviceType, checked))
     }
 
-    fun toggle() {
-        cvh.action(BooleanAction(template.getTemplateId(), !template.isChecked()))
-
-        val nextLevel = if (template.isChecked()) MIN_LEVEL else MAX_LEVEL
-        clipLayer.setLevel(nextLevel)
-    }
-
     fun beginUpdateRange() {
         status.setVisibility(View.GONE)
         statusExtra.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
                 .getDimensionPixelSize(R.dimen.control_status_expanded).toFloat())
     }
 
-    fun updateRange(f: Float, checked: Boolean) {
-        clipLayer.setLevel(if (checked) (MAX_LEVEL * f).toInt() else MIN_LEVEL)
+    fun updateRange(ratioDiff: Float, checked: Boolean) {
+        val changeAmount = if (checked) (MAX_LEVEL * ratioDiff).toInt() else MIN_LEVEL
+        val newLevel = Math.max(MIN_LEVEL, Math.min(MAX_LEVEL, clipLayer.getLevel() + changeAmount))
+        clipLayer.setLevel(newLevel)
 
-        if (checked && f < 100.0f && f > 0.0f) {
-            statusExtra.setText("" + (f * 100.0).toInt() + "%")
+        if (checked) {
+            val newValue = levelToRangeValue()
+            val formattedNewValue = format(rangeTemplate.getFormatString().toString(),
+                    DEFAULT_FORMAT, newValue)
+
+            statusExtra.setText(formattedNewValue)
             statusExtra.setVisibility(View.VISIBLE)
         } else {
             statusExtra.setText("")
@@ -94,17 +120,30 @@
         }
     }
 
-    fun endUpdateRange(f: Float) {
-        statusExtra.setText(" - " + (f * 100.0).toInt() + "%")
+    private fun format(primaryFormat: String, backupFormat: String, value: Float): String {
+        return try {
+            String.format(primaryFormat, value)
+        } catch (e: IllegalFormatException) {
+            Log.w(ControlsUiController.TAG, "Illegal format in range template", e)
+            if (backupFormat == "") {
+                ""
+            } else {
+                format(backupFormat, "", value)
+            }
+        }
+    }
 
-        val newValue = rangeTemplate.getMinValue() +
-            (f * (rangeTemplate.getMaxValue() - rangeTemplate.getMinValue()))
+    private fun levelToRangeValue(): Float {
+        val ratio = clipLayer.getLevel().toFloat() / MAX_LEVEL
+        return rangeTemplate.getMinValue() +
+            (ratio * (rangeTemplate.getMaxValue() - rangeTemplate.getMinValue()))
+    }
 
+    fun endUpdateRange() {
         statusExtra.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
                 .getDimensionPixelSize(R.dimen.control_status_normal).toFloat())
         status.setVisibility(View.VISIBLE)
-
-        cvh.action(FloatAction(rangeTemplate.getTemplateId(), findNearestStep(newValue)))
+        cvh.action(FloatAction(rangeTemplate.getTemplateId(), findNearestStep(levelToRangeValue())))
     }
 
     fun findNearestStep(value: Float): Float {
@@ -125,59 +164,39 @@
         return rangeTemplate.getMaxValue()
     }
 
-    inner class ToggleRangeTouchListener() : View.OnTouchListener {
-        private var initialTouchX: Float = 0.0f
-        private var initialTouchY: Float = 0.0f
-        private var isDragging: Boolean = false
-        private val minDragDiff = 20
+    inner class ToggleRangeGestureListener(
+        val v: View
+    ) : SimpleOnGestureListener() {
+        var isDragging: Boolean = false
 
-        override fun onTouch(v: View, e: MotionEvent): Boolean {
-            when (e.getActionMasked()) {
-                MotionEvent.ACTION_DOWN -> setupTouch(e)
-                MotionEvent.ACTION_MOVE -> detectDrag(v, e)
-                MotionEvent.ACTION_UP -> endTouch(v, e)
-            }
-
+        override fun onDown(e: MotionEvent): Boolean {
             return true
         }
 
-        private fun setupTouch(e: MotionEvent) {
-            initialTouchX = e.getX()
-            initialTouchY = e.getY()
+        override fun onLongPress(e: MotionEvent) {
+            ControlActionCoordinator.longPress(this@ToggleRangeBehavior.cvh)
         }
 
-        private fun detectDrag(v: View, e: MotionEvent) {
-            val xDiff = Math.abs(e.getX() - initialTouchX)
-            val yDiff = Math.abs(e.getY() - initialTouchY)
-
-            if (xDiff < minDragDiff) {
-                isDragging = false
-            } else {
-                if (!isDragging) {
-                    this@ToggleRangeBehavior.beginUpdateRange()
-                }
-                v.getParent().requestDisallowInterceptTouchEvent(true)
-                isDragging = true
-                if (yDiff > xDiff) {
-                    endTouch(v, e)
-                } else {
-                    val percent = Math.max(0.0f, Math.min(1.0f, e.getX() / v.getWidth()))
-                    this@ToggleRangeBehavior.updateRange(percent, true)
-                }
-            }
-        }
-
-        private fun endTouch(v: View, e: MotionEvent) {
+        override fun onScroll(
+            e1: MotionEvent,
+            e2: MotionEvent,
+            xDiff: Float,
+            yDiff: Float
+        ): Boolean {
             if (!isDragging) {
-                this@ToggleRangeBehavior.toggle()
-            } else {
-                val percent = Math.max(0.0f, Math.min(1.0f, e.getX() / v.getWidth()))
-                this@ToggleRangeBehavior.endUpdateRange(percent)
+                this@ToggleRangeBehavior.beginUpdateRange()
+                isDragging = true
             }
 
-            initialTouchX = 0.0f
-            initialTouchY = 0.0f
-            isDragging = false
+            this@ToggleRangeBehavior.updateRange(-xDiff / v.getWidth(), true)
+            return true
+        }
+
+        override fun onSingleTapUp(e: MotionEvent): Boolean {
+            val th = this@ToggleRangeBehavior
+            ControlActionCoordinator.toggle(th.cvh, th.template.getTemplateId(),
+                    th.template.isChecked())
+            return true
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 0ec739fe..bf501ce 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -26,8 +26,10 @@
 import android.os.HandlerThread;
 import android.os.ServiceManager;
 import android.util.DisplayMetrics;
+import android.view.Choreographer;
 import android.view.IWindowManager;
 import android.view.LayoutInflater;
+import android.view.WindowManager;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.util.NotificationMessagingUtil;
@@ -98,10 +100,10 @@
 
     @Singleton
     @Provides
-    // Single instance of DisplayMetrics, gets updated by StatusBar, but can be used
-    // anywhere it is needed.
-    public DisplayMetrics provideDisplayMetrics() {
-        return new DisplayMetrics();
+    public DisplayMetrics provideDisplayMetrics(Context context, WindowManager windowManager) {
+        DisplayMetrics displayMetrics = new DisplayMetrics();
+        context.getDisplay().getMetrics(displayMetrics);
+        return displayMetrics;
     }
 
     /** */
@@ -203,4 +205,11 @@
     public ViewMediatorCallback providesViewMediatorCallback(KeyguardViewMediator viewMediator) {
         return viewMediator.getViewMediatorCallback();
     }
+
+    /** */
+    @Singleton
+    @Provides
+    public Choreographer providesChoreographer() {
+        return Choreographer.getInstance();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index f068d9c..91aeb22 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -19,14 +19,13 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.content.res.Resources;
+import android.view.Choreographer;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.BootCompleteCache;
 import com.android.systemui.BootCompleteCacheImpl;
 import com.android.systemui.DumpController;
 import com.android.systemui.assist.AssistModule;
-import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.log.dagger.LogModule;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -34,18 +33,22 @@
 import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.BlurUtils;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.StatusBarWindowBlurController;
+import com.android.systemui.statusbar.NotificationShadeWindowBlurController;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
 import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.notification.people.PeopleHubModule;
 import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
 import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
+import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.KeyguardLiftController;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.ConcurrencyModule;
 import com.android.systemui.util.sensors.AsyncSensorManager;
 import com.android.systemui.util.time.SystemClock;
@@ -100,11 +103,16 @@
     @Singleton
     @Provides
     @Nullable
-    static StatusBarWindowBlurController providesBlurController(BlurUtils blurUtils,
-            @Main Resources resources, SysuiStatusBarStateController statusBarStateController,
-            DumpController dumpController) {
-        return blurUtils.supportsBlursOnWindows() ? new StatusBarWindowBlurController(resources,
-                statusBarStateController, blurUtils, dumpController) : null;
+    static NotificationShadeWindowBlurController providesBlurController(BlurUtils blurUtils,
+            SysuiStatusBarStateController statusBarStateController,
+            DumpController dumpController, BiometricUnlockController biometricUnlockController,
+            KeyguardStateController keyguardStateController,
+            NotificationShadeWindowController notificationShadeWindowController,
+            Choreographer choreographer) {
+        return blurUtils.supportsBlursOnWindows() ? new NotificationShadeWindowBlurController(
+                statusBarStateController, blurUtils, biometricUnlockController,
+                keyguardStateController, notificationShadeWindowController, choreographer,
+                dumpController) : null;
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 4e88726..c45063a 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -167,6 +167,10 @@
                 // again, it will only show after the brightness sensor has stabilized,
                 // avoiding a potential flicker.
                 scrimOpacity = 255;
+            } else if (!mScreenOff && mLightSensor == null) {
+                // No light sensor but previous state turned the screen black. Make the scrim
+                // transparent and below views visible.
+                scrimOpacity = 0;
             } else if (brightnessReady) {
                 // Only unblank scrim once brightness is ready.
                 scrimOpacity = computeScrimOpacity(sensorValue);
@@ -199,7 +203,7 @@
         mDozeService.setDozeScreenBrightness(clampToUserSetting(mDefaultDozeBrightness));
         mDozeHost.setAodDimmingScrim(0f);
     }
-
+    //TODO: brightnessfloat change usages to float.
     private int clampToUserSetting(int brightness) {
         int userSetting = Settings.System.getIntForUser(mContext.getContentResolver(),
                 Settings.System.SCREEN_BRIGHTNESS, Integer.MAX_VALUE,
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index b3fc027..d298b99 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -101,6 +101,7 @@
 import com.android.systemui.plugins.GlobalActionsPanelPlugin;
 import com.android.systemui.statusbar.BlurUtils;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.EmergencyDialerConstants;
@@ -1045,7 +1046,9 @@
             Action action = getItem(position);
             View view = action.create(mContext, convertView, parent, LayoutInflater.from(mContext));
             view.setOnClickListener(v -> onClickItem(position));
-            view.setOnLongClickListener(v -> onLongClickItem(position));
+            if (action instanceof LongPressAction) {
+                view.setOnLongClickListener(v -> onLongClickItem(position));
+            }
             return view;
         }
 
@@ -1666,7 +1669,7 @@
             }
             if (mBackgroundDrawable == null) {
                 mBackgroundDrawable = new ScrimDrawable();
-                mScrimAlpha = 0.8f;
+                mScrimAlpha = ScrimController.BUSY_SCRIM_ALPHA;
             }
             getWindow().setBackgroundDrawable(mBackgroundDrawable);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index 146f36b..280a248 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -149,7 +149,7 @@
         }
 
         if (mBlurUtils.supportsBlursOnWindows()) {
-            background.setAlpha((int) (ScrimController.GRADIENT_SCRIM_ALPHA_BUSY * 255));
+            background.setAlpha((int) (ScrimController.BUSY_SCRIM_ALPHA * 255));
             mBlurUtils.applyBlur(d.getWindow().getDecorView().getViewRootImpl(),
                         mBlurUtils.radiusForRatio(1));
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
index fa8269d..ed6675dc 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
@@ -31,7 +31,6 @@
 import android.util.MathUtils;
 import android.util.Size;
 import android.view.DisplayInfo;
-import android.view.WindowManager;
 
 import com.android.systemui.R;
 
@@ -71,8 +70,7 @@
         }
 
         DisplayInfo displayInfo = new DisplayInfo();
-        WindowManager wm = context.getSystemService(WindowManager.class);
-        wm.getDefaultDisplay().getDisplayInfo(displayInfo);
+        context.getDisplay().getDisplayInfo(displayInfo);
 
         // We only do transition in portrait currently, b/137962047.
         int orientation = context.getResources().getConfiguration().orientation;
@@ -88,6 +86,10 @@
         mImageProcessHelper = new ImageProcessHelper();
         mImageRevealHelper = new ImageRevealHelper(this);
 
+        startProcessingImage();
+    }
+
+    protected void startProcessingImage() {
         if (loadBitmap()) {
             // Compute threshold of the image, this is an async work.
             mImageProcessHelper.start(mBitmap);
@@ -113,7 +115,7 @@
         mBitmap = null;
     }
 
-    private boolean loadBitmap() {
+    protected boolean loadBitmap() {
         if (DEBUG) {
             Log.d(TAG, "loadBitmap: mBitmap=" + mBitmap);
         }
@@ -122,12 +124,7 @@
             mWcgContent = mWallpaperManager.wallpaperSupportsWcg(WallpaperManager.FLAG_SYSTEM);
             mWallpaperManager.forgetLoadedWallpaper();
             if (mBitmap != null) {
-                float scale = (float) mScissor.height() / mBitmap.getHeight();
-                int surfaceHeight = Math.max(mScissor.height(), mBitmap.getHeight());
-                int surfaceWidth = scale > 1f
-                        ? Math.round(mBitmap.getWidth() * scale)
-                        : mBitmap.getWidth();
-                mSurfaceSize.set(0, 0, surfaceWidth, surfaceHeight);
+                mSurfaceSize.set(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
             }
         }
         if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 9da99c4..83a6d75 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1651,7 +1651,7 @@
                             "KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM");
                     StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj;
                     handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration);
-                    mFalsingManager.onSucccessfulUnlock();
+                    mFalsingManager.onSuccessfulUnlock();
                     Trace.endSection();
                     break;
                 case KEYGUARD_DONE_PENDING_TIMEOUT:
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index e119bef..4a7469c 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -61,6 +61,18 @@
         return buffer;
     }
 
+    /** Provides a logging buffer for all logs related to Quick Settings. */
+    @Provides
+    @Singleton
+    @QSLog
+    public static LogBuffer provideQuickSettingsLogBuffer(
+            LogcatEchoTracker bufferFilter,
+            DumpController dumpController) {
+        LogBuffer buffer = new LogBuffer("QSLog", 500, 10, bufferFilter);
+        buffer.attach(dumpController);
+        return buffer;
+    }
+
     /** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */
     @Provides
     @Singleton
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/QSLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/QSLog.java
new file mode 100644
index 0000000..dd5010c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/QSLog.java
@@ -0,0 +1,33 @@
+/*
+ * 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.log.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.log.LogBuffer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/** A {@link LogBuffer} for QS-related messages. */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface QSLog {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index 6f03f18..41b3130 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -345,6 +345,14 @@
     }
 
     /**
+     * Sets the current bound with the currently store aspect ratio.
+     * @param stackBounds
+     */
+    public void transformBoundsToAspectRatio(Rect stackBounds) {
+        transformBoundsToAspectRatio(stackBounds, mAspectRatio, true);
+    }
+
+    /**
      * Set the current bounds (or the default bounds if there are no current bounds) with the
      * specified aspect ratio.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index f9b18cf..c7bfc06 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -280,7 +280,9 @@
         if (mToActivityMessenger != null) {
             Bundle data = new Bundle();
             data.putInt(EXTRA_MENU_STATE, menuState);
-            data.putParcelable(EXTRA_STACK_BOUNDS, stackBounds);
+            if (stackBounds != null) {
+                data.putParcelable(EXTRA_STACK_BOUNDS, stackBounds);
+            }
             data.putParcelable(EXTRA_MOVEMENT_BOUNDS, movementBounds);
             data.putBoolean(EXTRA_ALLOW_TIMEOUT, allowMenuTimeout);
             data.putBoolean(EXTRA_WILL_RESIZE_MENU, willResizeMenu);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
new file mode 100644
index 0000000..9fb6234
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.pip.phone;
+
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_USER_RESIZE;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_BOTTOM;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_LEFT;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_RIGHT;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_TOP;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.hardware.input.InputManager;
+import android.os.Looper;
+import android.provider.DeviceConfig;
+import android.util.DisplayMetrics;
+import android.view.InputChannel;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.InputMonitor;
+import android.view.MotionEvent;
+
+import com.android.internal.policy.TaskResizingAlgorithm;
+import com.android.systemui.R;
+import com.android.systemui.pip.PipBoundsHandler;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Helper on top of PipTouchHandler that handles inputs OUTSIDE of the PIP window, which is used to
+ * trigger dynamic resize.
+ */
+public class PipResizeGestureHandler {
+
+    private static final String TAG = "PipResizeGestureHandler";
+
+    private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+    private final PipBoundsHandler mPipBoundsHandler;
+    private final PipTouchHandler mPipTouchHandler;
+    private final PipMotionHelper mMotionHelper;
+    private final int mDisplayId;
+    private final Executor mMainExecutor;
+    private final Region mTmpRegion = new Region();
+
+    private final PointF mDownPoint = new PointF();
+    private final Point mMaxSize = new Point();
+    private final Point mMinSize = new Point();
+    private final Rect mTmpBounds = new Rect();
+    private final int mDelta;
+
+    private boolean mAllowGesture = false;
+    private boolean mIsAttached;
+    private boolean mIsEnabled;
+    private boolean mEnablePipResize;
+
+    private InputMonitor mInputMonitor;
+    private InputEventReceiver mInputEventReceiver;
+
+    private int mCtrlType;
+
+    public PipResizeGestureHandler(Context context, PipBoundsHandler pipBoundsHandler,
+            PipTouchHandler pipTouchHandler, PipMotionHelper motionHelper) {
+        final Resources res = context.getResources();
+        context.getDisplay().getMetrics(mDisplayMetrics);
+        mDisplayId = context.getDisplayId();
+        mMainExecutor = context.getMainExecutor();
+        mPipBoundsHandler = pipBoundsHandler;
+        mPipTouchHandler = pipTouchHandler;
+        mMotionHelper = motionHelper;
+
+        context.getDisplay().getRealSize(mMaxSize);
+        mDelta = res.getDimensionPixelSize(R.dimen.pip_resize_edge_size);
+
+        mEnablePipResize = DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_SYSTEMUI,
+                PIP_USER_RESIZE,
+                /* defaultValue = */ false);
+        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mMainExecutor,
+                new DeviceConfig.OnPropertiesChangedListener() {
+                    @Override
+                    public void onPropertiesChanged(DeviceConfig.Properties properties) {
+                        if (properties.getKeyset().contains(PIP_USER_RESIZE)) {
+                            mEnablePipResize = properties.getBoolean(
+                                    PIP_USER_RESIZE, /* defaultValue = */ false);
+                        }
+                    }
+                });
+    }
+
+    private void disposeInputChannel() {
+        if (mInputEventReceiver != null) {
+            mInputEventReceiver.dispose();
+            mInputEventReceiver = null;
+        }
+        if (mInputMonitor != null) {
+            mInputMonitor.dispose();
+            mInputMonitor = null;
+        }
+    }
+
+    void onActivityPinned() {
+        mIsAttached = true;
+        updateIsEnabled();
+    }
+
+    void onActivityUnpinned() {
+        mIsAttached = false;
+        updateIsEnabled();
+    }
+
+    private void updateIsEnabled() {
+        boolean isEnabled = mIsAttached && mEnablePipResize;
+        if (isEnabled == mIsEnabled) {
+            return;
+        }
+        mIsEnabled = isEnabled;
+        disposeInputChannel();
+
+        if (mIsEnabled) {
+            // Register input event receiver
+            mInputMonitor = InputManager.getInstance().monitorGestureInput(
+                    "pip-resize", mDisplayId);
+            mInputEventReceiver = new SysUiInputEventReceiver(
+                    mInputMonitor.getInputChannel(), Looper.getMainLooper());
+        }
+    }
+
+    private void onInputEvent(InputEvent ev) {
+        if (ev instanceof MotionEvent) {
+            onMotionEvent((MotionEvent) ev);
+        }
+    }
+
+    private boolean isWithinTouchRegion(int x, int y) {
+        final Rect currentPipBounds = mMotionHelper.getBounds();
+        if (currentPipBounds == null) {
+            return false;
+        }
+
+        mTmpBounds.set(currentPipBounds);
+        mTmpBounds.inset(-mDelta, -mDelta);
+
+        mTmpRegion.set(mTmpBounds);
+        mTmpRegion.op(currentPipBounds, Region.Op.DIFFERENCE);
+
+        if (mTmpRegion.contains(x, y)) {
+            if (x < currentPipBounds.left) {
+                mCtrlType |= CTRL_LEFT;
+            }
+            if (x > currentPipBounds.right) {
+                mCtrlType |= CTRL_RIGHT;
+            }
+            if (y < currentPipBounds.top) {
+                mCtrlType |= CTRL_TOP;
+            }
+            if (y > currentPipBounds.bottom) {
+                mCtrlType |= CTRL_BOTTOM;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    private void onMotionEvent(MotionEvent ev) {
+        int action = ev.getActionMasked();
+        if (action == MotionEvent.ACTION_DOWN) {
+            mAllowGesture = isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
+            if (mAllowGesture) {
+                mDownPoint.set(ev.getX(), ev.getY());
+            }
+
+        } else if (mAllowGesture) {
+            final Rect currentPipBounds = mMotionHelper.getBounds();
+            Rect newSize = TaskResizingAlgorithm.resizeDrag(ev.getX(), ev.getY(), mDownPoint.x,
+                    mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x, mMinSize.y, mMaxSize,
+                    true, true);
+            mPipBoundsHandler.transformBoundsToAspectRatio(newSize);
+            switch (action) {
+                case MotionEvent.ACTION_POINTER_DOWN:
+                    // We do not support multi touch for resizing via drag
+                    mAllowGesture = false;
+                    break;
+                case MotionEvent.ACTION_MOVE:
+                    // Capture inputs
+                    mInputMonitor.pilferPointers();
+                    //TODO: Actually do resize here.
+                    break;
+                case MotionEvent.ACTION_UP:
+                case MotionEvent.ACTION_CANCEL:
+                    //TODO: Finish resize operation here.
+                    mMotionHelper.synchronizePinnedStackBounds();
+                    mCtrlType = CTRL_NONE;
+                    mAllowGesture = false;
+                    break;
+            }
+        }
+    }
+
+    void updateMaxSize(int maxX, int maxY) {
+        mMaxSize.set(maxX, maxY);
+    }
+
+    void updateMiniSize(int minX, int minY) {
+        mMinSize.set(minX, minY);
+    }
+
+    class SysUiInputEventReceiver extends InputEventReceiver {
+        SysUiInputEventReceiver(InputChannel channel, Looper looper) {
+            super(channel, looper);
+        }
+
+        public void onInputEvent(InputEvent event) {
+            PipResizeGestureHandler.this.onInputEvent(event);
+            finishInputEvent(event, true);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 65cc666..924edb6 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -73,6 +73,7 @@
     private final ViewConfiguration mViewConfig;
     private final PipMenuListener mMenuListener = new PipMenuListener();
     private final PipBoundsHandler mPipBoundsHandler;
+    private final PipResizeGestureHandler mPipResizeGestureHandler;
     private IPinnedStackController mPinnedStackController;
 
     private final PipMenuActivityController mMenuController;
@@ -188,6 +189,8 @@
         mGesture = new DefaultPipTouchGesture();
         mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mActivityTaskManager,
                 mMenuController, mSnapAlgorithm, mFlingAnimationUtils);
+        mPipResizeGestureHandler =
+                new PipResizeGestureHandler(context, pipBoundsHandler, this, mMotionHelper);
         mTouchState = new PipTouchState(mViewConfig, mHandler,
                 () -> mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
                         mMovementBounds, true /* allowMenuTimeout */, willResizeMenu()));
@@ -227,6 +230,7 @@
     public void onActivityPinned() {
         cleanUp();
         mShowPipMenuOnAnimationEnd = true;
+        mPipResizeGestureHandler.onActivityPinned();
     }
 
     public void onActivityUnpinned(ComponentName topPipActivity) {
@@ -234,11 +238,14 @@
             // Clean up state after the last PiP activity is removed
             cleanUp();
         }
+        mPipResizeGestureHandler.onActivityUnpinned();
     }
 
     public void onPinnedStackAnimationEnded() {
         // Always synchronize the motion helper bounds once PiP animations finish
         mMotionHelper.synchronizePinnedStackBounds();
+        mPipResizeGestureHandler.updateMiniSize(mMotionHelper.getBounds().width(),
+                mMotionHelper.getBounds().height());
 
         if (mShowPipMenuOnAnimationEnd) {
             mMenuController.showMenu(MENU_STATE_CLOSE, mMotionHelper.getBounds(),
@@ -279,6 +286,7 @@
         Size expandedSize = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio,
                 mExpandedShortestEdgeSize, displaySize.x, displaySize.y);
         mExpandedBounds.set(0, 0, expandedSize.getWidth(), expandedSize.getHeight());
+        mPipResizeGestureHandler.updateMaxSize(expandedSize.getWidth(), expandedSize.getHeight());
         Rect expandedMovementBounds = new Rect();
         mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds, expandedMovementBounds,
                 bottomOffset);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroupController.java
index 86fccd7..eb5b4cc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroupController.java
@@ -69,8 +69,10 @@
                 @Override
                 public void setMobileDataIndicators(NetworkController.IconState statusIcon,
                         NetworkController.IconState qsIcon, int statusType, int qsType,
-                        boolean activityIn, boolean activityOut, String typeContentDescription,
-                        String description, boolean isWide, int subId, boolean roaming) {
+                        boolean activityIn, boolean activityOut,
+                        CharSequence typeContentDescription,
+                        CharSequence typeContentDescriptionHtml, CharSequence description,
+                        boolean isWide, int subId, boolean roaming) {
                     int slotIndex = getSlotIndex(subId);
                     if (slotIndex >= SIM_SLOTS) {
                         Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
@@ -83,7 +85,7 @@
                     mInfos[slotIndex].visible = statusIcon.visible;
                     mInfos[slotIndex].mobileSignalIconId = statusIcon.icon;
                     mInfos[slotIndex].contentDescription = statusIcon.contentDescription;
-                    mInfos[slotIndex].typeContentDescription = typeContentDescription;
+                    mInfos[slotIndex].typeContentDescription = typeContentDescription.toString();
                     mInfos[slotIndex].roaming = roaming;
                     mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
index 84fa700..3cf0718 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
@@ -18,6 +18,7 @@
 
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.external.TileServices;
+import com.android.systemui.qs.logging.QSLogger;
 
 import java.util.Collection;
 
@@ -27,6 +28,7 @@
     void forceCollapsePanels();
     void openPanels();
     Context getContext();
+    QSLogger getQSLogger();
     Collection<QSTile> getTiles();
     void addCallback(Callback callback);
     void removeCallback(Callback callback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 14a117c..53454d6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -38,7 +38,6 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.FrameLayout;
 import android.widget.HorizontalScrollView;
 import android.widget.LinearLayout;
 
@@ -58,9 +57,9 @@
 import com.android.systemui.qs.QSHost.Callback;
 import com.android.systemui.qs.customize.QSCustomizer;
 import com.android.systemui.qs.external.CustomTile;
+import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.settings.BrightnessController;
 import com.android.systemui.settings.ToggleSliderView;
-import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController.BrightnessMirrorListener;
 import com.android.systemui.tuner.TunerService;
@@ -71,6 +70,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.stream.Collectors;
 
 import javax.inject.Inject;
 import javax.inject.Named;
@@ -86,6 +86,7 @@
 
     protected final Context mContext;
     protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
+    private String mCachedSpecs = "";
     protected final View mBrightnessView;
     private final H mHandler = new H();
     private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
@@ -103,6 +104,7 @@
     private QSDetail.Callback mCallback;
     private BrightnessController mBrightnessController;
     private DumpController mDumpController;
+    private final QSLogger mQSLogger;
     protected QSTileHost mHost;
 
     protected QSSecurityFooter mFooter;
@@ -117,9 +119,6 @@
     private BrightnessMirrorController mBrightnessMirrorController;
     private View mDivider;
 
-    private FrameLayout mPluginFrame;
-    private final PluginManager mPluginManager;
-
     private final LocalMediaManager.DeviceCallback mDeviceCallback =
             new LocalMediaManager.DeviceCallback() {
         @Override
@@ -145,24 +144,17 @@
         }
     };
 
-    public QSPanel(Context context) {
-        this(context, null);
-    }
-
-    public QSPanel(Context context, AttributeSet attrs) {
-        this(context, attrs, null);
-    }
-
-    public QSPanel(Context context, AttributeSet attrs, DumpController dumpController) {
-        this(context, attrs, dumpController, null, Dependency.get(BroadcastDispatcher.class));
-    }
-
     @Inject
-    public QSPanel(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
-            DumpController dumpController, PluginManager pluginManager,
-            BroadcastDispatcher broadcastDispatcher) {
+    public QSPanel(
+            @Named(VIEW_CONTEXT) Context context,
+            AttributeSet attrs,
+            DumpController dumpController,
+            BroadcastDispatcher broadcastDispatcher,
+            QSLogger qsLogger
+    ) {
         super(context, attrs);
         mContext = context;
+        mQSLogger = qsLogger;
 
         setOrientation(VERTICAL);
 
@@ -172,6 +164,7 @@
 
         mTileLayout = (QSTileLayout) LayoutInflater.from(mContext).inflate(
                 R.layout.qs_paged_tile_layout, this, false);
+        mQSLogger.logAllTilesChangeListening(mListening, getDumpableTag(), mCachedSpecs);
         mTileLayout.setListening(mListening);
         addView((View) mTileLayout);
 
@@ -198,7 +191,6 @@
         mBrightnessController = new BrightnessController(getContext(),
                 findViewById(R.id.brightness_slider), broadcastDispatcher);
         mDumpController = dumpController;
-        mPluginManager = pluginManager;
     }
 
     @Override
@@ -528,6 +520,7 @@
 
     public void setExpanded(boolean expanded) {
         if (mExpanded == expanded) return;
+        mQSLogger.logPanelExpanded(expanded, getDumpableTag());
         mExpanded = expanded;
         if (!mExpanded && mTileLayout instanceof PagedTileLayout) {
             ((PagedTileLayout) mTileLayout).setCurrentItem(0, false);
@@ -554,6 +547,7 @@
         if (mListening == listening) return;
         mListening = listening;
         if (mTileLayout != null) {
+            mQSLogger.logAllTilesChangeListening(listening, getDumpableTag(), mCachedSpecs);
             mTileLayout.setListening(listening);
         }
         if (mListening) {
@@ -561,6 +555,12 @@
         }
     }
 
+    private String getTilesSpecs() {
+        return mRecords.stream()
+                .map(tileRecord ->  tileRecord.tile.getTileSpec())
+                .collect(Collectors.joining(","));
+    }
+
     public void setListening(boolean listening, boolean expanded) {
         setListening(listening && expanded);
         getFooter().setListening(listening);
@@ -618,6 +618,7 @@
             record.tile.removeCallback(record.callback);
         }
         mRecords.clear();
+        mCachedSpecs = "";
         for (QSTile tile : tiles) {
             addTile(tile, collapsedView);
         }
@@ -682,6 +683,7 @@
         r.tileView.init(r.tile);
         r.tile.refreshState();
         mRecords.add(r);
+        mCachedSpecs = getTilesSpecs();
 
         if (mTileLayout != null) {
             mTileLayout.addTile(r);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 00e09f8..73c42d4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -43,6 +43,7 @@
 import com.android.systemui.qs.external.CustomTile;
 import com.android.systemui.qs.external.TileLifecycleManager;
 import com.android.systemui.qs.external.TileServices;
+import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.phone.AutoTileManager;
@@ -82,6 +83,7 @@
     private final PluginManager mPluginManager;
     private final DumpController mDumpController;
     private final BroadcastDispatcher mBroadcastDispatcher;
+    private final QSLogger mQSLogger;
 
     private final List<Callback> mCallbacks = new ArrayList<>();
     private AutoTileManager mAutoTiles;
@@ -101,12 +103,14 @@
             Provider<AutoTileManager> autoTiles,
             DumpController dumpController,
             BroadcastDispatcher broadcastDispatcher,
-            Optional<StatusBar> statusBarOptional) {
+            Optional<StatusBar> statusBarOptional,
+            QSLogger qsLogger) {
         mIconController = iconController;
         mContext = context;
         mTunerService = tunerService;
         mPluginManager = pluginManager;
         mDumpController = dumpController;
+        mQSLogger = qsLogger;
         mBroadcastDispatcher = broadcastDispatcher;
 
         mServices = new TileServices(this, bgLooper, mBroadcastDispatcher);
@@ -159,6 +163,10 @@
         onTuningChanged(TILES_SETTING, value);
     }
 
+    public QSLogger getQSLogger() {
+        return mQSLogger;
+    }
+
     @Override
     public void addCallback(Callback callback) {
         mCallbacks.add(callback);
@@ -223,6 +231,7 @@
         mTiles.entrySet().stream().filter(tile -> !tileSpecs.contains(tile.getKey())).forEach(
                 tile -> {
                     Log.d(TAG, "Destroying tile: " + tile.getKey());
+                    mQSLogger.logTileDestroyed(tile.getKey(), "Tile removed");
                     tile.getValue().destroy();
                 });
         final LinkedHashMap<String, QSTile> newTiles = new LinkedHashMap<>();
@@ -237,9 +246,11 @@
                         tile.userSwitch(currentUser);
                     }
                     newTiles.put(tileSpec, tile);
+                    mQSLogger.logTileAdded(tileSpec);
                 } else {
                     tile.destroy();
                     Log.d(TAG, "Destroying not available tile: " + tileSpec);
+                    mQSLogger.logTileDestroyed(tileSpec, "Tile not available");
                 }
             } else {
                 Log.d(TAG, "Creating tile: " + tileSpec);
@@ -249,9 +260,11 @@
                         if (tile.isAvailable()) {
                             tile.setTileSpec(tileSpec);
                             newTiles.put(tileSpec, tile);
+                            mQSLogger.logTileAdded(tileSpec);
                         } else {
                             tile.destroy();
                             Log.d(TAG, "Destroying not available tile: " + tileSpec);
+                            mQSLogger.logTileDestroyed(tileSpec, "Tile not available");
                         }
                     }
                 } catch (Throwable t) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index e920701..fda2f6a4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -29,10 +29,12 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.DumpController;
 import com.android.systemui.R;
+import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTile.SignalState;
 import com.android.systemui.plugins.qs.QSTile.State;
 import com.android.systemui.qs.customize.QSCustomizer;
+import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 import com.android.systemui.util.Utils;
@@ -65,9 +67,14 @@
     private QSTileLayout mRegularTileLayout;
 
     @Inject
-    public QuickQSPanel(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
-            DumpController dumpController) {
-        super(context, attrs, dumpController);
+    public QuickQSPanel(
+            @Named(VIEW_CONTEXT) Context context,
+            AttributeSet attrs,
+            DumpController dumpController,
+            BroadcastDispatcher broadcastDispatcher,
+            QSLogger qsLogger
+    ) {
+        super(context, attrs, dumpController, broadcastDispatcher, qsLogger);
         if (mFooter != null) {
             removeView(mFooter.getView());
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index ae61622..21a424c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -191,6 +191,7 @@
         mTile.setLabel(tile.getLabel());
         mTile.setSubtitle(tile.getSubtitle());
         mTile.setContentDescription(tile.getContentDescription());
+        mTile.setStateDescription(tile.getStateDescription());
         mTile.setState(tile.getState());
     }
 
@@ -209,8 +210,10 @@
 
     @Override
     public void handleSetListening(boolean listening) {
+        super.handleSetListening(listening);
         if (mListening == listening) return;
         mListening = listening;
+
         try {
             if (listening) {
                 updateDefaultTileAndIcon();
@@ -345,6 +348,12 @@
             state.contentDescription = state.label;
         }
 
+        if (mTile.getStateDescription() != null) {
+            state.stateDescription = mTile.getStateDescription();
+        } else {
+            state.stateDescription = null;
+        }
+
         if (state instanceof BooleanState) {
             state.expandedAccessibilityClassName = Switch.class.getName();
             ((BooleanState) state).value = (state.state == Tile.STATE_ACTIVE);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
new file mode 100644
index 0000000..ab8de26
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
@@ -0,0 +1,147 @@
+/*
+ * 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.qs.logging
+
+import android.service.quicksettings.Tile
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.LogLevel.VERBOSE
+import com.android.systemui.log.LogMessage
+import com.android.systemui.log.dagger.QSLog
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.statusbar.StatusBarState
+import javax.inject.Inject
+
+private const val TAG = "QSLog"
+
+class QSLogger @Inject constructor(
+    @QSLog private val buffer: LogBuffer
+) {
+
+    fun logTileAdded(tileSpec: String) {
+        log(DEBUG, {
+            str1 = tileSpec
+        }, {
+            "[$str1] Tile added"
+        })
+    }
+
+    fun logTileDestroyed(tileSpec: String, reason: String) {
+        log(DEBUG, {
+            str1 = tileSpec
+            str2 = reason
+        }, {
+            "[$str1] Tile destroyed. Reason: $str2"
+        })
+    }
+
+    fun logTileChangeListening(tileSpec: String, listening: Boolean) {
+        log(VERBOSE, {
+            bool1 = listening
+            str1 = tileSpec
+        }, {
+            "[$str1] Tile listening=$bool1"
+        })
+    }
+
+    fun logAllTilesChangeListening(listening: Boolean, containerName: String, allSpecs: String) {
+        log(DEBUG, {
+            bool1 = listening
+            str1 = containerName
+            str2 = allSpecs
+        }, {
+            "Tiles listening=$bool1 in $str1. $str2"
+        })
+    }
+
+    fun logTileClick(tileSpec: String, statusBarState: Int, state: Int) {
+        log(DEBUG, {
+            str1 = tileSpec
+            int1 = statusBarState
+            str2 = StatusBarState.toShortString(statusBarState)
+            str3 = toStateString(state)
+        }, {
+            "[$str1] Tile clicked. StatusBarState=$str2. TileState=$str3"
+        })
+    }
+
+    fun logTileSecondaryClick(tileSpec: String, statusBarState: Int, state: Int) {
+        log(DEBUG, {
+            str1 = tileSpec
+            int1 = statusBarState
+            str2 = StatusBarState.toShortString(statusBarState)
+            str3 = toStateString(state)
+        }, {
+            "[$str1] Tile long clicked. StatusBarState=$str2. TileState=$str3"
+        })
+    }
+
+    fun logTileLongClick(tileSpec: String, statusBarState: Int, state: Int) {
+        log(DEBUG, {
+            str1 = tileSpec
+            int1 = statusBarState
+            str2 = StatusBarState.toShortString(statusBarState)
+            str3 = toStateString(state)
+        }, {
+            "[$str1] Tile long clicked. StatusBarState=$str2. TileState=$str3"
+        })
+    }
+
+    fun logTileUpdated(tileSpec: String, state: QSTile.State) {
+        log(VERBOSE, {
+            str1 = tileSpec
+            str2 = state.label.toString()
+            str3 = state.icon.toString()
+            int1 = state.state
+            if (state is QSTile.SignalState) {
+                bool1 = true
+                bool2 = state.activityIn
+                bool3 = state.activityOut
+            }
+        }, {
+            "[$str1] Tile updated. Label=$str2. State=$int1. Icon=$str3." +
+                if (bool1) " Activity in/out=$bool2/$bool3" else ""
+        })
+    }
+
+    fun logPanelExpanded(expanded: Boolean, containerName: String) {
+        log(DEBUG, {
+            str1 = containerName
+            bool1 = expanded
+        }, {
+            "$str1 expanded=$bool1"
+        })
+    }
+
+    private fun toStateString(state: Int): String {
+        return when (state) {
+            Tile.STATE_ACTIVE -> "active"
+            Tile.STATE_INACTIVE -> "inactive"
+            Tile.STATE_UNAVAILABLE -> "unavailable"
+            else -> "wrong state"
+        }
+    }
+
+    private inline fun log(
+        logLevel: LogLevel,
+        initializer: LogMessage.() -> Unit,
+        noinline printer: LogMessage.() -> String
+    ) {
+        buffer.log(TAG, logLevel, initializer, printer)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 2fe64d2..8feee10 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -63,7 +63,6 @@
     private String mAccessibilityClass;
     private boolean mTileState;
     private boolean mCollapsedView;
-    private boolean mClicked;
     private boolean mShowRippleEffect = true;
 
     private final ImageView mBg;
@@ -230,13 +229,35 @@
         setLongClickable(state.handlesLongClick);
         mIcon.setIcon(state, allowAnimations);
         setContentDescription(state.contentDescription);
+        final StringBuilder stateDescription = new StringBuilder();
+        switch (state.state) {
+            case Tile.STATE_UNAVAILABLE:
+                stateDescription.append(mContext.getString(R.string.tile_unavailable));
+                break;
+            case Tile.STATE_INACTIVE:
+                if (state instanceof QSTile.BooleanState) {
+                    stateDescription.append(mContext.getString(R.string.switch_bar_off));
+                }
+                break;
+            case Tile.STATE_ACTIVE:
+                if (state instanceof QSTile.BooleanState) {
+                    stateDescription.append(mContext.getString(R.string.switch_bar_on));
+                }
+                break;
+            default:
+                break;
+        }
+        if (!TextUtils.isEmpty(state.stateDescription)) {
+            stateDescription.append(", ");
+            stateDescription.append(state.stateDescription);
+        }
+        setStateDescription(stateDescription.toString());
 
         mAccessibilityClass =
                 state.state == Tile.STATE_UNAVAILABLE ? null : state.expandedAccessibilityClassName;
         if (state instanceof QSTile.BooleanState) {
             boolean newState = ((BooleanState) state).value;
             if (mTileState != newState) {
-                mClicked = false;
                 mTileState = newState;
             }
         }
@@ -288,23 +309,10 @@
     }
 
     @Override
-    public boolean performClick() {
-        mClicked = true;
-        return super.performClick();
-    }
-
-    @Override
     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
         super.onInitializeAccessibilityEvent(event);
         if (!TextUtils.isEmpty(mAccessibilityClass)) {
             event.setClassName(mAccessibilityClass);
-            if (Switch.class.getName().equals(mAccessibilityClass)) {
-                boolean b = mClicked ? !mTileState : mTileState;
-                String label = getResources()
-                        .getString(b ? R.string.switch_bar_on : R.string.switch_bar_off);
-                event.setContentDescription(label);
-                event.setChecked(b);
-            }
         }
     }
 
@@ -316,11 +324,13 @@
         if (!TextUtils.isEmpty(mAccessibilityClass)) {
             info.setClassName(mAccessibilityClass);
             if (Switch.class.getName().equals(mAccessibilityClass)) {
-                boolean b = mClicked ? !mTileState : mTileState;
-                String label = getResources()
-                        .getString(b ? R.string.switch_bar_on : R.string.switch_bar_off);
+                String label = getResources().getString(
+                        mTileState ? R.string.switch_bar_on : R.string.switch_bar_off);
+                // Set the text here for tests in
+                // android.platform.test.scenario.sysui.quicksettings. Can be removed when
+                // UiObject2 has a new getStateDescription() API and tests are updated.
                 info.setText(label);
-                info.setChecked(b);
+                info.setChecked(mTileState);
                 info.setCheckable(true);
                 if (isLongClickable()) {
                     info.addAction(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index e559694..60f6647 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -27,6 +27,8 @@
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION;
 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
@@ -41,7 +43,6 @@
 import android.util.Log;
 import android.util.SparseArray;
 
-import androidx.annotation.NonNull;
 import androidx.lifecycle.Lifecycle;
 import androidx.lifecycle.LifecycleOwner;
 import androidx.lifecycle.LifecycleRegistry;
@@ -63,6 +64,7 @@
 import com.android.systemui.qs.PagedTileLayout.TilePage;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.QuickStatusBarHeader;
+import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tiles.QSSettingsControllerKt;
 import com.android.systemui.qs.tiles.QSSettingsPanel;
 
@@ -95,6 +97,7 @@
     private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
     private final StatusBarStateController
             mStatusBarStateController = Dependency.get(StatusBarStateController.class);
+    private final QSLogger mQSLogger;
 
     private final ArrayList<Callback> mCallbacks = new ArrayList<>();
     private final Object mStaleListener = new Object();
@@ -156,6 +159,7 @@
         mState = newTileState();
         mTmpState = newTileState();
         mQSSettingsPanelOption = QSSettingsControllerKt.getQSSettingsPanelOption();
+        mQSLogger = host.getQSLogger();
     }
 
     protected final void resetStates() {
@@ -243,6 +247,7 @@
         mMetricsLogger.write(populate(new LogMaker(ACTION_QS_CLICK).setType(TYPE_ACTION)
                 .addTaggedData(FIELD_STATUS_BAR_STATE,
                         mStatusBarStateController.getState())));
+        mQSLogger.logTileClick(mTileSpec, mStatusBarStateController.getState(), mState.state);
         mHandler.sendEmptyMessage(H.CLICK);
     }
 
@@ -250,6 +255,8 @@
         mMetricsLogger.write(populate(new LogMaker(ACTION_QS_SECONDARY_CLICK).setType(TYPE_ACTION)
                 .addTaggedData(FIELD_STATUS_BAR_STATE,
                         mStatusBarStateController.getState())));
+        mQSLogger.logTileSecondaryClick(mTileSpec, mStatusBarStateController.getState(),
+                mState.state);
         mHandler.sendEmptyMessage(H.SECONDARY_CLICK);
     }
 
@@ -257,6 +264,7 @@
         mMetricsLogger.write(populate(new LogMaker(ACTION_QS_LONG_PRESS).setType(TYPE_ACTION)
                 .addTaggedData(FIELD_STATUS_BAR_STATE,
                         mStatusBarStateController.getState())));
+        mQSLogger.logTileLongClick(mTileSpec, mStatusBarStateController.getState(), mState.state);
         mHandler.sendEmptyMessage(H.LONG_CLICK);
 
         Prefs.putInt(
@@ -359,6 +367,7 @@
         handleUpdateState(mTmpState, arg);
         final boolean changed = mTmpState.copyTo(mState);
         if (changed) {
+            mQSLogger.logTileUpdated(mTileSpec, mState);
             handleStateChanged();
         }
         mHandler.removeMessages(H.STALE);
@@ -445,9 +454,15 @@
         mIsFullQs = 0;
     }
 
-    protected abstract void handleSetListening(boolean listening);
+    @CallSuper
+    protected void handleSetListening(boolean listening) {
+        if (mTileSpec != null) {
+            mQSLogger.logTileChangeListening(mTileSpec, listening);
+        }
+    }
 
     protected void handleDestroy() {
+        mQSLogger.logTileDestroyed(mTileSpec, "Handle destroy");
         if (mListeners.size() != 0) {
             handleSetListening(false);
         }
@@ -592,6 +607,12 @@
         public Drawable getInvisibleDrawable(Context context) {
             return mInvisibleDrawable;
         }
+
+        @Override
+        @NonNull
+        public String toString() {
+            return "DrawableIcon";
+        }
     }
 
     public static class DrawableIconWithRes extends DrawableIcon {
@@ -606,6 +627,12 @@
         public boolean equals(Object o) {
             return o instanceof DrawableIconWithRes && ((DrawableIconWithRes) o).mId == mId;
         }
+
+        @Override
+        @NonNull
+        public String toString() {
+            return String.format("DrawableIconWithRes[resId=0x%08x]", mId);
+        }
     }
 
     public static class ResourceIcon extends Icon {
@@ -642,6 +669,7 @@
         }
 
         @Override
+        @NonNull
         public String toString() {
             return String.format("ResourceIcon[resId=0x%08x]", mResId);
         }
@@ -660,6 +688,12 @@
             // workaround: get a clean state for every new AVD
             return context.getDrawable(mAnimatedResId).getConstantState().newDrawable();
         }
+
+        @Override
+        @NonNull
+        public String toString() {
+            return String.format("AnimationIcon[resId=0x%08x]", mResId);
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index 5e297e2..b24fdbf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -130,6 +130,7 @@
     }
 
     public void handleSetListening(boolean listening) {
+        super.handleSetListening(listening);
         if (mListening == listening) return;
         mListening = listening;
         if (listening) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index d62f10d..4449d48 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -74,6 +74,7 @@
 
     @Override
     public void handleSetListening(boolean listening) {
+        super.handleSetListening(listening);
         mSetting.setListening(listening);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 9282a2e..cc7aaea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -82,10 +82,6 @@
     }
 
     @Override
-    public void handleSetListening(boolean listening) {
-    }
-
-    @Override
     protected void handleClick() {
         // Secondary clicks are header clicks, just toggle.
         final boolean isEnabled = mState.value;
@@ -134,25 +130,27 @@
         state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
         state.secondaryLabel = TextUtils.emptyIfNull(
                 getSecondaryLabel(enabled, connecting, connected, state.isTransient));
+        state.contentDescription = state.label;
+        state.stateDescription = "";
         if (enabled) {
             if (connected) {
                 state.icon = new BluetoothConnectedTileIcon();
                 if (!TextUtils.isEmpty(mController.getConnectedDeviceName())) {
                     state.label = mController.getConnectedDeviceName();
                 }
-                state.contentDescription =
+                state.stateDescription =
                         mContext.getString(R.string.accessibility_bluetooth_name, state.label)
                                 + ", " + state.secondaryLabel;
             } else if (state.isTransient) {
                 state.icon = ResourceIcon.get(
                         com.android.internal.R.drawable.ic_bluetooth_transient_animation);
-                state.contentDescription = state.secondaryLabel;
+                state.stateDescription = state.secondaryLabel;
             } else {
                 state.icon =
                         ResourceIcon.get(com.android.internal.R.drawable.ic_qs_bluetooth);
                 state.contentDescription = mContext.getString(
-                        R.string.accessibility_quick_settings_bluetooth) + ","
-                        + mContext.getString(R.string.accessibility_not_connected);
+                        R.string.accessibility_quick_settings_bluetooth);
+                state.stateDescription = mContext.getString(R.string.accessibility_not_connected);
             }
             state.state = Tile.STATE_ACTIVE;
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 32b051e..4b53ae2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -97,6 +97,7 @@
 
     @Override
     public void handleSetListening(boolean listening) {
+        super.handleSetListening(listening);
         if (DEBUG) Log.d(TAG, "handleSetListening " + listening);
         if (!listening) {
             mController.setDiscovering(false);
@@ -183,6 +184,7 @@
     protected void handleUpdateState(BooleanState state, Object arg) {
         state.label = mContext.getString(R.string.quick_settings_cast_title);
         state.contentDescription = state.label;
+        state.stateDescription = "";
         state.value = false;
         final List<CastDevice> devices = mController.getCastDevices();
         boolean connecting = false;
@@ -192,8 +194,9 @@
             if (device.state == CastDevice.STATE_CONNECTED) {
                 state.value = true;
                 state.secondaryLabel = getDeviceName(device);
-                state.contentDescription = state.contentDescription + ","
-                        + mContext.getString(R.string.accessibility_cast_name, state.label);
+                state.stateDescription = state.stateDescription + ","
+                        + mContext.getString(
+                                R.string.accessibility_cast_name, state.label);
                 connecting = false;
                 break;
             } else if (device.state == CastDevice.STATE_CONNECTING) {
@@ -217,9 +220,8 @@
             state.state = Tile.STATE_UNAVAILABLE;
             String noWifi = mContext.getString(R.string.quick_settings_cast_no_wifi);
             state.secondaryLabel = noWifi;
-            state.contentDescription = state.contentDescription + ", " + mContext.getString(
-                    R.string.accessibility_quick_settings_not_available, noWifi);
         }
+        state.stateDescription = state.stateDescription + ", " + state.secondaryLabel;
         mDetailAdapter.updateItems(devices);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 22470c7..bc03ca6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -26,6 +26,7 @@
 import android.provider.Settings;
 import android.service.quicksettings.Tile;
 import android.telephony.SubscriptionManager;
+import android.text.Html;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -90,10 +91,6 @@
     }
 
     @Override
-    public void handleSetListening(boolean listening) {
-    }
-
-    @Override
     public Intent getLongClickIntent() {
         if (getState().state == Tile.STATE_UNAVAILABLE) {
             return new Intent(Settings.ACTION_WIRELESS_SETTINGS);
@@ -194,27 +191,24 @@
             state.secondaryLabel = r.getString(R.string.cell_data_off);
         }
 
-
-        // TODO(b/77881974): Instead of switching out the description via a string check for
-        // we need to have two strings provided by the MobileIconGroup.
-        final CharSequence contentDescriptionSuffix;
+        state.contentDescription = state.label;
         if (state.state == Tile.STATE_INACTIVE) {
-            contentDescriptionSuffix = r.getString(R.string.cell_data_off_content_description);
+            // This information is appended later by converting the Tile.STATE_INACTIVE state.
+            state.stateDescription = "";
         } else {
-            contentDescriptionSuffix = state.secondaryLabel;
+            state.stateDescription = state.secondaryLabel;
         }
-
-        state.contentDescription = state.label + ", " + contentDescriptionSuffix;
     }
 
     private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) {
         if (TextUtils.isEmpty(dataType)) {
-            return current;
+            return Html.fromHtml(current.toString(), 0);
         }
         if (TextUtils.isEmpty(current)) {
-            return dataType;
+            return Html.fromHtml(dataType.toString(), 0);
         }
-        return mContext.getString(R.string.mobile_carrier_text_format, current, dataType);
+        String concat = mContext.getString(R.string.mobile_carrier_text_format, current, dataType);
+        return Html.fromHtml(concat, 0);
     }
 
     private CharSequence getMobileDataContentName(CallbackInfo cb) {
@@ -255,14 +249,17 @@
 
         @Override
         public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
-                int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
-                String description, boolean isWide, int subId, boolean roaming) {
+                int qsType, boolean activityIn, boolean activityOut,
+                CharSequence typeContentDescription,
+                CharSequence typeContentDescriptionHtml, CharSequence description,
+                boolean isWide, int subId, boolean roaming) {
             if (qsIcon == null) {
                 // Not data sim, don't display.
                 return;
             }
             mInfo.dataSubscriptionName = mController.getMobileDataNetworkName();
-            mInfo.dataContentDescription = (description != null) ? typeContentDescription : null;
+            mInfo.dataContentDescription =
+                    (description != null) ? typeContentDescriptionHtml : null;
             mInfo.activityIn = activityIn;
             mInfo.activityOut = activityOut;
             mInfo.roaming = roaming;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index ecb4048..9c0030d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -66,6 +66,7 @@
 
     @Override
     public void handleSetListening(boolean listening) {
+        super.handleSetListening(listening);
         mSetting.setListening(listening);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 79996bc..8ba6084 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -50,10 +50,6 @@
     }
 
     @Override
-    public void handleSetListening(boolean listening) {
-    }
-
-    @Override
     public Intent getLongClickIntent() {
         return new Intent(Settings.ACTION_DATA_SAVER_SETTINGS);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 52d1a5b3..ebf45a6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -240,6 +240,8 @@
                 zen != Global.ZEN_MODE_OFF, mController.getConfig(), false));
         state.icon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_dnd);
         checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_ADJUST_VOLUME);
+        // Keeping the secondaryLabel in contentDescription instead of stateDescription is easier
+        // to understand.
         switch (zen) {
             case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
                 state.contentDescription =
@@ -287,6 +289,7 @@
 
     @Override
     public void handleSetListening(boolean listening) {
+        super.handleSetListening(listening);
         if (mListening == listening) return;
         mListening = listening;
         if (mListening) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index dafdd89..27ccd7c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -58,10 +58,6 @@
     }
 
     @Override
-    public void handleSetListening(boolean listening) {
-    }
-
-    @Override
     protected void handleUserSwitch(int newUserId) {
     }
 
@@ -102,14 +98,13 @@
         }
         state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
         state.secondaryLabel = "";
+        state.stateDescription = "";
         if (!mFlashlightController.isAvailable()) {
             state.icon = mIcon;
             state.slash.isSlashed = true;
             state.secondaryLabel = mContext.getString(
                     R.string.quick_settings_flashlight_camera_in_use);
-            state.contentDescription = mContext.getString(
-                    R.string.accessibility_quick_settings_flashlight_unavailable)
-                    + ", " + state.secondaryLabel;
+            state.stateDescription = state.secondaryLabel;
             state.state = Tile.STATE_UNAVAILABLE;
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 42f8010..0c86157 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -69,6 +69,7 @@
 
     @Override
     public void handleSetListening(boolean listening) {
+        super.handleSetListening(listening);
         if (mListening == listening) return;
         mListening = listening;
         if (listening) {
@@ -148,6 +149,7 @@
 
         state.secondaryLabel = getSecondaryLabel(
                 isTileActive, isTransient, isDataSaverEnabled, numConnectedDevices);
+        state.stateDescription = state.secondaryLabel;
     }
 
     @Nullable
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index fbdca3b..02f364b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -61,10 +61,6 @@
     }
 
     @Override
-    public void handleSetListening(boolean listening) {
-    }
-
-    @Override
     public Intent getLongClickIntent() {
         return new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
     }
@@ -105,15 +101,8 @@
         }
         state.icon = mIcon;
         state.slash.isSlashed = !state.value;
-        if (locationEnabled) {
-            state.label = mContext.getString(R.string.quick_settings_location_label);
-            state.contentDescription = mContext.getString(
-                    R.string.accessibility_quick_settings_location_on);
-        } else {
-            state.label = mContext.getString(R.string.quick_settings_location_label);
-            state.contentDescription = mContext.getString(
-                    R.string.accessibility_quick_settings_location_off);
-        }
+        state.label = mContext.getString(R.string.quick_settings_location_label);
+        state.contentDescription = state.label;
         state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
         state.expandedAccessibilityClassName = Switch.class.getName();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index f0140ba..4bee075 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -57,6 +57,7 @@
 
     @Override
     public void handleSetListening(boolean listening) {
+        super.handleSetListening(listening);
         mListening = listening;
         if (mListening) {
             mBroadcastDispatcher.registerReceiver(mNfcReceiver,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index 241b375..bc1c1e1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -190,6 +190,7 @@
 
     @Override
     protected void handleSetListening(boolean listening) {
+        super.handleSetListening(listening);
         mIsListening = listening;
         if (listening) {
             mListener.setCallback(this);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index 7ca1e44..2557226 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -51,9 +51,6 @@
         return new BooleanState();
     }
 
-    public void handleSetListening(boolean listening) {
-    }
-
     @Override
     public Intent getLongClickIntent() {
         return new Intent(Settings.ACTION_DISPLAY_SETTINGS);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 596c3b9..88a30a1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -105,10 +105,6 @@
     }
 
     @Override
-    protected void handleSetListening(boolean listening) {
-    }
-
-    @Override
     public CharSequence getTileLabel() {
         return mContext.getString(R.string.quick_settings_screen_record_label);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index 8f1769b..b90ca01 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -32,10 +32,11 @@
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 
-import javax.inject.Inject;
 import java.time.LocalTime;
 import java.time.format.DateTimeFormatter;
 
+import javax.inject.Inject;
+
 /**
  * Quick Settings tile for: Night Mode / Dark Theme / Dark Mode.
  *
@@ -141,10 +142,6 @@
     }
 
     @Override
-    protected void handleSetListening(boolean listening) {
-    }
-
-    @Override
     public CharSequence getTileLabel() {
         return getState().label;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
index 64fe54a..7d4343c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
@@ -128,7 +128,7 @@
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
-        FontSizeUtils.updateFontSize(mName, R.dimen.qs_detail_item_secondary_text_size);
+        FontSizeUtils.updateFontSize(mName, getFontSizeDimen());
     }
 
     @Override
@@ -146,4 +146,8 @@
     public boolean hasOverlappingRendering() {
         return false;
     }
+
+    protected int getFontSizeDimen() {
+        return R.dimen.qs_detail_item_secondary_text_size;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
index 7c1ffde..aab30d4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
@@ -73,10 +73,6 @@
     }
 
     @Override
-    public void handleSetListening(boolean listening) {
-    }
-
-    @Override
     public CharSequence getTileLabel() {
         return getState().label;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index b7ce101..39bfd5a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -84,10 +84,6 @@
     }
 
     @Override
-    public void handleSetListening(boolean listening) {
-    }
-
-    @Override
     public void setDetailListening(boolean listening) {
         if (listening) {
             mWifiController.addAccessPointCallback(mDetailAdapter);
@@ -195,6 +191,7 @@
         state.activityIn = cb.enabled && cb.activityIn;
         state.activityOut = cb.enabled && cb.activityOut;
         final StringBuffer minimalContentDescription = new StringBuffer();
+        final StringBuffer minimalStateDescription = new StringBuffer();
         final Resources r = mContext.getResources();
         if (isTransient) {
             state.icon = ResourceIcon.get(
@@ -219,13 +216,14 @@
                 mContext.getString(R.string.quick_settings_wifi_label)).append(",");
         if (state.value) {
             if (wifiConnected) {
-                minimalContentDescription.append(cb.wifiSignalContentDescription).append(",");
+                minimalStateDescription.append(cb.wifiSignalContentDescription);
                 minimalContentDescription.append(removeDoubleQuotes(cb.ssid));
                 if (!TextUtils.isEmpty(state.secondaryLabel)) {
                     minimalContentDescription.append(",").append(state.secondaryLabel);
                 }
             }
         }
+        state.stateDescription = minimalStateDescription.toString();
         state.contentDescription = minimalContentDescription.toString();
         state.dualLabelContentDescription = r.getString(
                 R.string.accessibility_quick_settings_open_settings, getTileLabel());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 7853dc3..318c0c4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -50,10 +50,6 @@
     }
 
     @Override
-    public void handleSetListening(boolean listening) {
-    }
-
-    @Override
     public Intent getLongClickIntent() {
         return new Intent(Settings.ACTION_MANAGED_PROFILE_SETTINGS);
     }
@@ -103,14 +99,11 @@
         state.icon = mIcon;
         if (state.value) {
             state.slash.isSlashed = false;
-            state.contentDescription =  mContext.getString(
-                    R.string.accessibility_quick_settings_work_mode_on);
         } else {
             state.slash.isSlashed = true;
-            state.contentDescription =  mContext.getString(
-                    R.string.accessibility_quick_settings_work_mode_off);
         }
         state.label = mContext.getString(R.string.quick_settings_work_mode_label);
+        state.contentDescription = state.label;
         state.expandedAccessibilityClassName = Switch.class.getName();
         state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 4f38a15..197fe21 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -442,27 +442,35 @@
 
         ValueAnimator screenshotDropInAnim = screenRect != null ? createRectAnimation(screenRect)
                 : createScreenshotDropInAnimation();
-        ValueAnimator screenshotFadeOutAnim = createScreenshotToCornerAnimation(w, h);
+        ValueAnimator screenshotToCornerAnimation = createScreenshotToCornerAnimation(w, h);
         mScreenshotAnimation = new AnimatorSet();
-        mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotFadeOutAnim);
-        mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
+        mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotToCornerAnimation);
+
+        saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
             @Override
-            public void onAnimationEnd(Animator animation) {
-                // Save the screenshot once we have a bit of time now
-                saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
-                    @Override
-                    void onActionsReady(Uri uri, List<Notification.Action> smartActions,
-                            List<Notification.Action> actions) {
-                        if (uri == null) {
-                            mNotificationsController.notifyScreenshotError(
-                                    R.string.screenshot_failed_to_capture_text);
+            void onActionsReady(Uri uri, List<Notification.Action> smartActions,
+                    List<Notification.Action> actions) {
+                if (uri == null) {
+                    mNotificationsController.notifyScreenshotError(
+                            R.string.screenshot_failed_to_capture_text);
+                } else {
+                    mScreenshotHandler.post(() -> {
+                        if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) {
+                            mScreenshotAnimation.addListener(
+                                    new AnimatorListenerAdapter() {
+                                        @Override
+                                        public void onAnimationEnd(Animator animation) {
+                                            super.onAnimationEnd(animation);
+                                            createScreenshotActionsShadeAnimation(
+                                                    smartActions, actions).start();
+                                        }
+                                    });
                         } else {
-                            mScreenshotHandler.post(() ->
-                                    createScreenshotActionsShadeAnimation(smartActions,
-                                            actions).start());
+                            createScreenshotActionsShadeAnimation(smartActions,
+                                    actions).start();
                         }
-                    }
-                });
+                    });
+                }
                 mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
                 mScreenshotHandler.sendMessageDelayed(
                         mScreenshotHandler.obtainMessage(MESSAGE_CORNER_TIMEOUT),
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index e7e1ba8..92236ae 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -38,7 +38,6 @@
 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;
@@ -124,10 +123,6 @@
             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);
-
         ContentResolver resolver = mContext.getContentResolver();
         Bitmap image = mParams.image;
         Resources r = mContext.getResources();
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
index b1f1f38..821144a 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
@@ -17,8 +17,8 @@
 package com.android.systemui.settings;
 
 import static com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MAX;
-import static com.android.settingslib.display.BrightnessUtils.convertGammaToLinear;
-import static com.android.settingslib.display.BrightnessUtils.convertLinearToGamma;
+import static com.android.settingslib.display.BrightnessUtils.convertGammaToLinearFloat;
+import static com.android.settingslib.display.BrightnessUtils.convertLinearToGammaFloat;
 
 import android.animation.ValueAnimator;
 import android.content.ContentResolver;
@@ -39,7 +39,9 @@
 import android.service.vr.IVrManager;
 import android.service.vr.IVrStateCallbacks;
 import android.util.Log;
+import android.util.MathUtils;
 
+import com.android.internal.BrightnessSynchronizer;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.RestrictedLockUtilsInternal;
@@ -58,12 +60,21 @@
     private static final int MSG_DETACH_LISTENER = 4;
     private static final int MSG_VR_MODE_CHANGED = 5;
 
-    private final int mMinimumBacklight;
-    private final int mMaximumBacklight;
-    private final int mDefaultBacklight;
-    private final int mMinimumBacklightForVr;
-    private final int mMaximumBacklightForVr;
-    private final int mDefaultBacklightForVr;
+    private static final Uri BRIGHTNESS_MODE_URI =
+            Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE);
+    private static final Uri BRIGHTNESS_URI =
+            Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
+    private static final Uri BRIGHTNESS_FLOAT_URI =
+            Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FLOAT);
+    private static final Uri BRIGHTNESS_FOR_VR_FLOAT_URI =
+            Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT);
+
+    private final float mMinimumBacklight;
+    private final float mMaximumBacklight;
+    private final float mDefaultBacklight;
+    private final float mMinimumBacklightForVr;
+    private final float mMaximumBacklightForVr;
+    private final float mDefaultBacklightForVr;
 
     private final Context mContext;
     private final ToggleSlider mControl;
@@ -90,16 +101,9 @@
         public void onBrightnessLevelChanged();
     }
 
-    /** ContentObserver to watch brightness **/
+    /** ContentObserver to watch brightness */
     private class BrightnessObserver extends ContentObserver {
 
-        private final Uri BRIGHTNESS_MODE_URI =
-                Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE);
-        private final Uri BRIGHTNESS_URI =
-                Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
-        private final Uri BRIGHTNESS_FOR_VR_URI =
-                Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_VR);
-
         public BrightnessObserver(Handler handler) {
             super(handler);
         }
@@ -116,9 +120,9 @@
             if (BRIGHTNESS_MODE_URI.equals(uri)) {
                 mBackgroundHandler.post(mUpdateModeRunnable);
                 mBackgroundHandler.post(mUpdateSliderRunnable);
-            } else if (BRIGHTNESS_URI.equals(uri)) {
+            } else if (BRIGHTNESS_FLOAT_URI.equals(uri)) {
                 mBackgroundHandler.post(mUpdateSliderRunnable);
-            } else if (BRIGHTNESS_FOR_VR_URI.equals(uri)) {
+            } else if (BRIGHTNESS_FOR_VR_FLOAT_URI.equals(uri)) {
                 mBackgroundHandler.post(mUpdateSliderRunnable);
             } else {
                 mBackgroundHandler.post(mUpdateModeRunnable);
@@ -139,7 +143,10 @@
                     BRIGHTNESS_URI,
                     false, this, UserHandle.USER_ALL);
             cr.registerContentObserver(
-                    BRIGHTNESS_FOR_VR_URI,
+                    BRIGHTNESS_FLOAT_URI,
+                    false, this, UserHandle.USER_ALL);
+            cr.registerContentObserver(
+                    BRIGHTNESS_FOR_VR_FLOAT_URI,
                     false, this, UserHandle.USER_ALL);
         }
 
@@ -229,18 +236,21 @@
     private final Runnable mUpdateSliderRunnable = new Runnable() {
         @Override
         public void run() {
-            final int val;
+            final float valFloat;
             final boolean inVrMode = mIsVrModeEnabled;
             if (inVrMode) {
-                val = Settings.System.getIntForUser(mContext.getContentResolver(),
-                        Settings.System.SCREEN_BRIGHTNESS_FOR_VR, mDefaultBacklightForVr,
+                valFloat = Settings.System.getFloatForUser(mContext.getContentResolver(),
+                        Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT, mDefaultBacklightForVr,
                         UserHandle.USER_CURRENT);
             } else {
-                val = Settings.System.getIntForUser(mContext.getContentResolver(),
-                        Settings.System.SCREEN_BRIGHTNESS, mDefaultBacklight,
+                valFloat = Settings.System.getFloatForUser(mContext.getContentResolver(),
+                        Settings.System.SCREEN_BRIGHTNESS_FLOAT, mDefaultBacklight,
                         UserHandle.USER_CURRENT);
             }
-            mHandler.obtainMessage(MSG_UPDATE_SLIDER, val, inVrMode ? 1 : 0).sendToTarget();
+            // Value is passed as intbits, since this is what the message takes.
+            final int valueAsIntBits = Float.floatToIntBits(valFloat);
+            mHandler.obtainMessage(MSG_UPDATE_SLIDER, valueAsIntBits,
+                    inVrMode ? 1 : 0).sendToTarget();
         }
     };
 
@@ -259,7 +269,7 @@
             try {
                 switch (msg.what) {
                     case MSG_UPDATE_SLIDER:
-                        updateSlider(msg.arg1, msg.arg2 != 0);
+                        updateSlider(Float.intBitsToFloat(msg.arg1), msg.arg2 != 0);
                         break;
                     case MSG_SET_CHECKED:
                         mControl.setChecked(msg.arg1 != 0);
@@ -298,12 +308,19 @@
         mBrightnessObserver = new BrightnessObserver(mHandler);
 
         PowerManager pm = context.getSystemService(PowerManager.class);
-        mMinimumBacklight = pm.getMinimumScreenBrightnessSetting();
-        mMaximumBacklight = pm.getMaximumScreenBrightnessSetting();
-        mDefaultBacklight = pm.getDefaultScreenBrightnessSetting();
-        mMinimumBacklightForVr = pm.getMinimumScreenBrightnessForVrSetting();
-        mMaximumBacklightForVr = pm.getMaximumScreenBrightnessForVrSetting();
-        mDefaultBacklightForVr = pm.getDefaultScreenBrightnessForVrSetting();
+        mMinimumBacklight = pm.getBrightnessConstraint(
+                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
+        mMaximumBacklight = pm.getBrightnessConstraint(
+                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
+        mDefaultBacklight = pm.getBrightnessConstraint(
+                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT);
+        mMinimumBacklightForVr = pm.getBrightnessConstraint(
+                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR);
+        mMaximumBacklightForVr = pm.getBrightnessConstraint(
+                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM_VR);
+        mDefaultBacklightForVr = pm.getBrightnessConstraint(
+                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_VR);
+
 
         mAutomaticAvailable = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_automatic_brightness_available);
@@ -344,37 +361,39 @@
             mSliderAnimator.cancel();
         }
 
-        final int min;
-        final int max;
+        final float minBacklight;
+        final float maxBacklight;
         final int metric;
-        final String setting;
+        final String settingToChange;
 
         if (mIsVrModeEnabled) {
             metric = MetricsEvent.ACTION_BRIGHTNESS_FOR_VR;
-            min = mMinimumBacklightForVr;
-            max = mMaximumBacklightForVr;
-            setting = Settings.System.SCREEN_BRIGHTNESS_FOR_VR;
+            minBacklight = mMinimumBacklightForVr;
+            maxBacklight = mMaximumBacklightForVr;
+            settingToChange = Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT;
         } else {
             metric = mAutomatic
                     ? MetricsEvent.ACTION_BRIGHTNESS_AUTO
                     : MetricsEvent.ACTION_BRIGHTNESS;
-            min = mMinimumBacklight;
-            max = mMaximumBacklight;
-            setting = Settings.System.SCREEN_BRIGHTNESS;
+            minBacklight = mMinimumBacklight;
+            maxBacklight = mMaximumBacklight;
+            settingToChange = Settings.System.SCREEN_BRIGHTNESS_FLOAT;
         }
-
-        final int val = convertGammaToLinear(value, min, max);
-
+        final float valFloat = MathUtils.min(convertGammaToLinearFloat(value,
+                minBacklight, maxBacklight),
+                1.0f);
         if (stopTracking) {
-            MetricsLogger.action(mContext, metric, val);
-        }
+            // TODO(brightnessfloat): change to use float value instead.
+            MetricsLogger.action(mContext, metric,
+                    BrightnessSynchronizer.brightnessFloatToInt(mContext, valFloat));
 
-        setBrightness(val);
+        }
+        setBrightness(valFloat);
         if (!tracking) {
             AsyncTask.execute(new Runnable() {
                     public void run() {
-                        Settings.System.putIntForUser(mContext.getContentResolver(),
-                                setting, val, UserHandle.USER_CURRENT);
+                        Settings.System.putFloatForUser(mContext.getContentResolver(),
+                                settingToChange, valFloat, UserHandle.USER_CURRENT);
                     }
                 });
         }
@@ -402,7 +421,7 @@
                 mUserTracker.getCurrentUserId());
     }
 
-    private void setBrightness(int brightness) {
+    private void setBrightness(float brightness) {
         mDisplayManager.setTemporaryBrightness(brightness);
     }
 
@@ -413,9 +432,9 @@
         }
     }
 
-    private void updateSlider(int val, boolean inVrMode) {
-        final int min;
-        final int max;
+    private void updateSlider(float brightnessValue, boolean inVrMode) {
+        final float min;
+        final float max;
         if (inVrMode) {
             min = mMinimumBacklightForVr;
             max = mMaximumBacklightForVr;
@@ -423,7 +442,10 @@
             min = mMinimumBacklight;
             max = mMaximumBacklight;
         }
-        if (val == convertGammaToLinear(mControl.getValue(), min, max)) {
+        // convertGammaToLinearFloat returns 0-1
+        if (BrightnessSynchronizer.brightnessFloatToInt(mContext, brightnessValue)
+                == BrightnessSynchronizer.brightnessFloatToInt(mContext,
+                convertGammaToLinearFloat(mControl.getValue(), min, max))) {
             // If we have more resolution on the slider than we do in the actual setting, then
             // multiple slider positions will map to the same setting value. Thus, if we see a
             // setting value here that maps to the current slider position, we don't bother to
@@ -431,7 +453,8 @@
             // change to the user even though it isn't one.
             return;
         }
-        final int sliderVal = convertLinearToGamma(val, min, max);
+        // Returns GAMMA_SPACE_MIN - GAMMA_SPACE_MAX
+        final int sliderVal = convertLinearToGammaFloat(brightnessValue, min, max);
         animateSliderTo(sliderVal);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowBlurController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowBlurController.kt
new file mode 100644
index 0000000..85215ed
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowBlurController.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import android.view.Choreographer
+import android.view.View
+import com.android.internal.util.IndentingPrintWriter
+import com.android.systemui.DumpController
+import com.android.systemui.Dumpable
+import com.android.systemui.Interpolators
+import com.android.systemui.statusbar.phone.BiometricUnlockController
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
+import com.android.systemui.statusbar.phone.NotificationShadeWindowController
+import com.android.systemui.statusbar.phone.PanelExpansionListener
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import javax.inject.Inject
+import javax.inject.Singleton
+import kotlin.math.max
+
+/**
+ * Controller responsible for statusbar window blur.
+ */
+@Singleton
+class NotificationShadeWindowBlurController @Inject constructor(
+    private val statusBarStateController: SysuiStatusBarStateController,
+    private val blurUtils: BlurUtils,
+    private val biometricUnlockController: BiometricUnlockController,
+    private val keyguardStateController: KeyguardStateController,
+    private val notificationShadeWindowController: NotificationShadeWindowController,
+    private val choreographer: Choreographer,
+    dumpController: DumpController
+) : PanelExpansionListener, Dumpable {
+    companion object {
+        private const val WAKE_UP_ANIMATION_ENABLED = true
+        private const val SHADE_BLUR_ENABLED = true
+    }
+
+    lateinit var root: View
+    private var keyguardAnimator: Animator? = null
+    private var notificationAnimator: Animator? = null
+    private var updateScheduled: Boolean = false
+    private var shadeExpansion = 1.0f
+    private var shadeBlurRadius = 0
+        set(value) {
+            if (field == value) return
+            field = value
+            scheduleUpdate()
+        }
+    private var wakeAndUnlockBlurRadius = 0
+        set(value) {
+            if (field == value) return
+            field = value
+            scheduleUpdate()
+        }
+    private var incomingNotificationBlurRadius = 0
+        set(value) {
+            if (field == value) return
+            field = value
+            scheduleUpdate()
+        }
+
+    /**
+     * Callback that updates the window blur value and is called only once per frame.
+     */
+    private val updateBlurCallback = Choreographer.FrameCallback {
+        updateScheduled = false
+
+        var notificationBlur = 0
+        if (statusBarStateController.state == StatusBarState.KEYGUARD) {
+            notificationBlur = (incomingNotificationBlurRadius * shadeExpansion).toInt()
+        }
+
+        val blur = max(max(shadeBlurRadius, wakeAndUnlockBlurRadius), notificationBlur)
+        blurUtils.applyBlur(root.viewRootImpl, blur)
+    }
+
+    /**
+     * Animate blurs when unlocking.
+     */
+    private val keyguardStateCallback = object : KeyguardStateController.Callback {
+        override fun onKeyguardFadingAwayChanged() {
+            if (!keyguardStateController.isKeyguardFadingAway ||
+                    biometricUnlockController.mode != MODE_WAKE_AND_UNLOCK) {
+                return
+            }
+
+            keyguardAnimator?.cancel()
+            keyguardAnimator = ValueAnimator.ofFloat(1f, 0f).apply {
+                duration = keyguardStateController.keyguardFadingAwayDuration
+                startDelay = keyguardStateController.keyguardFadingAwayDelay
+                interpolator = Interpolators.DECELERATE_QUINT
+                addUpdateListener { animation: ValueAnimator ->
+                    wakeAndUnlockBlurRadius =
+                            blurUtils.radiusForRatio(animation.animatedValue as Float)
+                }
+                addListener(object : AnimatorListenerAdapter() {
+                    override fun onAnimationEnd(animation: Animator?) {
+                        keyguardAnimator = null
+                        scheduleUpdate()
+                    }
+                })
+                start()
+            }
+        }
+
+        override fun onKeyguardShowingChanged() {
+            if (keyguardStateController.isShowing) {
+                keyguardAnimator?.cancel()
+                notificationAnimator?.cancel()
+            }
+        }
+    }
+
+    init {
+        dumpController.registerDumpable(this)
+        if (WAKE_UP_ANIMATION_ENABLED) {
+            keyguardStateController.addCallback(keyguardStateCallback)
+        }
+    }
+
+    /**
+     * Update blurs when pulling down the shade
+     */
+    override fun onPanelExpansionChanged(expansion: Float, tracking: Boolean) {
+        if (!SHADE_BLUR_ENABLED) {
+            return
+        }
+
+        var newBlur = 0
+        if (statusBarStateController.state == StatusBarState.SHADE) {
+            newBlur = blurUtils.radiusForRatio(expansion)
+        }
+
+        if (shadeBlurRadius == newBlur) {
+            return
+        }
+        shadeBlurRadius = newBlur
+        scheduleUpdate()
+    }
+
+    private fun scheduleUpdate() {
+        if (updateScheduled) {
+            return
+        }
+        updateScheduled = true
+        choreographer.postFrameCallback(updateBlurCallback)
+    }
+
+    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+        IndentingPrintWriter(pw, "  ").use {
+            it.println("StatusBarWindowBlurController:")
+            it.increaseIndent()
+            it.println("shadeBlurRadius: $shadeBlurRadius")
+            it.println("wakeAndUnlockBlur: $wakeAndUnlockBlurRadius")
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWindowBlurController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWindowBlurController.kt
deleted file mode 100644
index 2e72163..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWindowBlurController.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar
-
-import android.content.res.Resources
-import android.view.View
-import com.android.internal.util.IndentingPrintWriter
-import com.android.systemui.DumpController
-import com.android.systemui.Dumpable
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.statusbar.phone.PanelExpansionListener
-import java.io.FileDescriptor
-import java.io.PrintWriter
-import javax.inject.Inject
-import javax.inject.Singleton
-
-/**
- * Controller responsible for statusbar window blur.
- */
-@Singleton
-class StatusBarWindowBlurController @Inject constructor(
-    @Main private val resources: Resources,
-    private val statusBarStateController: SysuiStatusBarStateController,
-    private val blurUtils: BlurUtils,
-    dumpController: DumpController
-) : PanelExpansionListener, Dumpable {
-
-    lateinit var root: View
-    private var blurRadius = 0
-
-    init {
-        dumpController.registerDumpable(this)
-    }
-
-    override fun onPanelExpansionChanged(expansion: Float, tracking: Boolean) {
-        val newBlur = if (statusBarStateController.state == StatusBarState.SHADE)
-            blurUtils.radiusForRatio(expansion)
-        else
-            0
-
-        if (blurRadius == newBlur) {
-            return
-        }
-        blurRadius = newBlur
-        blurUtils.applyBlur(root.viewRootImpl, blurRadius)
-    }
-
-    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
-        IndentingPrintWriter(pw, "  ").use {
-            it.println("StatusBarWindowBlurController:")
-            it.increaseIndent()
-            it.println("blurRadius: $blurRadius")
-        }
-    }
-}
\ No newline at end of file
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 916da6e..b0bf813 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -290,6 +290,9 @@
             NotificationEntry entry = mPendingNotifications.get(key);
             entry.abortTask();
             mPendingNotifications.remove(key);
+            for (NotifCollectionListener listener : mNotifCollectionListeners) {
+                listener.onEntryCleanUp(entry);
+            }
             mLogger.logInflationAborted(key, "pending", reason);
         }
         NotificationEntry addedEntry = getActiveNotificationUnfiltered(key);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
index 3fa1954..b5c81b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
@@ -151,10 +151,6 @@
                         .append(" ");
             }
 
-            if (notifEntry.hasInflationError()) {
-                rksb.append("(!)hasInflationError ");
-            }
-
             if (notifEntry.getDismissState() != NOT_DISMISSED) {
                 rksb.append("dismissState=")
                         .append(notifEntry.getDismissState())
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 38d8d97..ec3285f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -44,6 +44,7 @@
 import android.annotation.IntDef;
 import android.annotation.MainThread;
 import android.annotation.Nullable;
+import android.app.Notification;
 import android.os.RemoteException;
 import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.NotificationListenerService.RankingMap;
@@ -239,8 +240,7 @@
             // Also mark any children as dismissed as system server will auto-dismiss them as well
             if (entry.getSbn().getNotification().isGroupSummary()) {
                 for (NotificationEntry otherEntry : mNotificationSet.values()) {
-                    if (otherEntry.getSbn().getGroupKey().equals(entry.getSbn().getGroupKey())
-                            && otherEntry.getDismissState() != DISMISSED) {
+                    if (shouldAutoDismiss(otherEntry, entry.getSbn().getGroupKey())) {
                         otherEntry.setDismissState(PARENT_DISMISSED);
                         if (isCanceled(otherEntry)) {
                             canceledEntries.add(otherEntry);
@@ -544,6 +544,28 @@
         return entry.getDismissState() != NOT_DISMISSED;
     }
 
+    /**
+     * When a group summary is dismissed, NotificationManager will also try to dismiss its children.
+     * Returns true if we think dismissing the group summary with group key
+     * <code>dismissedGroupKey</code> will cause NotificationManager to also dismiss
+     * <code>entry</code>.
+     *
+     * See NotificationManager.cancelGroupChildrenByListLocked() for corresponding code.
+     */
+    private static boolean shouldAutoDismiss(
+            NotificationEntry entry,
+            String dismissedGroupKey) {
+        return entry.getSbn().getGroupKey().equals(dismissedGroupKey)
+                && !entry.getSbn().getNotification().isGroupSummary()
+                && !hasFlag(entry, Notification.FLAG_FOREGROUND_SERVICE)
+                && !hasFlag(entry, Notification.FLAG_BUBBLE)
+                && entry.getDismissState() != DISMISSED;
+    }
+
+    private static boolean hasFlag(NotificationEntry entry, int flag) {
+        return (entry.getSbn().getNotification().flags & flag) != 0;
+    }
+
     private void dispatchOnEntryInit(NotificationEntry entry) {
         mAmDispatchingToOtherCode = true;
         for (NotifCollectionListener listener : mNotifCollectionListeners) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
index 9272e51b..83f56cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
@@ -18,9 +18,7 @@
 
 import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
 
-import android.os.RemoteException;
 import android.service.notification.NotificationStats;
-import android.service.notification.StatusBarNotification;
 
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
@@ -29,6 +27,7 @@
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
 import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
 import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
 
 import javax.inject.Inject;
@@ -44,6 +43,7 @@
 
     private final IStatusBarService mStatusBarService;
     private final NotifCollection mNotifCollection;
+    private final NotifInflationErrorManager mNotifErrorManager;
 
     private NotificationRowBinderImpl mNotificationRowBinder;
     private InflationCallback mExternalInflationCallback;
@@ -51,9 +51,11 @@
     @Inject
     public NotifInflaterImpl(
             IStatusBarService statusBarService,
-            NotifCollection notifCollection) {
+            NotifCollection notifCollection,
+            NotifInflationErrorManager errorManager) {
         mStatusBarService = statusBarService;
         mNotifCollection = notifCollection;
+        mNotifErrorManager = errorManager;
     }
 
     /**
@@ -81,7 +83,6 @@
     @Override
     public void inflateViews(NotificationEntry entry) {
         try {
-            entry.setHasInflationError(false);
             requireBinder().inflateViews(entry, getDismissCallback(entry));
         } catch (InflationException e) {
             // logged in mInflationCallback.handleInflationException
@@ -131,25 +132,12 @@
                 public void handleInflationException(
                         NotificationEntry entry,
                         Exception e) {
-                    entry.setHasInflationError(true);
-                    try {
-                        final StatusBarNotification sbn = entry.getSbn();
-                        // report notification inflation errors back up
-                        // to notification delegates
-                        mStatusBarService.onNotificationError(
-                                sbn.getPackageName(),
-                                sbn.getTag(),
-                                sbn.getId(),
-                                sbn.getUid(),
-                                sbn.getInitialPid(),
-                                e.getMessage(),
-                                sbn.getUserId());
-                    } catch (RemoteException ex) {
-                    }
+                    mNotifErrorManager.setInflationError(entry, e);
                 }
 
                 @Override
                 public void onAsyncInflationFinished(NotificationEntry entry) {
+                    mNotifErrorManager.clearInflationError(entry);
                     if (mExternalInflationCallback != null) {
                         mExternalInflationCallback.onInflationFinished(entry);
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 006d40d..f482d37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -122,9 +122,6 @@
      */
     @CancellationReason int mCancellationReason = REASON_NOT_CANCELED;
 
-    /** @see #hasInflationError() */
-    private boolean mHasInflationError;
-
     /** @see #getDismissState() */
     @NonNull private DismissState mDismissState = DismissState.NOT_DISMISSED;
 
@@ -274,23 +271,6 @@
      */
 
     /**
-     * Whether this notification had an error when attempting to inflate. This is only used in
-     * the NewNotifPipeline
-     */
-    public boolean hasInflationError() {
-        return mHasInflationError;
-    }
-
-    /**
-     * Set whether the notification has an error while inflating.
-     *
-     * TODO: Move this into an inflation error manager class.
-     */
-    public void setHasInflationError(boolean hasError) {
-        mHasInflationError = hasError;
-    }
-
-    /**
      * Set if the user has dismissed this notif but we haven't yet heard back from system server to
      * confirm the dismissal.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index 1e5946a..1c8fdac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -16,12 +16,18 @@
 
 package com.android.systemui.statusbar.notification.collection.coordinator;
 
+import android.os.RemoteException;
+import android.service.notification.StatusBarNotification;
+
+import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
 import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -34,7 +40,7 @@
  * Aborts inflation when a notification is removed.
  *
  * If a notification is not done inflating, this coordinator will filter the notification out
- * from the NotifListBuilder.
+ * from the {@link ShadeListBuilder}.
  */
 @Singleton
 public class PreparationCoordinator implements Coordinator {
@@ -42,15 +48,22 @@
 
     private final PreparationCoordinatorLogger mLogger;
     private final NotifInflater mNotifInflater;
+    private final NotifInflationErrorManager mNotifErrorManager;
     private final List<NotificationEntry> mPendingNotifications = new ArrayList<>();
+    private final IStatusBarService mStatusBarService;
 
     @Inject
     public PreparationCoordinator(
             PreparationCoordinatorLogger logger,
-            NotifInflaterImpl notifInflater) {
+            NotifInflaterImpl notifInflater,
+            NotifInflationErrorManager errorManager,
+            IStatusBarService service) {
         mLogger = logger;
         mNotifInflater = notifInflater;
         mNotifInflater.setInflationCallback(mInflationCallback);
+        mNotifErrorManager = errorManager;
+        mNotifErrorManager.addInflationErrorListener(mInflationErrorListener);
+        mStatusBarService = service;
     }
 
     @Override
@@ -84,8 +97,7 @@
          */
         @Override
         public boolean shouldFilterOut(NotificationEntry entry, long now) {
-            if (entry.hasInflationError()) {
-                mPendingNotifications.remove(entry);
+            if (mNotifErrorManager.hasInflationError(entry)) {
                 return true;
             }
             return false;
@@ -112,6 +124,34 @@
         }
     };
 
+    private final NotifInflationErrorManager.NotifInflationErrorListener mInflationErrorListener =
+            new NotifInflationErrorManager.NotifInflationErrorListener() {
+        @Override
+        public void onNotifInflationError(NotificationEntry entry, Exception e) {
+            mPendingNotifications.remove(entry);
+            try {
+                final StatusBarNotification sbn = entry.getSbn();
+                // report notification inflation errors back up
+                // to notification delegates
+                mStatusBarService.onNotificationError(
+                        sbn.getPackageName(),
+                        sbn.getTag(),
+                        sbn.getId(),
+                        sbn.getUid(),
+                        sbn.getInitialPid(),
+                        e.getMessage(),
+                        sbn.getUserId());
+            } catch (RemoteException ex) {
+            }
+            mNotifInflationErrorFilter.invalidateList();
+        }
+
+        @Override
+        public void onNotifInflationErrorCleared(NotificationEntry entry) {
+            mNotifInflationErrorFilter.invalidateList();
+        }
+    };
+
     private void inflateEntry(NotificationEntry entry, String reason) {
         abortInflation(entry, reason);
         mPendingNotifications.add(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 3e0bcbb..8a6d5c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -91,6 +91,7 @@
                 presenter,
                 listContainer,
                 bindRowCallback)
+        notifBindPipelineInitializer.initialize()
 
         if (featureFlags.isNewNotifPipelineEnabled) {
             newNotifPipeline.get().initialize(notificationListener, notificationRowBinder)
@@ -99,7 +100,6 @@
         if (featureFlags.isNewNotifPipelineRenderingEnabled) {
             // TODO
         } else {
-            notifBindPipelineInitializer.initialize()
             notificationRowBinder.setInflationCallback(entryManager)
 
             remoteInputUriController.attach(entryManager)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index c2da517..41b248f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -78,9 +78,9 @@
         mDismissButton.setText(R.string.clear_all_notifications_text);
         mDismissButton.setContentDescription(
                 mContext.getString(R.string.accessibility_clear_all));
-        mManageButton.setText(R.string.manage_notifications_text);
+        mManageButton.setText(R.string.manage_notifications_history_text);
         mManageButton.setContentDescription(
-                mContext.getString(R.string.accessibility_manage_notification));
+                mContext.getString(R.string.manage_notifications_history_text));
     }
 
     public boolean isButtonVisible() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java
index af2d084..e2513da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java
@@ -25,11 +25,10 @@
 import androidx.annotation.Nullable;
 import androidx.core.os.CancellationSignal;
 
-import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
 
 import java.util.Map;
@@ -78,8 +77,8 @@
     private BindStage mStage;
 
     @Inject
-    NotifBindPipeline(NotificationEntryManager entryManager) {
-        entryManager.addNotificationEntryListener(mEntryListener);
+    NotifBindPipeline(CommonNotifCollection collection) {
+        collection.addCollectionListener(mCollectionListener);
     }
 
     /**
@@ -158,18 +157,15 @@
         callbacks.clear();
     }
 
-    //TODO: Move this to onManageEntry hook when we split that from add/remove
-    private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
+    private final NotifCollectionListener mCollectionListener = new NotifCollectionListener() {
         @Override
-        public void onPendingEntryAdded(NotificationEntry entry) {
+        public void onEntryInit(NotificationEntry entry) {
             mBindEntries.put(entry, new BindEntry());
             mStage.createStageParams(entry);
         }
 
         @Override
-        public void onEntryRemoved(NotificationEntry entry,
-                @Nullable NotificationVisibility visibility,
-                boolean removedByUser) {
+        public void onEntryCleanUp(NotificationEntry entry) {
             BindEntry bindEntry = mBindEntries.remove(entry);
             ExpandableNotificationRow row = bindEntry.row;
             if (row != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifInflationErrorManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifInflationErrorManager.java
new file mode 100644
index 0000000..a3ca084
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifInflationErrorManager.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import androidx.annotation.NonNull;
+import androidx.collection.ArraySet;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * A manager handling the error state of a notification when it encounters an exception while
+ * inflating. We don't want to show these notifications to the user but may want to keep them
+ * around for logging purposes.
+ */
+@Singleton
+public class NotifInflationErrorManager {
+
+    Set<NotificationEntry> mErroredNotifs = new ArraySet<>();
+    List<NotifInflationErrorListener> mListeners = new ArrayList<>();
+
+    @Inject
+    public NotifInflationErrorManager() { }
+
+    /**
+     * Mark the notification as errored out due to encountering an exception while inflating.
+     *
+     * @param e the exception encountered while inflating
+     */
+    public void setInflationError(NotificationEntry entry, Exception e) {
+        mErroredNotifs.add(entry);
+        for (int i = 0; i < mListeners.size(); i++) {
+            mListeners.get(i).onNotifInflationError(entry, e);
+        }
+    }
+
+    /**
+     * Notification inflated successfully and is no longer errored out.
+     */
+    public void clearInflationError(NotificationEntry entry) {
+        if (mErroredNotifs.contains(entry)) {
+            mErroredNotifs.remove(entry);
+            for (int i = 0; i < mListeners.size(); i++) {
+                mListeners.get(i).onNotifInflationErrorCleared(entry);
+            }
+        }
+    }
+
+    /**
+     * Whether or not the notification encountered an exception while inflating.
+     */
+    public boolean hasInflationError(@NonNull NotificationEntry entry) {
+        return mErroredNotifs.contains(entry);
+    }
+
+    /**
+     * Add listener for changes in inflation error state.
+     */
+    public void addInflationErrorListener(NotifInflationErrorListener listener) {
+        mListeners.add(listener);
+    }
+
+    /**
+     * Listener for changes in notification inflation error state.
+     */
+    public interface NotifInflationErrorListener {
+
+        /**
+         * Called when notification encounters an inflation exception.
+         *
+         * @param e the exception encountered while inflating
+         */
+        void onNotifInflationError(NotificationEntry entry, Exception e);
+
+        /**
+         * Called when notification inflation error is cleared.
+         */
+        default void onNotifInflationErrorCleared(NotificationEntry entry) {}
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index bb0681c..a0af4ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -23,14 +23,6 @@
 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
 
-import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_BUBBLE;
-import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_DEMOTE;
-import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_FAVORITE;
-import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_HOME;
-import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_MUTE;
-import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_SNOOZE;
-import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_UNBUBBLE;
-
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.annotation.IntDef;
@@ -69,11 +61,13 @@
 import android.widget.TextView;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.phone.ShadeController;
 
 import java.lang.annotation.Retention;
 import java.util.Arrays;
@@ -92,6 +86,7 @@
     ShortcutManager mShortcutManager;
     private PackageManager mPm;
     private VisualStabilityManager mVisualStabilityManager;
+    private ShadeController mShadeController;
 
     private String mPackageName;
     private String mAppName;
@@ -103,24 +98,30 @@
     private NotificationEntry mEntry;
     private StatusBarNotification mSbn;
     private boolean mIsDeviceProvisioned;
-    private int mStartingChannelImportance;
     private boolean mStartedAsBubble;
     private boolean mIsBubbleable;
-    // TODO: remove when launcher api works
-    @VisibleForTesting
-    boolean mShowHomeScreen = false;
 
-    private @UpdateChannelRunnable.Action int mSelectedAction = -1;
+    private @Action int mSelectedAction = -1;
 
     private OnSnoozeClickListener mOnSnoozeClickListener;
     private OnSettingsClickListener mOnSettingsClickListener;
-    private OnAppSettingsClickListener mAppSettingsClickListener;
     private NotificationGuts mGutsContainer;
     private BubbleController mBubbleController;
 
     @VisibleForTesting
     boolean mSkipPost = false;
 
+    @Retention(SOURCE)
+    @IntDef({ACTION_BUBBLE, ACTION_HOME, ACTION_FAVORITE, ACTION_SNOOZE, ACTION_MUTE})
+    private @interface Action {}
+    static final int ACTION_BUBBLE = 0;
+    static final int ACTION_HOME = 1;
+    static final int ACTION_FAVORITE = 2;
+    static final int ACTION_SNOOZE = 3;
+    static final int ACTION_MUTE = 4;
+    static final int ACTION_SETTINGS = 5;
+    static final int ACTION_UNBUBBLE = 6;
+
     private OnClickListener mOnBubbleClick = v -> {
         mSelectedAction = mStartedAsBubble ? ACTION_UNBUBBLE : ACTION_BUBBLE;
         if (mStartedAsBubble) {
@@ -136,12 +137,14 @@
     private OnClickListener mOnHomeClick = v -> {
         mSelectedAction = ACTION_HOME;
         mShortcutManager.requestPinShortcut(mShortcutInfo, null);
+        mShadeController.animateCollapsePanels();
         closeControls(v, true);
     };
 
     private OnClickListener mOnFavoriteClick = v -> {
         mSelectedAction = ACTION_FAVORITE;
-        closeControls(v, true);
+        updateChannel();
+
     };
 
     private OnClickListener mOnSnoozeClick = v -> {
@@ -151,13 +154,8 @@
     };
 
     private OnClickListener mOnMuteClick = v -> {
-      mSelectedAction = ACTION_MUTE;
-      closeControls(v, true);
-    };
-
-    private OnClickListener mOnDemoteClick = v -> {
-        mSelectedAction = ACTION_DEMOTE;
-        closeControls(v, true);
+        mSelectedAction = ACTION_MUTE;
+        updateChannel();
     };
 
     public NotificationConversationInfo(Context context, AttributeSet attrs) {
@@ -197,15 +195,14 @@
         mEntry = entry;
         mSbn = entry.getSbn();
         mPm = pm;
-        mAppSettingsClickListener = onAppSettingsClick;
         mAppName = mPackageName;
         mOnSettingsClickListener = onSettingsClick;
         mNotificationChannel = notificationChannel;
-        mStartingChannelImportance = mNotificationChannel.getImportance();
         mAppUid = mSbn.getUid();
         mDelegatePkg = mSbn.getOpPkg();
         mIsDeviceProvisioned = isDeviceProvisioned;
         mOnSnoozeClickListener = onSnoozeClickListener;
+        mShadeController = Dependency.get(ShadeController.class);
 
         mShortcutManager = shortcutManager;
         mLauncherApps = launcherApps;
@@ -251,9 +248,6 @@
                 mNotificationChannel = mINotificationManager.getConversationNotificationChannel(
                         mContext.getOpPackageName(), UserHandle.getUserId(mAppUid), mPackageName,
                         mNotificationChannel.getId(), false, mConversationId);
-
-                // TODO: ask LA to pin the shortcut once api exists for pinning one shortcut at a
-                // time
             } catch (RemoteException e) {
                 Slog.e(TAG, "Could not create conversation channel", e);
             }
@@ -274,40 +268,24 @@
 
         Button home = findViewById(R.id.home);
         home.setOnClickListener(mOnHomeClick);
-        home.setVisibility(mShowHomeScreen && mShortcutInfo != null
+        home.setVisibility(mShortcutInfo != null
                 && mShortcutManager.isRequestPinShortcutSupported()
                 ? VISIBLE : GONE);
 
-        Button favorite = findViewById(R.id.fave);
+        View favorite = findViewById(R.id.fave);
         favorite.setOnClickListener(mOnFavoriteClick);
-        if (mNotificationChannel.isImportantConversation()) {
-            favorite.setText(R.string.notification_conversation_unfavorite);
-            favorite.setCompoundDrawablesRelative(
-                    mContext.getDrawable(R.drawable.ic_star), null, null, null);
-        } else {
-            favorite.setText(R.string.notification_conversation_favorite);
-            favorite.setCompoundDrawablesRelative(
-                    mContext.getDrawable(R.drawable.ic_star_border), null, null, null);
-        }
 
         Button snooze = findViewById(R.id.snooze);
         snooze.setOnClickListener(mOnSnoozeClick);
 
-        Button mute = findViewById(R.id.mute);
+        View mute = findViewById(R.id.mute);
         mute.setOnClickListener(mOnMuteClick);
-        if (mStartingChannelImportance >= IMPORTANCE_DEFAULT
-                || mStartingChannelImportance == IMPORTANCE_UNSPECIFIED) {
-            mute.setText(R.string.notification_conversation_mute);
-            favorite.setCompoundDrawablesRelative(
-                    mContext.getDrawable(R.drawable.ic_notifications_silence), null, null, null);
-        } else {
-            mute.setText(R.string.notification_conversation_unmute);
-            favorite.setCompoundDrawablesRelative(
-                    mContext.getDrawable(R.drawable.ic_notifications_alert), null, null, null);
-        }
 
-        ImageButton demote = findViewById(R.id.demote);
-        demote.setOnClickListener(mOnDemoteClick);
+        final View settingsButton = findViewById(R.id.info);
+        settingsButton.setOnClickListener(getSettingsOnClickListener());
+        settingsButton.setVisibility(settingsButton.hasOnClickListeners() ? VISIBLE : GONE);
+
+        updateToggleActions();
     }
 
     private void bindHeader() {
@@ -315,26 +293,6 @@
 
         // Delegate
         bindDelegate();
-
-        // Set up app settings link (i.e. Customize)
-        View settingsLinkView = findViewById(R.id.app_settings);
-        Intent settingsIntent = getAppSettingsIntent(mPm, mPackageName,
-                mNotificationChannel,
-                mSbn.getId(), mSbn.getTag());
-        if (settingsIntent != null
-                && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) {
-            settingsLinkView.setVisibility(VISIBLE);
-            settingsLinkView.setOnClickListener((View view) -> {
-                mAppSettingsClickListener.onClick(view, settingsIntent);
-            });
-        } else {
-            settingsLinkView.setVisibility(View.GONE);
-        }
-
-        // System Settings button.
-        final View settingsButton = findViewById(R.id.info);
-        settingsButton.setOnClickListener(getSettingsOnClickListener());
-        settingsButton.setVisibility(settingsButton.hasOnClickListeners() ? VISIBLE : GONE);
     }
 
     private OnClickListener getSettingsOnClickListener() {
@@ -424,15 +382,12 @@
 
     private void bindDelegate() {
         TextView delegateView = findViewById(R.id.delegate_name);
-        TextView dividerView = findViewById(R.id.pkg_divider);
 
         if (!TextUtils.equals(mPackageName, mDelegatePkg)) {
             // this notification was posted by a delegate!
             delegateView.setVisibility(View.VISIBLE);
-            dividerView.setVisibility(View.VISIBLE);
         } else {
             delegateView.setVisibility(View.GONE);
-            dividerView.setVisibility(View.GONE);
         }
     }
 
@@ -492,26 +447,37 @@
         }
     }
 
-    private Intent getAppSettingsIntent(PackageManager pm, String packageName,
-            NotificationChannel channel, int id, String tag) {
-        Intent intent = new Intent(Intent.ACTION_MAIN)
-                .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES)
-                .setPackage(packageName);
-        final List<ResolveInfo> resolveInfos = pm.queryIntentActivities(
-                intent,
-                PackageManager.MATCH_DEFAULT_ONLY
-        );
-        if (resolveInfos == null || resolveInfos.size() == 0 || resolveInfos.get(0) == null) {
-            return null;
+    private void updateToggleActions() {
+        ImageButton favorite = findViewById(R.id.fave);
+        if (mNotificationChannel.isImportantConversation()) {
+            favorite.setContentDescription(
+                    mContext.getString(R.string.notification_conversation_favorite));
+            favorite.setImageResource(R.drawable.ic_important);
+        } else {
+            favorite.setContentDescription(
+                    mContext.getString(R.string.notification_conversation_unfavorite));
+            favorite.setImageResource(R.drawable.ic_important_outline);
         }
-        final ActivityInfo activityInfo = resolveInfos.get(0).activityInfo;
-        intent.setClassName(activityInfo.packageName, activityInfo.name);
-        if (channel != null) {
-            intent.putExtra(Notification.EXTRA_CHANNEL_ID, channel.getId());
+
+        ImageButton mute = findViewById(R.id.mute);
+        if (mNotificationChannel.getImportance() >= IMPORTANCE_DEFAULT
+                || mNotificationChannel.getImportance() == IMPORTANCE_UNSPECIFIED) {
+            mute.setContentDescription(
+                    mContext.getString(R.string.notification_conversation_unmute));
+            mute.setImageResource(R.drawable.ic_notifications_alert);
+        } else {
+            mute.setContentDescription(
+                    mContext.getString(R.string.notification_conversation_mute));
+            mute.setImageResource(R.drawable.ic_notifications_silence);
         }
-        intent.putExtra(Notification.EXTRA_NOTIFICATION_ID, id);
-        intent.putExtra(Notification.EXTRA_NOTIFICATION_TAG, tag);
-        return intent;
+    }
+
+    private void updateChannel() {
+        Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
+        bgHandler.post(
+                new UpdateChannelRunnable(mINotificationManager, mPackageName,
+                        mAppUid, mSelectedAction, mNotificationChannel));
+        mVisualStabilityManager.temporarilyAllowReordering();
     }
 
     /**
@@ -556,11 +522,7 @@
     @Override
     public boolean handleCloseControls(boolean save, boolean force) {
         if (save && mSelectedAction > -1) {
-            Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
-            bgHandler.post(
-                    new UpdateChannelRunnable(mINotificationManager, mPackageName,
-                            mAppUid, mSelectedAction, mNotificationChannel));
-            mVisualStabilityManager.temporarilyAllowReordering();
+            updateChannel();
         }
         return false;
     }
@@ -575,19 +537,7 @@
         return false;
     }
 
-    static class UpdateChannelRunnable implements Runnable {
-
-        @Retention(SOURCE)
-        @IntDef({ACTION_BUBBLE, ACTION_HOME, ACTION_FAVORITE, ACTION_SNOOZE, ACTION_MUTE,
-                ACTION_DEMOTE})
-        private @interface Action {}
-        static final int ACTION_BUBBLE = 0;
-        static final int ACTION_HOME = 1;
-        static final int ACTION_FAVORITE = 2;
-        static final int ACTION_SNOOZE = 3;
-        static final int ACTION_MUTE = 4;
-        static final int ACTION_DEMOTE = 5;
-        static final int ACTION_UNBUBBLE = 6;
+    class UpdateChannelRunnable implements Runnable {
 
         private final INotificationManager mINotificationManager;
         private final String mAppPkg;
@@ -633,10 +583,6 @@
                                     mChannelToUpdate.getOriginalImportance(), IMPORTANCE_DEFAULT));
                         }
                         break;
-                    case ACTION_DEMOTE:
-                        mChannelToUpdate.setDemoted(!mChannelToUpdate.isDemoted());
-                        break;
-
                 }
 
                 if (channelSettingChanged) {
@@ -646,13 +592,7 @@
             } catch (RemoteException e) {
                 Log.e(TAG, "Unable to update notification channel", e);
             }
+            ThreadUtils.postOnMainThread(() -> updateToggleActions());
         }
     }
-
-    @Retention(SOURCE)
-    @IntDef({BEHAVIOR_ALERTING, BEHAVIOR_SILENT, BEHAVIOR_BUBBLE})
-    private @interface AlertingBehavior {}
-    private static final int BEHAVIOR_ALERTING = 0;
-    private static final int BEHAVIOR_SILENT = 1;
-    private static final int BEHAVIOR_BUBBLE = 2;
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 6789c81..352abcf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -58,6 +58,7 @@
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
 import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
@@ -384,6 +385,7 @@
                 guts.resetFalsingCheck();
                 mOnSettingsClickListener.onSettingsClick(sbn.getKey());
                 startAppNotificationSettingsActivity(packageName, appUid, channel, row);
+                notificationInfoView.closeControls(v, false);
             };
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
index f124179..f783245 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
@@ -18,12 +18,8 @@
 
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL;
 
-import android.os.RemoteException;
-import android.service.notification.StatusBarNotification;
-
 import androidx.annotation.NonNull;
 
-import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
@@ -41,14 +37,14 @@
 @Singleton
 public class RowContentBindStage extends BindStage<RowContentBindParams> {
     private final NotificationRowContentBinder mBinder;
-    private final IStatusBarService mStatusBarService;
+    private final NotifInflationErrorManager mNotifInflationErrorManager;
 
     @Inject
     RowContentBindStage(
             NotificationRowContentBinder binder,
-            IStatusBarService statusBarService) {
+            NotifInflationErrorManager errorManager) {
         mBinder = binder;
-        mStatusBarService = statusBarService;
+        mNotifInflationErrorManager = errorManager;
     }
 
     @Override
@@ -78,24 +74,12 @@
         InflationCallback inflationCallback = new InflationCallback() {
             @Override
             public void handleInflationException(NotificationEntry entry, Exception e) {
-                entry.setHasInflationError(true);
-                try {
-                    final StatusBarNotification sbn = entry.getSbn();
-                    mStatusBarService.onNotificationError(
-                            sbn.getPackageName(),
-                            sbn.getTag(),
-                            sbn.getId(),
-                            sbn.getUid(),
-                            sbn.getInitialPid(),
-                            e.getMessage(),
-                            sbn.getUserId());
-                } catch (RemoteException ex) {
-                }
+                mNotifInflationErrorManager.setInflationError(entry, e);
             }
 
             @Override
             public void onAsyncInflationFinished(NotificationEntry entry) {
-                entry.setHasInflationError(false);
+                mNotifInflationErrorManager.clearInflationError(entry);
                 getStageParams(entry).clearDirtyContentViews();
                 callback.onStageFinished(entry);
             }
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 4b9976c..0cc3371 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
@@ -5481,7 +5481,7 @@
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void manageNotifications(View v) {
-        Intent intent = new Intent(Settings.ACTION_ALL_APPS_NOTIFICATION_SETTINGS);
+        Intent intent = new Intent(Settings.ACTION_NOTIFICATION_HISTORY);
         mStatusBar.startActivity(intent, true, true, Intent.FLAG_ACTIVITY_SINGLE_TOP);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index 44a3204..745843d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -375,6 +375,7 @@
                 mDownPoint.set(ev.getX(), ev.getY());
                 mThresholdCrossed = false;
             }
+
         } else if (mAllowGesture) {
             if (!mThresholdCrossed) {
                 if (action == MotionEvent.ACTION_POINTER_DOWN) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 72dfa18..ee31300 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -643,9 +643,12 @@
         if (previewBefore != null) {
             mPreviewContainer.removeView(previewBefore);
         }
+
         if (mLeftIsVoiceAssist) {
-            mLeftPreview = mPreviewInflater.inflatePreviewFromService(
-                    Dependency.get(AssistManager.class).getVoiceInteractorComponentName());
+            if (Dependency.get(AssistManager.class).getVoiceInteractorComponentName() != null) {
+                mLeftPreview = mPreviewInflater.inflatePreviewFromService(
+                        Dependency.get(AssistManager.class).getVoiceInteractorComponentName());
+            }
         } else {
             mLeftPreview = mPreviewInflater.inflatePreview(mLeftButton.getIntent());
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 9e64748..3f5215e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -269,6 +269,17 @@
         }
     };
 
+    private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
+            new DeviceConfig.OnPropertiesChangedListener() {
+        @Override
+        public void onPropertiesChanged(DeviceConfig.Properties properties) {
+            if (properties.getKeyset().contains(NAV_BAR_HANDLE_FORCE_OPAQUE)) {
+                mForceNavBarHandleOpaque = properties.getBoolean(
+                        NAV_BAR_HANDLE_FORCE_OPAQUE, /* defaultValue = */ true);
+            }
+        }
+    };
+
     @Inject
     public NavigationBarFragment(AccessibilityManagerWrapper accessibilityManagerWrapper,
             DeviceProvisionedController deviceProvisionedController, MetricsLogger metricsLogger,
@@ -298,21 +309,6 @@
         mDivider = divider;
         mRecentsOptional = recentsOptional;
         mHandler = mainHandler;
-
-        mForceNavBarHandleOpaque = DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                NAV_BAR_HANDLE_FORCE_OPAQUE,
-                /* defaultValue = */ true);
-        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post,
-                new DeviceConfig.OnPropertiesChangedListener() {
-                    @Override
-                    public void onPropertiesChanged(DeviceConfig.Properties properties) {
-                        if (properties.getKeyset().contains(NAV_BAR_HANDLE_FORCE_OPAQUE)) {
-                            mForceNavBarHandleOpaque = properties.getBoolean(
-                                    NAV_BAR_HANDLE_FORCE_OPAQUE, /* defaultValue = */ true);
-                        }
-                    }
-                });
     }
 
     // ----- Fragment Lifecycle Callbacks -----
@@ -338,6 +334,13 @@
 
         // Respect the latest disabled-flags.
         mCommandQueue.recomputeDisableFlags(mDisplayId, false);
+
+        mForceNavBarHandleOpaque = DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_SYSTEMUI,
+                NAV_BAR_HANDLE_FORCE_OPAQUE,
+                /* defaultValue = */ true);
+        DeviceConfig.addOnPropertiesChangedListener(
+                DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener);
     }
 
     @Override
@@ -346,6 +349,8 @@
         mNavigationModeController.removeListener(this);
         mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener);
         mContentResolver.unregisterContentObserver(mAssistContentObserver);
+
+        DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
index 10b68b9..40b1610 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
@@ -18,8 +18,8 @@
 
 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED;
+
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index ab1c8ad..8729e04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -45,7 +45,7 @@
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.StatusBarWindowBlurController;
+import com.android.systemui.statusbar.NotificationShadeWindowBlurController;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -81,7 +81,7 @@
     private final CommandQueue mCommandQueue;
     private final NotificationShadeWindowView mView;
     private final ShadeController mShadeController;
-    private final StatusBarWindowBlurController mBlurController;
+    private final NotificationShadeWindowBlurController mBlurController;
 
     private GestureDetector mGestureDetector;
     private View mBrightnessMirror;
@@ -123,7 +123,7 @@
             CommandQueue commandQueue,
             ShadeController shadeController,
             DockManager dockManager,
-            @Nullable StatusBarWindowBlurController blurController,
+            @Nullable NotificationShadeWindowBlurController blurController,
             NotificationShadeWindowView statusBarWindowView,
             NotificationPanelViewController notificationPanelViewController) {
         mInjectionInflationController = injectionInflationController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 2b9fc8d..10821d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -107,7 +107,7 @@
     /**
      * Default alpha value for most scrims.
      */
-    public static final float GRADIENT_SCRIM_ALPHA = 0.2f;
+    public static final float SCRIM_ALPHA = 0.2f;
     /**
      * Scrim opacity when the phone is about to wake-up.
      */
@@ -116,12 +116,12 @@
      * A scrim varies its opacity based on a busyness factor, for example
      * how many notifications are currently visible.
      */
-    public static final float GRADIENT_SCRIM_ALPHA_BUSY = 0.7f;
+    public static final float BUSY_SCRIM_ALPHA = 0.54f;
 
     /**
      * The most common scrim, the one under the keyguard.
      */
-    protected static final float SCRIM_BEHIND_ALPHA_KEYGUARD = GRADIENT_SCRIM_ALPHA;
+    protected static final float SCRIM_BEHIND_ALPHA_KEYGUARD = SCRIM_ALPHA;
 
     static final int TAG_KEY_ANIM = R.id.scrim;
     private static final int TAG_START_ALPHA = R.id.scrim_alpha_start;
@@ -481,7 +481,7 @@
             // Darken scrim as you pull down the shade when unlocked
             float behindFraction = getInterpolatedFraction();
             behindFraction = (float) Math.pow(behindFraction, 0.8f);
-            mBehindAlpha = behindFraction * GRADIENT_SCRIM_ALPHA_BUSY;
+            mBehindAlpha = behindFraction * BUSY_SCRIM_ALPHA;
             mInFrontAlpha = 0;
         } else if (mState == ScrimState.KEYGUARD || mState == ScrimState.PULSING) {
             // Either darken of make the scrim transparent when you
@@ -489,7 +489,7 @@
             float interpolatedFract = getInterpolatedFraction();
             float alphaBehind = mState.getBehindAlpha();
             if (mDarkenWhileDragging) {
-                mBehindAlpha = MathUtils.lerp(GRADIENT_SCRIM_ALPHA_BUSY, alphaBehind,
+                mBehindAlpha = MathUtils.lerp(BUSY_SCRIM_ALPHA, alphaBehind,
                         interpolatedFract);
                 mInFrontAlpha = mState.getFrontAlpha();
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 40f8d58..c23fd0a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -92,7 +92,7 @@
     BOUNCER {
         @Override
         public void prepare(ScrimState previousState) {
-            mBehindAlpha = ScrimController.GRADIENT_SCRIM_ALPHA_BUSY;
+            mBehindAlpha = ScrimController.BUSY_SCRIM_ALPHA;
             mFrontAlpha = 0f;
             mBubbleAlpha = 0f;
         }
@@ -106,7 +106,7 @@
         public void prepare(ScrimState previousState) {
             mBehindAlpha = 0;
             mBubbleAlpha = 0f;
-            mFrontAlpha = ScrimController.GRADIENT_SCRIM_ALPHA_BUSY;
+            mFrontAlpha = ScrimController.BUSY_SCRIM_ALPHA;
         }
     },
 
@@ -234,8 +234,8 @@
             mBubbleTint = Color.TRANSPARENT;
 
             mFrontAlpha = ScrimController.TRANSPARENT;
-            mBehindAlpha = ScrimController.GRADIENT_SCRIM_ALPHA_BUSY;
-            mBubbleAlpha = ScrimController.GRADIENT_SCRIM_ALPHA_BUSY;
+            mBehindAlpha = ScrimController.BUSY_SCRIM_ALPHA;
+            mBubbleAlpha = ScrimController.BUSY_SCRIM_ALPHA;
 
             mAnimationDuration = ScrimController.ANIMATION_DURATION;
             mBlankScreen = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 6a046884..0644a42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -19,6 +19,7 @@
 import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
 import static android.view.ViewRootImpl.sNewInsetsMode;
 import static android.view.WindowInsets.Type.navigationBars;
+
 import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_UNLOCK_FADING;
 import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
@@ -95,6 +96,8 @@
     private static String TAG = "StatusBarKeyguardViewManager";
 
     protected final Context mContext;
+    private final ConfigurationController mConfigurationController;
+    private final NavigationModeController mNavigationModeController;
     private final NotificationShadeWindowController mNotificationShadeWindowController;
     private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() {
         @Override
@@ -208,21 +211,14 @@
         mContext = context;
         mViewMediatorCallback = callback;
         mLockPatternUtils = lockPatternUtils;
+        mConfigurationController = configurationController;
+        mNavigationModeController = navigationModeController;
         mNotificationShadeWindowController = notificationShadeWindowController;
         mKeyguardStateController = keyguardStateController;
         mMediaManager = notificationMediaManager;
         mKeyguardUpdateManager = keyguardUpdateMonitor;
-        mKeyguardUpdateManager.registerCallback(mUpdateMonitorCallback);
         mStatusBarStateController = sysuiStatusBarStateController;
-        mStatusBarStateController.addCallback(this);
-        configurationController.addCallback(this);
-        mGesturalNav = QuickStepContract.isGesturalMode(
-                navigationModeController.addListener(this));
         mDockManager = dockManager;
-        if (mDockManager != null) {
-            mDockManager.addListener(mDockEventListener);
-            mIsDocked = mDockManager.isDocked();
-        }
     }
 
     public void registerStatusBar(StatusBar statusBar,
@@ -246,6 +242,20 @@
         notificationPanelViewController.addExpansionListener(this);
         mBypassController = bypassController;
         mNotificationContainer = notificationContainer;
+
+        registerListeners();
+    }
+
+    private void registerListeners() {
+        mKeyguardUpdateManager.registerCallback(mUpdateMonitorCallback);
+        mStatusBarStateController.addCallback(this);
+        mConfigurationController.addCallback(this);
+        mGesturalNav = QuickStepContract.isGesturalMode(
+                mNavigationModeController.addListener(this));
+        if (mDockManager != null) {
+            mDockManager.addListener(mDockEventListener);
+            mIsDocked = mDockManager.isDocked();
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index d2e9262..690d573 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -177,8 +177,10 @@
 
     @Override
     public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
-            int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
-            String description, boolean isWide, int subId, boolean roaming) {
+            int qsType, boolean activityIn, boolean activityOut,
+            CharSequence typeContentDescription,
+            CharSequence typeContentDescriptionHtml, CharSequence description,
+            boolean isWide, int subId, boolean roaming) {
         MobileIconState state = getState(subId);
         if (state == null) {
             return;
@@ -387,7 +389,7 @@
         public int typeId;
         public boolean roaming;
         public boolean needsLeadingPadding;
-        public String typeContentDescription;
+        public CharSequence typeContentDescription;
 
         private MobileIconState(int subId) {
             super();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
index e1bb19a..97d348b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
@@ -111,30 +111,25 @@
     public void setWifiIndicators(final boolean enabled, final IconState statusIcon,
             final IconState qsIcon, final boolean activityIn, final boolean activityOut,
             final String description, boolean isTransient, String secondaryLabel) {
-        post(new Runnable() {
-            @Override
-            public void run() {
-                for (SignalCallback callback : mSignalCallbacks) {
-                    callback.setWifiIndicators(enabled, statusIcon, qsIcon, activityIn, activityOut,
-                            description, isTransient, secondaryLabel);
-                }
+        post(() -> {
+            for (SignalCallback callback : mSignalCallbacks) {
+                callback.setWifiIndicators(enabled, statusIcon, qsIcon, activityIn, activityOut,
+                        description, isTransient, secondaryLabel);
             }
         });
     }
 
     @Override
     public void setMobileDataIndicators(final IconState statusIcon, final IconState qsIcon,
-            final int statusType, final int qsType,final boolean activityIn,
-            final boolean activityOut, final String typeContentDescription,
-            final String description, final boolean isWide, final int subId, boolean roaming) {
-        post(new Runnable() {
-            @Override
-            public void run() {
-                for (SignalCallback signalCluster : mSignalCallbacks) {
-                    signalCluster.setMobileDataIndicators(statusIcon, qsIcon, statusType, qsType,
-                            activityIn, activityOut, typeContentDescription, description, isWide,
-                            subId, roaming);
-                }
+            final int statusType, final int qsType, final boolean activityIn,
+            final boolean activityOut, final CharSequence typeContentDescription,
+            CharSequence typeContentDescriptionHtml, final CharSequence description,
+            final boolean isWide, final int subId, boolean roaming) {
+        post(() -> {
+            for (SignalCallback signalCluster : mSignalCallbacks) {
+                signalCluster.setMobileDataIndicators(statusIcon, qsIcon, statusType, qsType,
+                        activityIn, activityOut, typeContentDescription,
+                        typeContentDescriptionHtml, description, isWide, subId, roaming);
             }
         });
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java
index 159bd41..4fbf5ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java
@@ -49,7 +49,7 @@
     @Override
     public void notifyListeners(SignalCallback callback) {
         boolean ethernetVisible = mCurrentState.connected;
-        String contentDescription = getStringIfExists(getContentDescription());
+        String contentDescription = getTextIfExists(getContentDescription()).toString();
 
         // TODO: wire up data transfer using WifiSignalPoller.
         callback.setEthernetIndicators(new IconState(ethernetVisible, getCurrentIconId(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java
new file mode 100644
index 0000000..07433e1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.tiles.UserDetailItemView;
+
+/**
+ * Displays a user on the keyguard user switcher.
+ */
+public class KeyguardUserDetailItemView extends UserDetailItemView {
+
+    public KeyguardUserDetailItemView(Context context) {
+        this(context, null);
+    }
+
+    public KeyguardUserDetailItemView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public KeyguardUserDetailItemView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public KeyguardUserDetailItemView(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected int getFontSizeDimen() {
+        return R.dimen.kg_user_switcher_text_size;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 28ba125..f6e1681 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -39,6 +39,7 @@
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.text.Html;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -114,9 +115,10 @@
         mDefaults = defaults;
         mSubscriptionInfo = info;
         mPhoneStateListener = new MobilePhoneStateListener((new Handler(receiverLooper))::post);
-        mNetworkNameSeparator = getStringIfExists(R.string.status_bar_network_name_separator);
-        mNetworkNameDefault = getStringIfExists(
-                com.android.internal.R.string.lockscreen_carrier_default);
+        mNetworkNameSeparator = getTextIfExists(R.string.status_bar_network_name_separator)
+                .toString();
+        mNetworkNameDefault = getTextIfExists(
+                com.android.internal.R.string.lockscreen_carrier_default).toString();
 
         mapIconSets();
 
@@ -153,10 +155,6 @@
         updateTelephony();
     }
 
-    public int getDataContentDescription() {
-        return getIcons().mDataContentDescription;
-    }
-
     public void setAirplaneMode(boolean airplaneMode) {
         mCurrentState.airplaneMode = airplaneMode;
         notifyListenersIfNecessary();
@@ -360,8 +358,14 @@
     public void notifyListeners(SignalCallback callback) {
         MobileIconGroup icons = getIcons();
 
-        String contentDescription = getStringIfExists(getContentDescription());
-        String dataContentDescription = getStringIfExists(icons.mDataContentDescription);
+        String contentDescription = getTextIfExists(getContentDescription()).toString();
+        CharSequence dataContentDescriptionHtml = getTextIfExists(icons.mDataContentDescription);
+
+        //TODO: Hacky
+        // The data content description can sometimes be shown in a text view and might come to us
+        // as HTML. Strip any styling here so that listeners don't have to care
+        CharSequence dataContentDescription = Html.fromHtml(
+                dataContentDescriptionHtml.toString(), 0).toString();
         if (mCurrentState.inetCondition == 0) {
             dataContentDescription = mContext.getString(R.string.data_connection_no_internet);
         }
@@ -376,7 +380,7 @@
 
         int qsTypeIcon = 0;
         IconState qsIcon = null;
-        String description = null;
+        CharSequence description = null;
         // Only send data sim callbacks to QS.
         if (mCurrentState.dataSim) {
             qsTypeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.mQsDataType : 0;
@@ -393,8 +397,9 @@
         showDataIcon &= mCurrentState.isDefault || dataDisabled;
         int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.mDataType : 0;
         callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
-                activityIn, activityOut, dataContentDescription, description, icons.mIsWide,
-                mSubscriptionInfo.getSubscriptionId(), mCurrentState.roaming);
+                activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml,
+                description, icons.mIsWide, mSubscriptionInfo.getSubscriptionId(),
+                mCurrentState.roaming);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 71db618..95a9772 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -44,6 +44,7 @@
     void addEmergencyListener(EmergencyListener listener);
     void removeEmergencyListener(EmergencyListener listener);
     boolean hasEmergencyCryptKeeperText();
+
     boolean isRadioOn();
 
     public interface SignalCallback {
@@ -51,10 +52,31 @@
                 boolean activityIn, boolean activityOut, String description, boolean isTransient,
                 String statusLabel) {}
 
+        /**
+         * Callback for listeners to be able to update the state of any UI tracking connectivity
+         *  @param statusIcon the icon that should be shown in the status bar
+         * @param qsIcon the icon to show in Quick Settings
+         * @param statusType the resId of the data type icon (e.g. LTE) to show in the status bar
+         * @param qsType similar to above, the resId of the data type icon to show in Quick Settings
+         * @param activityIn indicates whether there is inbound activity
+         * @param activityOut indicates outbound activity
+         * @param typeContentDescription the contentDescription of the data type
+         * @param typeContentDescriptionHtml the (possibly HTML-styled) contentDescription of the
+         *                                   data type. Suitable for display
+         * @param description description of the network (usually just the network name)
+         * @param isWide //TODO: unused?
+         * @param subId subscription ID for which to update the UI
+         * @param roaming indicates roaming
+         */
         default void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
-                int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
-                String description, boolean isWide, int subId, boolean roaming) {}
+                int qsType, boolean activityIn, boolean activityOut,
+                CharSequence typeContentDescription,
+                CharSequence typeContentDescriptionHtml, CharSequence description,
+                boolean isWide, int subId, boolean roaming) {
+        }
+
         default void setSubs(List<SubscriptionInfo> subs) {}
+
         default void setNoSims(boolean show, boolean simDetected) {}
 
         default void setEthernetIndicators(IconState icon) {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 46143ca..4f382e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -1016,6 +1016,7 @@
                             datatype.equals("4g") ? TelephonyIcons.FOUR_G :
                             datatype.equals("4g+") ? TelephonyIcons.FOUR_G_PLUS :
                             datatype.equals("5g") ? TelephonyIcons.NR_5G :
+                            datatype.equals("5ge") ? TelephonyIcons.LTE_CA_5G_E :
                             datatype.equals("5g+") ? TelephonyIcons.NR_5G_PLUS :
                             datatype.equals("e") ? TelephonyIcons.E :
                             datatype.equals("g") ? TelephonyIcons.G :
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
index fc6e5e2..749b56c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
@@ -17,6 +17,7 @@
 
 import static com.android.systemui.statusbar.policy.NetworkControllerImpl.TAG;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.util.Log;
 
@@ -166,8 +167,8 @@
     /**
      * Returns the resource if resId is not 0, and an empty string otherwise.
      */
-    protected String getStringIfExists(int resId) {
-        return resId != 0 ? mContext.getString(resId) : "";
+    @NonNull CharSequence getTextIfExists(int resId) {
+        return resId != 0 ? mContext.getText(resId) : "";
     }
 
     protected I getIcons() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
index c22ff8b..d9591cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
@@ -214,7 +214,7 @@
             0,
             0,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
-            R.string.data_connection_5ge,
+            R.string.data_connection_5ge_html,
             TelephonyIcons.ICON_5G_E,
             true);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 6baf36c..8bd0f2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -90,7 +90,7 @@
                     || !mHasMobileData || visibleWhenEnabled);
         String wifiDesc = wifiVisible ? mCurrentState.ssid : null;
         boolean ssidPresent = wifiVisible && mCurrentState.ssid != null;
-        String contentDescription = getStringIfExists(getContentDescription());
+        String contentDescription = getTextIfExists(getContentDescription()).toString();
         if (mCurrentState.inetCondition == 0) {
             contentDescription += ("," + mContext.getString(R.string.data_connection_no_internet));
         }
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index 2c7c52e..2e0c035 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -434,6 +434,7 @@
 
         @Override
         public void handleSetListening(boolean listening) {
+            super.handleSetListening(listening);
             if (gm != null) gm.setTile(listening ? this : null);
 
             final ActivityManager am = mContext.getSystemService(ActivityManager.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
index 48a5369..fc331d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
@@ -16,35 +16,185 @@
 
 package com.android.systemui;
 
-import android.test.suitebuilder.annotation.SmallTest;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
-import androidx.test.runner.AndroidJUnit4;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.WallpaperManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManagerGlobal;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.Size;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.SurfaceHolder;
+
+import com.android.systemui.glwallpaper.ImageWallpaperRenderer;
+import com.android.systemui.statusbar.phone.DozeParameters;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 import java.util.concurrent.CountDownLatch;
 
 @SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
 public class ImageWallpaperTest extends SysuiTestCase {
+    private static final int LOW_BMP_WIDTH = 128;
+    private static final int LOW_BMP_HEIGHT = 128;
+    private static final int INVALID_BMP_WIDTH = 1;
+    private static final int INVALID_BMP_HEIGHT = 1;
+    private static final int DISPLAY_WIDTH = 1920;
+    private static final int DISPLAY_HEIGHT = 1080;
+
+    @Mock
+    private SurfaceHolder mSurfaceHolder;
+    @Mock
+    private Context mMockContext;
+    @Mock
+    private Bitmap mWallpaperBitmap;
+    @Mock
+    private DozeParameters mDozeParam;
 
     private CountDownLatch mEventCountdown;
-    private CountDownLatch mAmbientEventCountdown;
 
     @Before
     public void setUp() throws Exception {
+        com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
         MockitoAnnotations.initMocks(this);
         mEventCountdown = new CountDownLatch(1);
-        mAmbientEventCountdown = new CountDownLatch(2);
+
+        WallpaperManager wallpaperManager = mock(WallpaperManager.class);
+        Resources resources = mock(Resources.class);
+
+        when(mMockContext.getSystemService(WallpaperManager.class)).thenReturn(wallpaperManager);
+        when(mMockContext.getResources()).thenReturn(resources);
+        when(resources.getConfiguration()).thenReturn(mock(Configuration.class));
+
+        DisplayInfo displayInfo = new DisplayInfo();
+        displayInfo.logicalWidth = DISPLAY_WIDTH;
+        displayInfo.logicalHeight = DISPLAY_HEIGHT;
+        when(mMockContext.getDisplay()).thenReturn(
+                new Display(mock(DisplayManagerGlobal.class), 0, displayInfo, (Resources) null));
+
+        when(wallpaperManager.getBitmap(false)).thenReturn(mWallpaperBitmap);
+        when(mWallpaperBitmap.getColorSpace()).thenReturn(ColorSpace.get(ColorSpace.Named.SRGB));
+        when(mWallpaperBitmap.getConfig()).thenReturn(Bitmap.Config.ARGB_8888);
+        when(mDozeParam.getDisplayNeedsBlanking()).thenReturn(false);
+    }
+
+    private ImageWallpaper createImageWallpaper() {
+        return new ImageWallpaper(mDozeParam) {
+            @Override
+            public Engine onCreateEngine() {
+                return new GLEngine(mMockContext, mDozeParam) {
+                    @Override
+                    public Context getDisplayContext() {
+                        return mMockContext;
+                    }
+
+                    @Override
+                    public SurfaceHolder getSurfaceHolder() {
+                        return mSurfaceHolder;
+                    }
+
+                    @Override
+                    public void setFixedSizeAllowed(boolean allowed) {
+                        super.setFixedSizeAllowed(allowed);
+                        assertWithMessage("mFixedSizeAllowed should be true").that(
+                                allowed).isTrue();
+                        mEventCountdown.countDown();
+                    }
+                };
+            }
+        };
+    }
+
+    private ImageWallpaperRenderer createImageWallpaperRenderer(ImageWallpaper.GLEngine engine) {
+        return new ImageWallpaperRenderer(mMockContext, engine) {
+            @Override
+            public void startProcessingImage() {
+                loadBitmap();
+            }
+        };
     }
 
     @Test
-    public void testDeliversAmbientModeChanged() {
-        //TODO: We need add tests for GLEngine.
+    public void testBitmapWallpaper_normal() {
+        // Will use a image wallpaper with dimensions DISPLAY_WIDTH x DISPLAY_WIDTH.
+        // Then we expect the surface size will be also DISPLAY_WIDTH x DISPLAY_WIDTH.
+        // Finally, we assert the transition will not be stopped.
+        verifySurfaceSizeAndAssertTransition(DISPLAY_WIDTH /* bmpWidth */,
+                DISPLAY_WIDTH /* bmpHeight */,
+                DISPLAY_WIDTH /* surfaceWidth */,
+                DISPLAY_WIDTH /* surfaceHeight */,
+                false /* assertion */);
     }
 
-    // TODO: Add more test cases for GLEngine, tracing in b/124838911.
+    @Test
+    public void testBitmapWallpaper_low_resolution() {
+        // Will use a image wallpaper with dimensions BMP_WIDTH x BMP_HEIGHT.
+        // Then we expect the surface size will be also BMP_WIDTH x BMP_HEIGHT.
+        // Finally, we assert the transition will be stopped.
+        verifySurfaceSizeAndAssertTransition(LOW_BMP_WIDTH /* bmpWidth */,
+                LOW_BMP_HEIGHT /* bmpHeight */,
+                LOW_BMP_WIDTH /* surfaceWidth */,
+                LOW_BMP_HEIGHT /* surfaceHeight */,
+                true /* assertion */);
+    }
+
+    @Test
+    public void testBitmapWallpaper_too_small() {
+        // Will use a image wallpaper with dimensions INVALID_BMP_WIDTH x INVALID_BMP_HEIGHT.
+        // Then we expect the surface size will be also MIN_SURFACE_WIDTH x MIN_SURFACE_HEIGHT.
+        // Finally, we assert the transition will be stopped.
+        verifySurfaceSizeAndAssertTransition(INVALID_BMP_WIDTH /* bmpWidth */,
+                INVALID_BMP_HEIGHT /* bmpHeight */,
+                ImageWallpaper.GLEngine.MIN_SURFACE_WIDTH /* surfaceWidth */,
+                ImageWallpaper.GLEngine.MIN_SURFACE_HEIGHT /* surfaceHeight */,
+                true /* assertion */);
+    }
+
+    private void verifySurfaceSizeAndAssertTransition(int bmpWidth, int bmpHeight,
+            int surfaceWidth, int surfaceHeight, boolean assertion) {
+        ImageWallpaper.GLEngine wallpaperEngine =
+                (ImageWallpaper.GLEngine) createImageWallpaper().onCreateEngine();
+
+        ImageWallpaper.GLEngine engineSpy = spy(wallpaperEngine);
+        when(engineSpy.mIsHighEndGfx).thenReturn(true);
+
+        when(mWallpaperBitmap.getWidth()).thenReturn(bmpWidth);
+        when(mWallpaperBitmap.getHeight()).thenReturn(bmpHeight);
+
+        ImageWallpaperRenderer renderer = createImageWallpaperRenderer(engineSpy);
+        doReturn(renderer).when(engineSpy).getRendererInstance();
+        engineSpy.onCreate(engineSpy.getSurfaceHolder());
+
+        verify(mSurfaceHolder, times(1)).setFixedSize(surfaceWidth, surfaceHeight);
+        assertWithMessage("setFixedSizeAllowed should have been called.").that(
+                mEventCountdown.getCount()).isEqualTo(0);
+
+        Size frameSize = renderer.reportSurfaceSize();
+        Rect frame = new Rect(0, 0, frameSize.getWidth(), frameSize.getHeight());
+        when(mSurfaceHolder.getSurfaceFrame()).thenReturn(frame);
+
+        assertThat(engineSpy.checkIfShouldStopTransition()).isEqualTo(assertion);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
index 25efd32..7fb711f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
@@ -28,6 +28,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.DumpController;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.classifier.brightline.BrightLineFalsingManager;
 import com.android.systemui.dock.DockManager;
@@ -53,6 +54,8 @@
     PluginManager mPluginManager;
     @Mock(stubOnly = true)
     ProximitySensor mProximitySensor;
+    @Mock(stubOnly = true)
+    private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private FalsingManagerProxy mProxy;
     private DeviceConfigProxy mDeviceConfig;
     private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
@@ -63,7 +66,6 @@
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
         mDeviceConfig = new DeviceConfigProxyFake();
         mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
                 BRIGHTLINE_FALSING_MANAGER_ENABLED, "false", false);
@@ -79,7 +81,8 @@
     @Test
     public void test_brightLineFalsingManagerDisabled() {
         mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics,
-                mProximitySensor, mDeviceConfig, mDockManager, mUiBgExecutor);
+                mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor,
+                new DumpController(), mUiBgExecutor);
         assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
     }
 
@@ -89,14 +92,16 @@
                 BRIGHTLINE_FALSING_MANAGER_ENABLED, "true", false);
         mExecutor.runAllReady();
         mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics,
-                mProximitySensor, mDeviceConfig, mDockManager, mUiBgExecutor);
+                mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor,
+                new DumpController(), mUiBgExecutor);
         assertThat(mProxy.getInternalFalsingManager(), instanceOf(BrightLineFalsingManager.class));
     }
 
     @Test
     public void test_brightLineFalsingManagerToggled() throws InterruptedException {
         mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics,
-                mProximitySensor, mDeviceConfig, mDockManager, mUiBgExecutor);
+                mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor,
+                new DumpController(), mUiBgExecutor);
         assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
 
         mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java
new file mode 100644
index 0000000..0aaa3b6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.classifier.brightline;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.util.DisplayMetrics;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dock.DockManager;
+import com.android.systemui.dock.DockManagerFake;
+import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.util.DeviceConfigProxyFake;
+import com.android.systemui.util.sensors.ProximitySensor;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class BrightLineFalsingManagerTest extends SysuiTestCase {
+
+
+    @Mock
+    private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    @Mock
+    private ProximitySensor mProximitySensor;
+
+    private BrightLineFalsingManager mFalsingManager;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        DisplayMetrics dm = new DisplayMetrics();
+        dm.xdpi = 100;
+        dm.ydpi = 100;
+        dm.widthPixels = 100;
+        dm.heightPixels = 100;
+        FalsingDataProvider falsingDataProvider = new FalsingDataProvider(dm);
+        DeviceConfigProxy deviceConfigProxy = new DeviceConfigProxyFake();
+        DockManager dockManager = new DockManagerFake();
+        mFalsingManager = new BrightLineFalsingManager(falsingDataProvider,
+                mKeyguardUpdateMonitor, mProximitySensor, deviceConfigProxy, dockManager);
+    }
+
+    @Test
+    public void testRegisterSensor() {
+        mFalsingManager.onScreenTurningOn();
+        verify(mProximitySensor).register(any(ProximitySensor.ProximitySensorListener.class));
+    }
+
+    @Test
+    public void testUnregisterSensor() {
+        mFalsingManager.onScreenTurningOn();
+        reset(mProximitySensor);
+        mFalsingManager.onScreenOff();
+        verify(mProximitySensor).unregister(any(ProximitySensor.ProximitySensorListener.class));
+    }
+
+    @Test
+    public void testUnregisterSensor_QS() {
+        mFalsingManager.onScreenTurningOn();
+        reset(mProximitySensor);
+        mFalsingManager.setQsExpanded(true);
+        verify(mProximitySensor).unregister(any(ProximitySensor.ProximitySensorListener.class));
+        mFalsingManager.setQsExpanded(false);
+        verify(mProximitySensor).register(any(ProximitySensor.ProximitySensorListener.class));
+    }
+
+    @Test
+    public void testUnregisterSensor_Bouncer() {
+        mFalsingManager.onScreenTurningOn();
+        reset(mProximitySensor);
+        mFalsingManager.onBouncerShown();
+        verify(mProximitySensor).unregister(any(ProximitySensor.ProximitySensorListener.class));
+        mFalsingManager.onBouncerHidden();
+        verify(mProximitySensor).register(any(ProximitySensor.ProximitySensorListener.class));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
index e3bcdc8..751217f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
@@ -39,6 +39,7 @@
 import com.android.systemui.util.time.FakeSystemClock
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNotEquals
 import org.junit.Assert.assertTrue
 import org.junit.Before
 import org.junit.Test
@@ -225,7 +226,7 @@
         val newControlInfo = TEST_CONTROL_INFO.copy(controlTitle = TEST_CONTROL_TITLE_2)
         val control = builderFromInfo(newControlInfo).build()
 
-        controller.loadForComponent(TEST_COMPONENT) {}
+        controller.loadForComponent(TEST_COMPONENT) { _, _ -> Unit }
 
         reset(persistenceWrapper)
         verify(bindingController).bindAndLoad(eq(TEST_COMPONENT),
@@ -293,11 +294,13 @@
         var loaded = false
         val control = builderFromInfo(TEST_CONTROL_INFO).build()
 
-        controller.loadForComponent(TEST_COMPONENT) {
+        controller.loadForComponent(TEST_COMPONENT) { controls, favorites ->
             loaded = true
-            assertEquals(1, it.size)
-            val controlStatus = it[0]
+            assertEquals(1, controls.size)
+            val controlStatus = controls[0]
             assertEquals(ControlStatus(control, false), controlStatus)
+
+            assertTrue(favorites.isEmpty())
         }
 
         verify(bindingController).bindAndLoad(eq(TEST_COMPONENT),
@@ -315,14 +318,17 @@
         val control2 = builderFromInfo(TEST_CONTROL_INFO_2).build()
         controller.changeFavoriteStatus(TEST_CONTROL_INFO, true)
 
-        controller.loadForComponent(TEST_COMPONENT) {
+        controller.loadForComponent(TEST_COMPONENT) { controls, favorites ->
             loaded = true
-            assertEquals(2, it.size)
-            val controlStatus = it.first { it.control.controlId == TEST_CONTROL_ID }
+            assertEquals(2, controls.size)
+            val controlStatus = controls.first { it.control.controlId == TEST_CONTROL_ID }
             assertEquals(ControlStatus(control, true), controlStatus)
 
-            val controlStatus2 = it.first { it.control.controlId == TEST_CONTROL_ID_2 }
+            val controlStatus2 = controls.first { it.control.controlId == TEST_CONTROL_ID_2 }
             assertEquals(ControlStatus(control2, false), controlStatus2)
+
+            assertEquals(1, favorites.size)
+            assertEquals(TEST_CONTROL_ID, favorites[0])
         }
 
         verify(bindingController).bindAndLoad(eq(TEST_COMPONENT),
@@ -338,13 +344,16 @@
         var loaded = false
         controller.changeFavoriteStatus(TEST_CONTROL_INFO, true)
 
-        controller.loadForComponent(TEST_COMPONENT) {
+        controller.loadForComponent(TEST_COMPONENT) { controls, favorites ->
             loaded = true
-            assertEquals(1, it.size)
-            val controlStatus = it[0]
+            assertEquals(1, controls.size)
+            val controlStatus = controls[0]
             assertEquals(TEST_CONTROL_ID, controlStatus.control.controlId)
             assertTrue(controlStatus.favorite)
             assertTrue(controlStatus.removed)
+
+            assertEquals(1, favorites.size)
+            assertEquals(TEST_CONTROL_ID, favorites[0])
         }
 
         verify(bindingController).bindAndLoad(eq(TEST_COMPONENT),
@@ -361,7 +370,7 @@
         val newControlInfo = TEST_CONTROL_INFO.copy(controlTitle = TEST_CONTROL_TITLE_2)
         val control = builderFromInfo(newControlInfo).build()
 
-        controller.loadForComponent(TEST_COMPONENT) {}
+        controller.loadForComponent(TEST_COMPONENT) { _, _ -> Unit }
 
         verify(bindingController).bindAndLoad(eq(TEST_COMPONENT),
                 capture(controlLoadCallbackCaptor))
@@ -483,4 +492,81 @@
         assertEquals(1, controller.countFavoritesForComponent(TEST_COMPONENT))
         assertEquals(1, controller.countFavoritesForComponent(TEST_COMPONENT_2))
     }
+
+    @Test
+    fun testGetFavoritesForComponent() {
+        controller.changeFavoriteStatus(TEST_CONTROL_INFO, true)
+        assertEquals(listOf(TEST_CONTROL_INFO), controller.getFavoritesForComponent(TEST_COMPONENT))
+    }
+
+    @Test
+    fun testGetFavoritesForComponent_otherComponent() {
+        controller.changeFavoriteStatus(TEST_CONTROL_INFO_2, true)
+        assertTrue(controller.getFavoritesForComponent(TEST_COMPONENT).isEmpty())
+    }
+
+    @Test
+    fun testGetFavoritesForComponent_multipleInOrder() {
+        val controlInfo = ControlInfo(TEST_COMPONENT, "id", "title", 0)
+
+        controller.changeFavoriteStatus(TEST_CONTROL_INFO, true)
+        controller.changeFavoriteStatus(controlInfo, true)
+
+        assertEquals(listOf(TEST_CONTROL_INFO, controlInfo),
+            controller.getFavoritesForComponent(TEST_COMPONENT))
+
+        controller.clearFavorites()
+
+        controller.changeFavoriteStatus(controlInfo, true)
+        controller.changeFavoriteStatus(TEST_CONTROL_INFO, true)
+
+        assertEquals(listOf(controlInfo, TEST_CONTROL_INFO),
+            controller.getFavoritesForComponent(TEST_COMPONENT))
+    }
+
+    @Test
+    fun testReplaceFavoritesForComponent_noFavorites() {
+        controller.replaceFavoritesForComponent(TEST_COMPONENT, listOf(TEST_CONTROL_INFO))
+
+        assertEquals(1, controller.countFavoritesForComponent(TEST_COMPONENT))
+        assertEquals(listOf(TEST_CONTROL_INFO), controller.getFavoritesForComponent(TEST_COMPONENT))
+    }
+
+    @Test
+    fun testReplaceFavoritesForComponent_differentComponentsAreFilteredOut() {
+        controller.replaceFavoritesForComponent(TEST_COMPONENT,
+            listOf(TEST_CONTROL_INFO, TEST_CONTROL_INFO_2))
+
+        assertEquals(1, controller.countFavoritesForComponent(TEST_COMPONENT))
+        assertEquals(listOf(TEST_CONTROL_INFO), controller.getFavoritesForComponent(TEST_COMPONENT))
+    }
+
+    @Test
+    fun testReplaceFavoritesForComponent_oldFavoritesRemoved() {
+        val controlInfo = ControlInfo(TEST_COMPONENT, "id", "title", 0)
+        assertNotEquals(TEST_CONTROL_INFO, controlInfo)
+
+        controller.changeFavoriteStatus(controlInfo, true)
+        controller.replaceFavoritesForComponent(TEST_COMPONENT, listOf(TEST_CONTROL_INFO))
+
+        assertEquals(1, controller.countFavoritesForComponent(TEST_COMPONENT))
+        assertEquals(listOf(TEST_CONTROL_INFO), controller.getFavoritesForComponent(TEST_COMPONENT))
+    }
+
+    @Test
+    fun testReplaceFavoritesForComponent_favoritesInOrder() {
+        val controlInfo = ControlInfo(TEST_COMPONENT, "id", "title", 0)
+
+        val listOrder1 = listOf(TEST_CONTROL_INFO, controlInfo)
+        controller.replaceFavoritesForComponent(TEST_COMPONENT, listOrder1)
+
+        assertEquals(2, controller.countFavoritesForComponent(TEST_COMPONENT))
+        assertEquals(listOrder1, controller.getFavoritesForComponent(TEST_COMPONENT))
+
+        val listOrder2 = listOf(controlInfo, TEST_CONTROL_INFO)
+        controller.replaceFavoritesForComponent(TEST_COMPONENT, listOrder2)
+
+        assertEquals(2, controller.countFavoritesForComponent(TEST_COMPONENT))
+        assertEquals(listOrder2, controller.getFavoritesForComponent(TEST_COMPONENT))
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoriteModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoriteModelTest.kt
new file mode 100644
index 0000000..9ffc29e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoriteModelTest.kt
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.management
+
+import android.app.PendingIntent
+import android.service.controls.Control
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.controls.ControlStatus
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Assert.fail
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+
+open class FavoriteModelTest : SysuiTestCase() {
+
+    @Mock
+    lateinit var pendingIntent: PendingIntent
+    @Mock
+    lateinit var allAdapter: ControlAdapter
+    @Mock
+    lateinit var favoritesAdapter: ControlAdapter
+
+    val idPrefix = "controlId"
+    val favoritesIndices = listOf(7, 3, 1, 9)
+    val favoritesList = favoritesIndices.map { "controlId$it" }
+    lateinit var controls: List<ControlStatus>
+
+    lateinit var model: FavoriteModel
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        // controlId0 --> zone = 0
+        // controlId1 --> zone = 1, favorite
+        // controlId2 --> zone = 2
+        // controlId3 --> zone = 0, favorite
+        // controlId4 --> zone = 1
+        // controlId5 --> zone = 2
+        // controlId6 --> zone = 0
+        // controlId7 --> zone = 1, favorite
+        // controlId8 --> zone = 2
+        // controlId9 --> zone = 0, favorite
+        controls = (0..9).map {
+            ControlStatus(
+                    Control.StatelessBuilder("$idPrefix$it", pendingIntent)
+                            .setZone((it % 3).toString())
+                            .build(),
+                    it in favoritesIndices
+            )
+        }
+
+        model = FavoriteModel(controls, favoritesList, favoritesAdapter, allAdapter)
+    }
+}
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class FavoriteModelNonParametrizedTests : FavoriteModelTest() {
+    @Test
+    fun testAll() {
+        // Zones are sorted alphabetically
+        val expected = listOf(
+                ZoneNameWrapper("0"),
+                ControlWrapper(controls[0]),
+                ControlWrapper(controls[3]),
+                ControlWrapper(controls[6]),
+                ControlWrapper(controls[9]),
+                ZoneNameWrapper("1"),
+                ControlWrapper(controls[1]),
+                ControlWrapper(controls[4]),
+                ControlWrapper(controls[7]),
+                ZoneNameWrapper("2"),
+                ControlWrapper(controls[2]),
+                ControlWrapper(controls[5]),
+                ControlWrapper(controls[8])
+        )
+        assertEquals(expected, model.all)
+    }
+
+    @Test
+    fun testFavoritesInOrder() {
+        val expected = favoritesIndices.map { ControlWrapper(controls[it]) }
+        assertEquals(expected, model.favorites)
+    }
+
+    @Test
+    fun testChangeFavoriteStatus_addFavorite() {
+        val controlToAdd = 6
+        model.changeFavoriteStatus("$idPrefix$controlToAdd", true)
+
+        val pair = model.all.findControl(controlToAdd)
+        pair?.let {
+            assertTrue(it.second.favorite)
+            assertEquals(it.second, model.favorites.last().controlStatus)
+            verify(favoritesAdapter).notifyItemInserted(model.favorites.size - 1)
+            verify(allAdapter).notifyItemChanged(it.first)
+            verifyNoMoreInteractions(favoritesAdapter, allAdapter)
+        } ?: run {
+            fail("control not found")
+        }
+    }
+
+    @Test
+    fun testChangeFavoriteStatus_removeFavorite() {
+        val controlToRemove = 3
+        model.changeFavoriteStatus("$idPrefix$controlToRemove", false)
+
+        val pair = model.all.findControl(controlToRemove)
+        pair?.let {
+            assertFalse(it.second.favorite)
+            assertTrue(model.favorites.none {
+                it.controlStatus.control.controlId == "$idPrefix$controlToRemove"
+            })
+            verify(favoritesAdapter).notifyItemRemoved(favoritesIndices.indexOf(controlToRemove))
+            verify(allAdapter).notifyItemChanged(it.first)
+            verifyNoMoreInteractions(favoritesAdapter, allAdapter)
+        } ?: run {
+            fail("control not found")
+        }
+    }
+
+    @Test
+    fun testChangeFavoriteStatus_sameStatus() {
+        model.changeFavoriteStatus("${idPrefix}7", true)
+        model.changeFavoriteStatus("${idPrefix}6", false)
+
+        val expected = favoritesIndices.map { ControlWrapper(controls[it]) }
+        assertEquals(expected, model.favorites)
+
+        verifyNoMoreInteractions(favoritesAdapter, allAdapter)
+    }
+
+    private fun List<ElementWrapper>.findControl(controlIndex: Int): Pair<Int, ControlStatus>? {
+        val index = indexOfFirst {
+            it is ControlWrapper &&
+                it.controlStatus.control.controlId == "$idPrefix$controlIndex"
+        }
+        return if (index == -1) null else index to (get(index) as ControlWrapper).controlStatus
+    }
+}
+
+@SmallTest
+@RunWith(Parameterized::class)
+class FavoriteModelParameterizedTest(val from: Int, val to: Int) : FavoriteModelTest() {
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0} -> {1}")
+        fun data(): Collection<Array<Int>> {
+            return (0..3).flatMap { from ->
+                (0..3).map { to ->
+                    arrayOf(from, to)
+                }
+            }.filterNot { it[0] == it[1] }
+        }
+    }
+
+    @Test
+    fun testMoveItem() {
+        val originalFavorites = model.favorites.toList()
+        val originalFavoritesIds =
+                model.favorites.map { it.controlStatus.control.controlId }.toSet()
+        model.onMoveItem(from, to)
+        assertEquals(originalFavorites[from], model.favorites[to])
+        // Check that we still have the same favorites
+        assertEquals(originalFavoritesIds,
+                model.favorites.map { it.controlStatus.control.controlId }.toSet())
+
+        verify(favoritesAdapter).notifyItemMoved(from, to)
+
+        verifyNoMoreInteractions(allAdapter, favoritesAdapter)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index 399f723f..9117ea8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -173,6 +173,21 @@
     }
 
     @Test
+    public void testPulsing_withoutLightSensor_setsAoDDimmingScrimTransparent() throws Exception {
+        mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
+                null /* sensor */, mBroadcastDispatcher, mDozeHost, null /* handler */,
+                DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY,
+                true /* debuggable */);
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mScreen.transitionTo(INITIALIZED, DOZE);
+        reset(mDozeHost);
+
+        mScreen.transitionTo(DOZE, DOZE_REQUEST_PULSE);
+
+        verify(mDozeHost).setAodDimmingScrim(eq(0f));
+    }
+
+    @Test
     public void testDockedAod_usesLightSensor() {
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE_AOD_DOCKED);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupControllerTest.java
index 1bfe1b1..715087d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupControllerTest.java
@@ -217,6 +217,6 @@
         mSignalCallback.setMobileDataIndicators(
                 mock(NetworkController.IconState.class),
                 mock(NetworkController.IconState.class),
-                0, 0, true, true, "", "", true, 0, true);
+                0, 0, true, true, "", "", "", true, 0, true);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index b7e9f07..d668458 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -42,6 +42,7 @@
 import com.android.systemui.SysuiBaseFragmentTest;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.CommandQueue;
@@ -104,7 +105,8 @@
                 mock(QSFactoryImpl.class), new Handler(), Looper.myLooper(),
                 mock(PluginManager.class), mock(TunerService.class),
                 () -> mock(AutoTileManager.class), mock(DumpController.class),
-                mock(BroadcastDispatcher.class), Optional.of(mock(StatusBar.class)));
+                mock(BroadcastDispatcher.class), Optional.of(mock(StatusBar.class)),
+                mock(QSLogger.class));
         qs.setHost(host);
 
         qs.setListening(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
index 38cdee4..b126d36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
@@ -35,9 +35,12 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.DumpController;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.plugins.qs.QSTileView;
 import com.android.systemui.qs.customize.QSCustomizer;
+import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 
 import org.junit.Before;
@@ -65,6 +68,12 @@
     private QSCustomizer mCustomizer;
     @Mock
     private QSTileImpl dndTile;
+    @Mock
+    private BroadcastDispatcher mBroadcastDispatcher;
+    @Mock
+    private DumpController mDumpController;
+    @Mock
+    private QSLogger mQSLogger;
     private ViewGroup mParentView;
     @Mock
     private QSDetail.Callback mCallback;
@@ -78,7 +87,8 @@
         mTestableLooper = TestableLooper.get(this);
         mTestableLooper.runWithLooper(() -> {
             mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
-            mQsPanel = new QSPanel(mContext, null);
+            mQsPanel = new QSPanel(mContext, null, mDumpController, mBroadcastDispatcher,
+                    mQSLogger);
             // Provides a parent with non-zero size for QSPanel
             mParentView = new FrameLayout(mContext);
             mParentView.addView(mQsPanel);
@@ -97,8 +107,10 @@
     public void testSetExpanded_Metrics() {
         mQsPanel.setExpanded(true);
         verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(true));
+        verify(mQSLogger).logPanelExpanded(true, mQsPanel.getDumpableTag());
         mQsPanel.setExpanded(false);
         verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(false));
+        verify(mQSLogger).logPanelExpanded(false, mQsPanel.getDumpableTag());
     }
 
     @Test
@@ -110,6 +122,14 @@
         verify(mCallback).onShowingDetail(any(), anyInt(), anyInt());
     }
 
+    @Test
+    public void setListening() {
+        when(dndTile.getTileSpec()).thenReturn("dnd");
+
+        mQsPanel.setListening(true);
+        verify(mQSLogger).logAllTilesChangeListening(true, mQsPanel.getDumpableTag(), "dnd");
+    }
+
 /*    @Test
     public void testOpenDetailsWithNullParameter_NoException() {
         mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 34111e2..d0c32ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -23,6 +23,7 @@
 
 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;
@@ -45,6 +46,7 @@
 import com.android.systemui.plugins.qs.QSFactory;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.external.CustomTile;
+import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.shared.plugins.PluginManager;
@@ -95,6 +97,8 @@
     @Mock
     private StatusBar mStatusBar;
     @Mock
+    private QSLogger mQSLogger;
+    @Mock
     private CustomTile mCustomTile;
 
     private Handler mHandler;
@@ -108,7 +112,7 @@
         mHandler = new Handler(mLooper.getLooper());
         mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler,
                 mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpController,
-                mBroadcastDispatcher, mStatusBar);
+                mBroadcastDispatcher, mStatusBar, mQSLogger);
         setUpTileFactory();
         Settings.Secure.putStringForUser(mContext.getContentResolver(), QSTileHost.TILES_SETTING,
                 "", ActivityManager.getCurrentUser());
@@ -187,6 +191,7 @@
         assertEquals(1, mQSTileHost.getTiles().size());
         QSTile element = CollectionUtils.firstOrNull(mQSTileHost.getTiles());
         assertTrue(element instanceof TestTile1);
+        verify(mQSLogger).logTileAdded("spec1");
     }
 
     @Test
@@ -200,6 +205,9 @@
         QSTile[] elements = mQSTileHost.getTiles().toArray(new QSTile[0]);
         assertTrue(elements[0] instanceof TestTile1);
         assertTrue(elements[1] instanceof TestTile2);
+
+        verify(mQSLogger).logTileAdded("spec1");
+        verify(mQSLogger).logTileAdded("spec2");
     }
 
     @Test
@@ -210,6 +218,8 @@
         mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "default");
         assertEquals(1, mQSTileHost.getTiles().size());
         assertEquals(mCustomTile, CollectionUtils.firstOrNull(mQSTileHost.getTiles()));
+
+        verify(mQSLogger).logTileAdded(CUSTOM_TILE_SPEC);
     }
 
     private static class TestQSTileHost extends QSTileHost {
@@ -217,10 +227,10 @@
                 QSFactoryImpl defaultFactory, Handler mainHandler, Looper bgLooper,
                 PluginManager pluginManager, TunerService tunerService,
                 Provider<AutoTileManager> autoTiles, DumpController dumpController,
-                BroadcastDispatcher broadcastDispatcher, StatusBar statusBar) {
+                BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger) {
             super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager,
                     tunerService, autoTiles, dumpController, broadcastDispatcher,
-                    Optional.of(statusBar));
+                    Optional.of(statusBar), qsLogger);
         }
 
         @Override
@@ -280,9 +290,6 @@
         }
 
         @Override
-        protected void handleSetListening(boolean listening) {}
-
-        @Override
         public CharSequence getTileLabel() {
             return null;
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index 2737b19..ab6b304 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -34,6 +34,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.phone.AutoTileManager;
@@ -78,6 +79,8 @@
     private DumpController mDumpController;
     @Mock
     private StatusBar mStatusBar;
+    @Mock
+    private QSLogger mQSLogger;
 
     @Before
     public void setUp() throws Exception {
@@ -94,7 +97,8 @@
                 () -> mAutoTileManager,
                 mDumpController,
                 mBroadcastDispatcher,
-                Optional.of(mStatusBar));
+                Optional.of(mStatusBar),
+                mQSLogger);
         mTileService = new TestTileServices(host, Looper.getMainLooper(), mBroadcastDispatcher);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 6bd24b9..4fada33 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -25,6 +25,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Matchers.argThat;
 import static org.mockito.Mockito.clearInvocations;
@@ -38,6 +39,7 @@
 
 import android.content.Intent;
 import android.metrics.LogMaker;
+import android.service.quicksettings.Tile;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
@@ -52,6 +54,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.statusbar.StatusBarState;
 
 import org.junit.Before;
@@ -61,6 +64,7 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.ArgumentMatcher;
 import org.mockito.Captor;
+import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidTestingRunner.class)
@@ -69,6 +73,11 @@
 public class QSTileImplTest extends SysuiTestCase {
 
     public static final int POSITION = 14;
+    private static final String SPEC = "spec";
+
+    @Mock
+    private QSLogger mQsLogger;
+
     private TestableLooper mTestableLooper;
     private TileImpl mTile;
     private QSTileHost mHost;
@@ -81,7 +90,6 @@
     @Before
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
-        String spec = "spec";
         mTestableLooper = TestableLooper.get(this);
         mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
         mDependency.injectMockDependency(ActivityStarter.class);
@@ -89,12 +97,13 @@
         mStatusBarStateController =
             mDependency.injectMockDependency(StatusBarStateController.class);
         mHost = mock(QSTileHost.class);
-        when(mHost.indexOf(spec)).thenReturn(POSITION);
+        when(mHost.indexOf(SPEC)).thenReturn(POSITION);
         when(mHost.getContext()).thenReturn(mContext.getBaseContext());
+        when(mHost.getQSLogger()).thenReturn(mQsLogger);
 
         mTile = spy(new TileImpl(mHost));
         mTile.mHandler = mTile.new H(mTestableLooper.getLooper());
-        mTile.setTileSpec(spec);
+        mTile.setTileSpec(SPEC);
     }
 
     @Test
@@ -104,6 +113,14 @@
     }
 
     @Test
+    public void testClick_log() {
+        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
+
+        mTile.click();
+        verify(mQsLogger).logTileClick(SPEC, StatusBarState.SHADE, Tile.STATE_ACTIVE);
+    }
+
+    @Test
     public void testClick_Metrics_Status_Bar_Status() {
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
         mTile.click();
@@ -119,6 +136,14 @@
     }
 
     @Test
+    public void testSecondaryClick_log() {
+        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
+
+        mTile.secondaryClick();
+        verify(mQsLogger).logTileSecondaryClick(SPEC, StatusBarState.SHADE, Tile.STATE_ACTIVE);
+    }
+
+    @Test
     public void testSecondaryClick_Metrics_Status_Bar_Status() {
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
         mTile.secondaryClick();
@@ -133,6 +158,15 @@
         verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_LONG_PRESS)));
     }
 
+
+    @Test
+    public void testLongClick_log() {
+        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
+
+        mTile.longClick();
+        verify(mQsLogger).logTileLongClick(SPEC, StatusBarState.SHADE, Tile.STATE_ACTIVE);
+    }
+
     @Test
     public void testLongClick_Metrics_Status_Bar_Status() {
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
@@ -200,6 +234,21 @@
         verify(mTile, never()).handleStale();
     }
 
+    @Test
+    public void testHandleDestroy_log() {
+        mTile.handleDestroy();
+        verify(mQsLogger).logTileDestroyed(eq(SPEC), anyString());
+    }
+
+    @Test
+    public void testListening_log() {
+        mTile.handleSetListening(true);
+        verify(mQsLogger).logTileChangeListening(SPEC, true);
+
+        mTile.handleSetListening(false);
+        verify(mQsLogger).logTileChangeListening(SPEC, false);
+    }
+
     private class TileLogMatcher implements ArgumentMatcher<LogMaker> {
 
         private final int mCategory;
@@ -236,6 +285,7 @@
     private static class TileImpl extends QSTileImpl<QSTile.BooleanState> {
         protected TileImpl(QSHost host) {
             super(host);
+            getState().state = Tile.STATE_ACTIVE;
         }
 
         @Override
@@ -264,11 +314,6 @@
         }
 
         @Override
-        protected void handleSetListening(boolean listening) {
-
-        }
-
-        @Override
         public CharSequence getTileLabel() {
             return null;
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java
index 2605402..62f406f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java
@@ -140,6 +140,11 @@
         return this;
     }
 
+    public SbnBuilder setFlag(Context context, int mask, boolean value) {
+        modifyNotification(context).setFlag(mask, value);
+        return this;
+    }
+
     public Notification.Builder modifyNotification(Context context) {
         if (mNotification != null) {
             mNotificationBuilder = new Notification.Builder(context, mNotification);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index abc0f3e..96db16a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -46,9 +46,12 @@
 import static java.util.Objects.requireNonNull;
 
 import android.annotation.Nullable;
+import android.app.Notification;
 import android.os.RemoteException;
 import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.NotificationStats;
+import android.service.notification.StatusBarNotification;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.ArrayMap;
@@ -734,6 +737,78 @@
     }
 
     @Test
+    public void testDismissingSummaryDoesNotDismissForegroundServiceChildren() {
+        // GIVEN a collection with three grouped notifs in it
+        CollectionEvent notif0 = postNotif(
+                buildNotif(TEST_PACKAGE, 0)
+                        .setGroup(mContext, GROUP_1)
+                        .setGroupSummary(mContext, true));
+        CollectionEvent notif1 = postNotif(
+                buildNotif(TEST_PACKAGE, 1)
+                        .setGroup(mContext, GROUP_1)
+                        .setFlag(mContext, Notification.FLAG_FOREGROUND_SERVICE, true));
+        CollectionEvent notif2 = postNotif(
+                buildNotif(TEST_PACKAGE, 2)
+                        .setGroup(mContext, GROUP_1));
+
+        // WHEN the summary is dismissed
+        mCollection.dismissNotification(notif0.entry, defaultStats(notif0.entry));
+
+        // THEN the foreground service child is not dismissed
+        assertEquals(DISMISSED, notif0.entry.getDismissState());
+        assertEquals(NOT_DISMISSED, notif1.entry.getDismissState());
+        assertEquals(PARENT_DISMISSED, notif2.entry.getDismissState());
+    }
+
+    @Test
+    public void testDismissingSummaryDoesNotDismissBubbledChildren() {
+        // GIVEN a collection with three grouped notifs in it
+        CollectionEvent notif0 = postNotif(
+                buildNotif(TEST_PACKAGE, 0)
+                        .setGroup(mContext, GROUP_1)
+                        .setGroupSummary(mContext, true));
+        CollectionEvent notif1 = postNotif(
+                buildNotif(TEST_PACKAGE, 1)
+                        .setGroup(mContext, GROUP_1)
+                        .setFlag(mContext, Notification.FLAG_BUBBLE, true));
+        CollectionEvent notif2 = postNotif(
+                buildNotif(TEST_PACKAGE, 2)
+                        .setGroup(mContext, GROUP_1));
+
+        // WHEN the summary is dismissed
+        mCollection.dismissNotification(notif0.entry, defaultStats(notif0.entry));
+
+        // THEN the bubbled child is not dismissed
+        assertEquals(DISMISSED, notif0.entry.getDismissState());
+        assertEquals(NOT_DISMISSED, notif1.entry.getDismissState());
+        assertEquals(PARENT_DISMISSED, notif2.entry.getDismissState());
+    }
+
+    @Test
+    public void testDismissingSummaryDoesNotDismissDuplicateSummaries() {
+        // GIVEN a group with a two summaries
+        CollectionEvent notif0 = postNotif(
+                buildNotif(TEST_PACKAGE, 0)
+                        .setGroup(mContext, GROUP_1)
+                        .setGroupSummary(mContext, true));
+        CollectionEvent notif1 = postNotif(
+                buildNotif(TEST_PACKAGE, 1)
+                        .setGroup(mContext, GROUP_1)
+                        .setGroupSummary(mContext, true));
+        CollectionEvent notif2 = postNotif(
+                buildNotif(TEST_PACKAGE, 2)
+                        .setGroup(mContext, GROUP_1));
+
+        // WHEN the first summary is dismissed
+        mCollection.dismissNotification(notif0.entry, defaultStats(notif0.entry));
+
+        // THEN the second summary is not auto-dismissed (but the child is)
+        assertEquals(DISMISSED, notif0.entry.getDismissState());
+        assertEquals(NOT_DISMISSED, notif1.entry.getDismissState());
+        assertEquals(PARENT_DISMISSED, notif2.entry.getDismissState());
+    }
+
+    @Test
     public void testLifetimeExtendersAreQueriedWhenNotifRemoved() {
         // GIVEN a couple notifications and a few lifetime extenders
         mExtender1.shouldExtendLifetime = true;
@@ -1000,6 +1075,13 @@
                 NotificationVisibility.obtain(entry.getKey(), 7, 2, true));
     }
 
+    public CollectionEvent postNotif(NotificationEntryBuilder builder) {
+        clearInvocations(mCollectionListener);
+        NotifEvent rawEvent = mNoMan.postNotif(builder);
+        verify(mCollectionListener).onEntryAdded(mEntryCaptor.capture());
+        return new CollectionEvent(rawEvent, requireNonNull(mEntryCaptor.getValue()));
+    }
+
     private static class RecordingCollectionListener implements NotifCollectionListener {
         private final Map<String, NotificationEntry> mLastSeenEntries = new ArrayMap<>();
 
@@ -1014,6 +1096,7 @@
 
         @Override
         public void onEntryUpdated(NotificationEntry entry) {
+            mLastSeenEntries.put(entry.getKey(), entry);
         }
 
         @Override
@@ -1098,6 +1181,26 @@
         }
     }
 
+    /**
+     * Wrapper around {@link NotifEvent} that adds the NotificationEntry that the collection under
+     * test creates.
+     */
+    private static class CollectionEvent {
+        public final String key;
+        public final StatusBarNotification sbn;
+        public final Ranking ranking;
+        public final RankingMap rankingMap;
+        public final NotificationEntry entry;
+
+        private CollectionEvent(NotifEvent rawEvent, NotificationEntry entry) {
+            this.key = rawEvent.key;
+            this.sbn = rawEvent.sbn;
+            this.ranking = rawEvent.ranking;
+            this.rankingMap = rawEvent.rankingMap;
+            this.entry = entry;
+        }
+    }
+
     private static final String TEST_PACKAGE = "com.android.test.collection";
     private static final String TEST_PACKAGE2 = "com.android.test.collection2";
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
index 300ec18..d2bb011 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
@@ -162,6 +162,11 @@
         return this;
     }
 
+    public NotificationEntryBuilder setFlag(Context context, int mask, boolean value) {
+        mSbnBuilder.setFlag(context, mask, value);
+        return this;
+    }
+
     /* Delegated to RankingBuilder */
 
     public NotificationEntryBuilder setRank(int rank) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
new file mode 100644
index 0000000..61a4fbe
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class PreparationCoordinatorTest extends SysuiTestCase {
+    private static final String TEST_MESSAGE = "TEST_MESSAGE";
+
+    private PreparationCoordinator mCoordinator;
+    private NotifFilter mInflationErrorFilter;
+    private NotifInflationErrorManager mErrorManager;
+    private NotificationEntry mEntry;
+    private Exception mInflationError;
+
+    @Mock
+    private NotifPipeline mNotifPipeline;
+    @Mock private IStatusBarService mService;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mEntry = new NotificationEntryBuilder().build();
+        mInflationError = new Exception(TEST_MESSAGE);
+        mErrorManager = new NotifInflationErrorManager();
+
+        mCoordinator = new PreparationCoordinator(
+                mock(PreparationCoordinatorLogger.class),
+                mock(NotifInflaterImpl.class),
+                mErrorManager,
+                mService);
+
+        ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class);
+        mCoordinator.attach(mNotifPipeline);
+        verify(mNotifPipeline, times(2)).addPreRenderFilter(filterCaptor.capture());
+        List<NotifFilter> filters = filterCaptor.getAllValues();
+        mInflationErrorFilter = filters.get(0);
+    }
+
+    @Test
+    public void testErrorLogsToService() throws RemoteException {
+        // WHEN an entry has an inflation error.
+        mErrorManager.setInflationError(mEntry, mInflationError);
+
+        // THEN we log to status bar service.
+        verify(mService).onNotificationError(
+                eq(mEntry.getSbn().getPackageName()),
+                eq(mEntry.getSbn().getTag()),
+                eq(mEntry.getSbn().getId()),
+                eq(mEntry.getSbn().getUid()),
+                eq(mEntry.getSbn().getInitialPid()),
+                eq(mInflationError.getMessage()),
+                eq(mEntry.getSbn().getUserId()));
+    }
+
+    @Test
+    public void testFiltersOutErroredNotifications() {
+        // WHEN an entry has an inflation error.
+        mErrorManager.setInflationError(mEntry, mInflationError);
+
+        // THEN we filter it from the notification list.
+        assertTrue(mInflationErrorFilter.shouldFilterOut(mEntry, 0));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
index 8f9f65d..408bba4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
@@ -28,9 +28,9 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback;
 
 import org.junit.Before;
@@ -57,17 +57,17 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        NotificationEntryManager entryManager = mock(NotificationEntryManager.class);
+        CommonNotifCollection collection = mock(CommonNotifCollection.class);
 
-        mBindPipeline = new NotifBindPipeline(entryManager);
+        mBindPipeline = new NotifBindPipeline(collection);
         mBindPipeline.setStage(mStage);
 
-        ArgumentCaptor<NotificationEntryListener> entryListenerCaptor =
-                ArgumentCaptor.forClass(NotificationEntryListener.class);
-        verify(entryManager).addNotificationEntryListener(entryListenerCaptor.capture());
-        NotificationEntryListener entryListener = entryListenerCaptor.getValue();
+        ArgumentCaptor<NotifCollectionListener> collectionListenerCaptor =
+                ArgumentCaptor.forClass(NotifCollectionListener.class);
+        verify(collection).addCollectionListener(collectionListenerCaptor.capture());
+        NotifCollectionListener listener = collectionListenerCaptor.getValue();
 
-        entryListener.onPendingEntryAdded(mEntry);
+        listener.onEntryInit(mEntry);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index f080d67..e8de10f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -75,6 +75,7 @@
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.phone.ShadeController;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -131,6 +132,8 @@
     private ShortcutManager mShortcutManager;
     @Mock
     private NotificationGuts mNotificationGuts;
+    @Mock
+    private ShadeController mShadeController;
 
     @Before
     public void setUp() throws Exception {
@@ -139,12 +142,12 @@
         mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
         mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
         mDependency.injectTestDependency(BubbleController.class, mBubbleController);
+        mDependency.injectTestDependency(ShadeController.class, mShadeController);
         // Inflate the layout
         final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
         mNotificationInfo = (NotificationConversationInfo) layoutInflater.inflate(
                 R.layout.notification_conversation_info,
                 null);
-        mNotificationInfo.mShowHomeScreen = true;
         mNotificationInfo.setGutsParent(mNotificationGuts);
         doAnswer((Answer<Object>) invocation -> {
             mNotificationInfo.handleCloseControls(true, false);
@@ -173,7 +176,7 @@
         when(mShortcutInfo.getShortLabel()).thenReturn("Convo name");
         List<ShortcutInfo> shortcuts = Arrays.asList(mShortcutInfo);
         when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcuts);
-        mImage = mContext.getDrawable(R.drawable.ic_star);
+        mImage = mContext.getDrawable(R.drawable.ic_remove);
         when(mLauncherApps.getShortcutBadgedIconDrawable(eq(mShortcutInfo),
                 anyInt())).thenReturn(mImage);
 
@@ -333,8 +336,6 @@
                 true);
         final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
         assertEquals(GONE, nameView.getVisibility());
-        final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider);
-        assertEquals(GONE, dividerView.getVisibility());
     }
 
     @Test
@@ -364,8 +365,6 @@
         final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
         assertEquals(VISIBLE, nameView.getVisibility());
         assertTrue(nameView.getText().toString().contains("Proxied"));
-        final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider);
-        assertEquals(VISIBLE, dividerView.getVisibility());
     }
 
     @Test
@@ -502,6 +501,7 @@
         verify(mShortcutManager, times(1)).requestPinShortcut(mShortcutInfo, null);
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), anyInt(), any());
+        verify(mShadeController).animateCollapsePanels();
     }
 
     @Test
@@ -644,9 +644,9 @@
                 true);
 
 
-        Button fave = mNotificationInfo.findViewById(R.id.fave);
-        assertEquals(mContext.getString(R.string.notification_conversation_favorite),
-                fave.getText().toString());
+        ImageButton fave = mNotificationInfo.findViewById(R.id.fave);
+        assertEquals(mContext.getString(R.string.notification_conversation_unfavorite),
+                fave.getContentDescription().toString());
 
         fave.performClick();
         mTestableLooper.processAllMessages();
@@ -677,9 +677,9 @@
                 null,
                 true);
 
-        Button fave = mNotificationInfo.findViewById(R.id.fave);
-        assertEquals(mContext.getString(R.string.notification_conversation_unfavorite),
-                fave.getText().toString());
+        ImageButton fave = mNotificationInfo.findViewById(R.id.fave);
+        assertEquals(mContext.getString(R.string.notification_conversation_favorite),
+                fave.getContentDescription().toString());
 
         fave.performClick();
         mTestableLooper.processAllMessages();
@@ -692,34 +692,6 @@
     }
 
     @Test
-    public void testDemote() throws Exception {
-        mNotificationInfo.bindNotification(
-                mShortcutManager,
-                mLauncherApps,
-                mMockPackageManager,
-                mMockINotificationManager,
-                mVisualStabilityManager,
-                TEST_PACKAGE_NAME,
-                mNotificationChannel,
-                mEntry,
-                null,
-                null,
-                null,
-                true);
-
-
-        ImageButton demote = mNotificationInfo.findViewById(R.id.demote);
-        demote.performClick();
-        mTestableLooper.processAllMessages();
-
-        ArgumentCaptor<NotificationChannel> captor =
-                ArgumentCaptor.forClass(NotificationChannel.class);
-        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
-                anyString(), anyInt(), captor.capture());
-        assertTrue(captor.getValue().isDemoted());
-    }
-
-    @Test
     public void testMute_mute() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
         mConversationChannel.setImportance(IMPORTANCE_DEFAULT);
@@ -738,9 +710,9 @@
                 null,
                 true);
 
-        Button mute = mNotificationInfo.findViewById(R.id.mute);
-        assertEquals(mContext.getString(R.string.notification_conversation_mute),
-                mute.getText().toString());
+        ImageButton mute = mNotificationInfo.findViewById(R.id.mute);
+        assertEquals(mContext.getString(R.string.notification_conversation_unmute),
+                mute.getContentDescription().toString());
 
         mute.performClick();
         mTestableLooper.processAllMessages();
@@ -774,9 +746,9 @@
                 true);
 
 
-        Button mute = mNotificationInfo.findViewById(R.id.mute);
-        assertEquals(mContext.getString(R.string.notification_conversation_unmute),
-                mute.getText().toString());
+        ImageButton mute = mNotificationInfo.findViewById(R.id.mute);
+        assertEquals(mContext.getString(R.string.notification_conversation_mute),
+                mute.getContentDescription().toString());
 
         mute.performClick();
         mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 35b5508..fd5512d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -41,7 +41,6 @@
 import android.view.LayoutInflater;
 import android.widget.RemoteViews;
 
-import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.TestableDependency;
 import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.bubbles.BubblesTestActivity;
@@ -50,10 +49,10 @@
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.SmartReplyController;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpansionLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
@@ -90,7 +89,7 @@
     private ExpandableNotificationRow mRow;
     private HeadsUpManagerPhone mHeadsUpManager;
     private final NotifBindPipeline mBindPipeline;
-    private final NotificationEntryListener mBindPipelineEntryListener;
+    private final NotifCollectionListener mBindPipelineEntryListener;
     private final RowContentBindStage mBindStage;
     private StatusBarStateController mStatusBarStateController;
 
@@ -112,17 +111,17 @@
                 mock(NotifRemoteViewCache.class),
                 mock(NotificationRemoteInputManager.class));
         contentBinder.setInflateSynchronously(true);
-        mBindStage = new RowContentBindStage(contentBinder, mock(IStatusBarService.class));
+        mBindStage = new RowContentBindStage(contentBinder, mock(NotifInflationErrorManager.class));
 
-        NotificationEntryManager entryManager = mock(NotificationEntryManager.class);
+        CommonNotifCollection collection = mock(CommonNotifCollection.class);
 
-        mBindPipeline = new NotifBindPipeline(entryManager);
+        mBindPipeline = new NotifBindPipeline(collection);
         mBindPipeline.setStage(mBindStage);
 
-        ArgumentCaptor<NotificationEntryListener> entryListenerCaptor =
-                ArgumentCaptor.forClass(NotificationEntryListener.class);
-        verify(entryManager).addNotificationEntryListener(entryListenerCaptor.capture());
-        mBindPipelineEntryListener = entryListenerCaptor.getValue();
+        ArgumentCaptor<NotifCollectionListener> collectionListenerCaptor =
+                ArgumentCaptor.forClass(NotifCollectionListener.class);
+        verify(collection).addCollectionListener(collectionListenerCaptor.capture());
+        mBindPipelineEntryListener = collectionListenerCaptor.getValue();
     }
 
     /**
@@ -377,7 +376,7 @@
         entry.createIcons(mContext, entry.getSbn());
         row.setEntry(entry);
 
-        mBindPipelineEntryListener.onPendingEntryAdded(entry);
+        mBindPipelineEntryListener.onEntryInit(entry);
         mBindPipeline.manageRow(entry, row);
 
         row.initialize(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
index 775f722..d9fe655 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
@@ -34,7 +34,6 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
@@ -62,7 +61,7 @@
         MockitoAnnotations.initMocks(this);
 
         mRowContentBindStage = new RowContentBindStage(mBinder,
-                mock(IStatusBarService.class));
+                mock(NotifInflationErrorManager.class));
         mRowContentBindStage.createStageParams(mEntry);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
index 8936a2d..e917c93 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
@@ -38,7 +38,7 @@
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.StatusBarWindowBlurController;
+import com.android.systemui.statusbar.NotificationShadeWindowBlurController;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -80,7 +80,7 @@
     @Mock private DockManager mDockManager;
     @Mock private NotificationPanelViewController mNotificationPanelViewController;
     @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
-    @Mock private StatusBarWindowBlurController mStatusBarWindowBlurController;
+    @Mock private NotificationShadeWindowBlurController mNotificationShadeWindowBlurController;
 
     @Before
     public void setUp() {
@@ -114,7 +114,7 @@
                 new CommandQueue(mContext),
                 mShadeController,
                 mDockManager,
-                mStatusBarWindowBlurController,
+                mNotificationShadeWindowBlurController,
                 mView,
                 mNotificationPanelViewController);
         mController.setupExpandedStatusBar();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 008a349..2e6fbe7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -521,10 +521,10 @@
         Assert.assertEquals(ScrimController.TRANSPARENT,
                 mScrimInFront.getViewAlpha(), 0.0f);
         // Back scrim should be visible
-        Assert.assertEquals(ScrimController.GRADIENT_SCRIM_ALPHA_BUSY,
+        Assert.assertEquals(ScrimController.BUSY_SCRIM_ALPHA,
                 mScrimBehind.getViewAlpha(), 0.0f);
         // Bubble scrim should be visible
-        Assert.assertEquals(ScrimController.GRADIENT_SCRIM_ALPHA_BUSY,
+        Assert.assertEquals(ScrimController.BUSY_SCRIM_ALPHA,
                 mScrimBehind.getViewAlpha(), 0.0f);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
index 85f6033..18e7840 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
@@ -115,15 +115,16 @@
         IconState qs = new IconState(true, 1, "");
         boolean in = true;
         boolean out = true;
-        String typeDescription = "Test 1";
-        String description = "Test 2";
+        CharSequence typeDescription = "Test 1";
+        CharSequence typeDescriptionHtml = "<b>Test 1</b>";
+        CharSequence description = "Test 2";
         int type = TelephonyIcons.ICON_1X;
         int qsType = TelephonyIcons.ICON_1X;
         boolean wide = true;
         int subId = 5;
         boolean roaming = true;
         mHandler.setMobileDataIndicators(status, qs, type, qsType, in, out, typeDescription,
-                description, wide, subId, roaming);
+                typeDescriptionHtml, description, wide, subId, roaming);
         waitForCallbacks();
 
         ArgumentCaptor<IconState> statusArg = ArgumentCaptor.forClass(IconState.class);
@@ -132,14 +133,16 @@
         ArgumentCaptor<Integer> qsTypeIconArg = ArgumentCaptor.forClass(Integer.class);
         ArgumentCaptor<Boolean> inArg = ArgumentCaptor.forClass(Boolean.class);
         ArgumentCaptor<Boolean> outArg = ArgumentCaptor.forClass(Boolean.class);
-        ArgumentCaptor<String> typeContentArg = ArgumentCaptor.forClass(String.class);
-        ArgumentCaptor<String> descArg = ArgumentCaptor.forClass(String.class);
+        ArgumentCaptor<CharSequence> typeContentArg = ArgumentCaptor.forClass(CharSequence.class);
+        ArgumentCaptor<CharSequence> typeContentHtmlArg =
+                ArgumentCaptor.forClass(CharSequence.class);
+        ArgumentCaptor<CharSequence> descArg = ArgumentCaptor.forClass(CharSequence.class);
         ArgumentCaptor<Boolean> wideArg = ArgumentCaptor.forClass(Boolean.class);
         ArgumentCaptor<Integer> subIdArg = ArgumentCaptor.forClass(Integer.class);
         Mockito.verify(mSignalCallback).setMobileDataIndicators(statusArg.capture(),
                 qsArg.capture(), typeIconArg.capture(), qsTypeIconArg.capture(), inArg.capture(),
-                outArg.capture(), typeContentArg.capture(), descArg.capture(), wideArg.capture(),
-                subIdArg.capture(), eq(roaming));
+                outArg.capture(), typeContentArg.capture(), typeContentHtmlArg.capture(),
+                descArg.capture(), wideArg.capture(), subIdArg.capture(), eq(roaming));
         assertEquals(status, statusArg.getValue());
         assertEquals(qs, qsArg.getValue());
         assertEquals(type, (int) typeIconArg.getValue());
@@ -147,6 +150,7 @@
         assertEquals(in, (boolean) inArg.getValue());
         assertEquals(out, (boolean) outArg.getValue());
         assertEquals(typeDescription, typeContentArg.getValue());
+        assertEquals(typeDescriptionHtml, typeContentHtmlArg.getValue());
         assertEquals(description, descArg.getValue());
         assertEquals(wide, (boolean) wideArg.getValue());
         assertEquals(subId, (int) subIdArg.getValue());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 294d546..9a0e97a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -413,7 +413,8 @@
                     iconArg.capture(),
                     anyInt(),
                     typeIconArg.capture(), dataInArg.capture(), dataOutArg.capture(),
-                    anyString(), anyString(), anyBoolean(), anyInt(), anyBoolean());
+                    any(CharSequence.class), any(CharSequence.class), any(CharSequence.class),
+                    anyBoolean(), anyInt(), anyBoolean());
         IconState iconState = iconArg.getValue();
         int state = SignalDrawable.getState(icon, CellSignalStrength.getNumSignalStrengthLevels(),
                 false);
@@ -445,8 +446,9 @@
                 iconArg.capture(),
                 any(),
                 typeIconArg.capture(),
-                anyInt(), anyBoolean(), anyBoolean(), anyString(), anyString(), anyBoolean(),
-                anyInt(), eq(roaming));
+                anyInt(), anyBoolean(), anyBoolean(),
+                any(CharSequence.class), any(CharSequence.class), any(CharSequence.class),
+                anyBoolean(), anyInt(), eq(roaming));
         IconState iconState = iconArg.getValue();
 
         int state = icon == -1 ? 0
@@ -468,19 +470,23 @@
             boolean cutOut) {
         verifyLastMobileDataIndicators(
                 visible, icon, typeIcon, qsVisible, qsIcon, qsTypeIcon, dataIn, dataOut, cutOut,
-                null);
+                null, null);
     }
 
     protected void verifyLastMobileDataIndicators(boolean visible, int icon, int typeIcon,
             boolean qsVisible, int qsIcon, int qsTypeIcon, boolean dataIn, boolean dataOut,
-            boolean cutOut, String typeContentDescription) {
+            boolean cutOut, CharSequence typeContentDescription,
+            CharSequence typeContentDescriptionHtml) {
         ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class);
         ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class);
         ArgumentCaptor<IconState> qsIconArg = ArgumentCaptor.forClass(IconState.class);
         ArgumentCaptor<Integer> qsTypeIconArg = ArgumentCaptor.forClass(Integer.class);
         ArgumentCaptor<Boolean> dataInArg = ArgumentCaptor.forClass(Boolean.class);
         ArgumentCaptor<Boolean> dataOutArg = ArgumentCaptor.forClass(Boolean.class);
-        ArgumentCaptor<String> typeContentDescriptionArg = ArgumentCaptor.forClass(String.class);
+        ArgumentCaptor<CharSequence> typeContentDescriptionArg =
+                ArgumentCaptor.forClass(CharSequence.class);
+        ArgumentCaptor<CharSequence> typeContentDescriptionHtmlArg =
+                ArgumentCaptor.forClass(CharSequence.class);
 
         Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators(
                 iconArg.capture(),
@@ -490,6 +496,7 @@
                 dataInArg.capture(),
                 dataOutArg.capture(),
                 typeContentDescriptionArg.capture(),
+                typeContentDescriptionHtmlArg.capture(),
                 anyString(), anyBoolean(), anyInt(), anyBoolean());
 
         IconState iconState = iconArg.getValue();
@@ -516,6 +523,10 @@
             assertEquals("Type content description", typeContentDescription,
                     typeContentDescriptionArg.getValue());
         }
+        if (typeContentDescriptionHtml != null) { // Only check if it was provided
+            assertEquals("Type content description (html)", typeContentDescriptionHtml,
+                    typeContentDescriptionHtmlArg.getValue());
+        }
     }
 
     protected void assertNetworkNameEquals(String expected) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index 1eb5939..a906d9f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -132,7 +132,7 @@
         // Verify that a SignalDrawable with a cut out is used to display data disabled.
         verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, 0,
                 true, DEFAULT_QS_SIGNAL_STRENGTH, 0, false,
-                false, true, NO_DATA_STRING);
+                false, true, NO_DATA_STRING, NO_DATA_STRING);
     }
 
     @Test
@@ -146,7 +146,7 @@
         // Verify that a SignalDrawable with a cut out is used to display data disabled.
         verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, 0,
                 true, DEFAULT_QS_SIGNAL_STRENGTH, 0, false,
-                false, true, NO_DATA_STRING);
+                false, true, NO_DATA_STRING, NO_DATA_STRING);
     }
 
     @Test
@@ -161,7 +161,7 @@
         // Verify that a SignalDrawable with a cut out is used to display data disabled.
         verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, 0,
                 true, DEFAULT_QS_SIGNAL_STRENGTH, 0, false,
-                false, false, NOT_DEFAULT_DATA_STRING);
+                false, false, NOT_DEFAULT_DATA_STRING, NOT_DEFAULT_DATA_STRING);
     }
 
     @Test
@@ -176,7 +176,7 @@
         // Verify that a SignalDrawable with a cut out is used to display data disabled.
         verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, 0,
                 true, DEFAULT_QS_SIGNAL_STRENGTH, 0, false,
-                false, false, NOT_DEFAULT_DATA_STRING);
+                false, false, NOT_DEFAULT_DATA_STRING, NOT_DEFAULT_DATA_STRING);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index f52c8c1..b922f06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -369,8 +369,8 @@
         mNetworkController.onReceive(mContext, intent);
 
         String defaultNetworkName = mMobileSignalController
-            .getStringIfExists(
-                com.android.internal.R.string.lockscreen_carrier_default);
+                .getTextIfExists(
+                com.android.internal.R.string.lockscreen_carrier_default).toString();
         assertNetworkNameEquals(defaultNetworkName);
     }
 
@@ -383,8 +383,8 @@
 
         mNetworkController.onReceive(mContext, intent);
 
-        String defaultNetworkName = mMobileSignalController.getStringIfExists(
-                com.android.internal.R.string.lockscreen_carrier_default);
+        String defaultNetworkName = mMobileSignalController.getTextIfExists(
+                com.android.internal.R.string.lockscreen_carrier_default).toString();
         assertNetworkNameEquals(defaultNetworkName);
     }
 
@@ -401,8 +401,8 @@
         mNetworkController.onReceive(mContext, intent);
 
         assertNetworkNameEquals(plmn
-                + mMobileSignalController.getStringIfExists(
-                        R.string.status_bar_network_name_separator)
+                + mMobileSignalController.getTextIfExists(
+                        R.string.status_bar_network_name_separator).toString()
                 + spn);
     }
 
diff --git a/packages/WAPPushManager/Android.bp b/packages/WAPPushManager/Android.bp
index cd804b2..083dac9 100644
--- a/packages/WAPPushManager/Android.bp
+++ b/packages/WAPPushManager/Android.bp
@@ -4,6 +4,7 @@
     name: "WAPPushManager",
     srcs: ["src/**/*.java"],
     platform_apis: true,
+    libs: ["telephony-common"],
     static_libs: ["android-common"],
     optimize: {
         proguard_flags_files: ["proguard.flags"],
diff --git a/packages/services/PacProcessor/src/com/android/pacprocessor/LibpacInterface.java b/packages/services/PacProcessor/src/com/android/pacprocessor/LibpacInterface.java
new file mode 100644
index 0000000..103ef781
--- /dev/null
+++ b/packages/services/PacProcessor/src/com/android/pacprocessor/LibpacInterface.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.pacprocessor;
+
+/**
+ * Common interface for both Android's and WebView's implementation of PAC processor.
+ *
+ * @hide
+ */
+interface LibpacInterface {
+    default boolean startPacSupport() {
+        return true;
+    }
+
+    default boolean stopPacSupport() {
+        return true;
+    }
+
+    boolean setCurrentProxyScript(String script);
+    String makeProxyRequest(String url, String host);
+}
diff --git a/packages/services/PacProcessor/src/com/android/pacprocessor/PacNative.java b/packages/services/PacProcessor/src/com/android/pacprocessor/PacNative.java
index 1e8109c..9c0cfc2 100644
--- a/packages/services/PacProcessor/src/com/android/pacprocessor/PacNative.java
+++ b/packages/services/PacProcessor/src/com/android/pacprocessor/PacNative.java
@@ -20,7 +20,7 @@
 /**
  * @hide
  */
-public class PacNative {
+public class PacNative implements LibpacInterface {
     private static final String TAG = "PacProxy";
 
     private static final PacNative sInstance = new PacNative();
@@ -49,34 +49,38 @@
         return sInstance;
     }
 
+    @Override
     public synchronized boolean startPacSupport() {
         if (createV8ParserNativeLocked()) {
             Log.e(TAG, "Unable to Create v8 Proxy Parser.");
-            return true;
+            return false;
         }
         mIsActive = true;
-        return false;
+        return true;
     }
 
+    @Override
     public synchronized boolean stopPacSupport() {
         if (mIsActive) {
             if (destroyV8ParserNativeLocked()) {
                 Log.e(TAG, "Unable to Destroy v8 Proxy Parser.");
-                return true;
+                return false;
             }
             mIsActive = false;
         }
-        return false;
+        return true;
     }
 
+    @Override
     public synchronized boolean setCurrentProxyScript(String script) {
         if (setProxyScriptNativeLocked(script)) {
             Log.e(TAG, "Unable to parse proxy script.");
-            return true;
+            return false;
         }
-        return false;
+        return true;
     }
 
+    @Override
     public synchronized String makeProxyRequest(String url, String host) {
         String ret = makeProxyRequestNativeLocked(url, host);
         if ((ret == null) || (ret.length() == 0)) {
diff --git a/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java b/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java
index b006d6e..7aea721 100644
--- a/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java
+++ b/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java
@@ -17,6 +17,7 @@
 
 import android.app.Service;
 import android.content.Intent;
+import android.content.res.Resources;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.Process;
@@ -30,19 +31,24 @@
 
 public class PacService extends Service {
     private static final String TAG = "PacService";
+    private static final boolean sUseWebViewPacProcessor = Resources.getSystem().getBoolean(
+            com.android.internal.R.bool.config_useWebViewPacProcessor);
 
-    private PacNative mPacNative = PacNative.getInstance();
+    private final LibpacInterface mLibpac = sUseWebViewPacProcessor
+            ? PacWebView.getInstance()
+            : PacNative.getInstance();
+
     private ProxyServiceStub mStub = new ProxyServiceStub();
 
     @Override
     public void onCreate() {
         super.onCreate();
-        mPacNative.startPacSupport();
+        mLibpac.startPacSupport();
     }
 
     @Override
     public void onDestroy() {
-        mPacNative.stopPacSupport();
+        mLibpac.stopPacSupport();
         super.onDestroy();
     }
 
@@ -52,7 +58,6 @@
     }
 
     private class ProxyServiceStub extends IProxyService.Stub {
-
         @Override
         public String resolvePacFile(String host, String url) throws RemoteException {
             try {
@@ -69,7 +74,7 @@
                         throw new IllegalArgumentException("Invalid host was passed");
                     }
                 }
-                return mPacNative.makeProxyRequest(url, host);
+                return mLibpac.makeProxyRequest(url, host);
             } catch (MalformedURLException e) {
                 throw new IllegalArgumentException("Invalid URL was passed");
             }
@@ -81,7 +86,7 @@
                 Log.e(TAG, "Only system user is allowed to call setPacFile");
                 throw new SecurityException();
             }
-            mPacNative.setCurrentProxyScript(script);
+            mLibpac.setCurrentProxyScript(script);
         }
 
         @Override
diff --git a/packages/services/PacProcessor/src/com/android/pacprocessor/PacWebView.java b/packages/services/PacProcessor/src/com/android/pacprocessor/PacWebView.java
new file mode 100644
index 0000000..89c4665
--- /dev/null
+++ b/packages/services/PacProcessor/src/com/android/pacprocessor/PacWebView.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.pacprocessor;
+
+import android.util.Log;
+import android.webkit.PacProcessor;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * @hide
+ */
+public class PacWebView implements LibpacInterface {
+    private static final String TAG = "PacWebView";
+
+    private static final PacWebView sInstance = new PacWebView();
+
+    private Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private PacProcessor mProcessor = PacProcessor.getInstance();
+
+    public static PacWebView getInstance() {
+        return sInstance;
+    }
+
+    @Override
+    public boolean setCurrentProxyScript(String script) {
+        synchronized (mLock) {
+            if (!mProcessor.setProxyScript(script)) {
+                Log.e(TAG, "Unable to parse proxy script.");
+                return false;
+            }
+            return true;
+        }
+    }
+
+    @Override
+    public String makeProxyRequest(String url, String host) {
+        synchronized (mLock) {
+            return mProcessor.findProxyForUrl(url);
+        }
+    }
+}
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 54b4201..63dd99e 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -244,6 +244,10 @@
     // Package: android
     NOTE_SOFTAP_AUTO_DISABLED = 58;
 
+    // Notify the user that their admin has changed location settings.
+    // Package: android
+    NOTE_LOCATION_CHANGED = 59;
+
     // ADD_NEW_IDS_ABOVE_THIS_LINE
     // Legacy IDs with arbitrary values appear below
     // Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 565ee63..75ec4b0 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1128,8 +1128,7 @@
     @Override
     public int getWindowIdForLeashToken(@NonNull IBinder token) {
         synchronized (mLock) {
-            // TODO: Add a method to lookup window ID by given leash token.
-            return -1;
+            return mA11yWindowManager.getWindowIdLocked(token);
         }
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 7b495ce..ba29bc8 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -761,11 +761,11 @@
     }
 
     @Override
-    public int addAccessibilityInteractionConnection(IWindow windowToken,
+    public int addAccessibilityInteractionConnection(IWindow windowToken, IBinder leashToken,
             IAccessibilityInteractionConnection connection, String packageName,
             int userId) throws RemoteException {
         return mA11yWindowManager.addAccessibilityInteractionConnection(
-                windowToken, connection, packageName, userId);
+                windowToken, leashToken, connection, packageName, userId);
     }
 
     @Override
@@ -2643,6 +2643,20 @@
     }
 
     @Override
+    public void associateEmbeddedHierarchy(@NonNull IBinder host, @NonNull IBinder embedded) {
+        synchronized (mLock) {
+            mA11yWindowManager.associateEmbeddedHierarchyLocked(host, embedded);
+        }
+    }
+
+    @Override
+    public void disassociateEmbeddedHierarchy(@NonNull IBinder token) {
+        synchronized (mLock) {
+            mA11yWindowManager.disassociateEmbeddedHierarchyLocked(token);
+        }
+    }
+
+    @Override
     public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
         synchronized (mLock) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index 96e345e..d98e31e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -443,8 +443,8 @@
                 return false;
             }
         }
-        // TODO: Check parent windowId if the giving windowId is from embedded view hierarchy.
-        if (windowId == mAccessibilityWindowManager.getActiveWindowId(userId)) {
+        if (mAccessibilityWindowManager.resolveParentWindowIdLocked(windowId)
+                == mAccessibilityWindowManager.getActiveWindowId(userId)) {
             return true;
         }
         return mAccessibilityWindowManager.findA11yWindowInfoByIdLocked(windowId) != null;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index a6041e0..8c00581 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -31,6 +31,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
@@ -100,6 +101,19 @@
             new SparseArray<>();
 
     /**
+     * Map of host view and embedded hierarchy, mapping from leash token of its ViewRootImpl.
+     * The key is the token from embedded hierarchy, and the value is the token from its host.
+     */
+    private final ArrayMap<IBinder, IBinder> mHostEmbeddedMap = new ArrayMap<>();
+
+    /**
+     * Map of window id and view hierarchy.
+     * The key is the window id when the ViewRootImpl register to accessibility, and the value is
+     * its leash token.
+     */
+    private final SparseArray<IBinder> mWindowIdMap = new SparseArray<>();
+
+    /**
      * This class implements {@link WindowManagerInternal.WindowsForAccessibilityCallback} to
      * receive {@link WindowInfo}s from window manager when there's an accessibility change in
      * window and holds window lists information per display.
@@ -913,19 +927,21 @@
      * Adds accessibility interaction connection according to given window token, package name and
      * window token.
      *
-     * @param windowToken The window token of accessibility interaction connection
+     * @param window The window token of accessibility interaction connection
+     * @param leashToken The leash token of accessibility interaction connection
      * @param connection The accessibility interaction connection
      * @param packageName The package name
      * @param userId The userId
      * @return The windowId of added connection
      * @throws RemoteException
      */
-    public int addAccessibilityInteractionConnection(@NonNull IWindow windowToken,
-            @NonNull IAccessibilityInteractionConnection connection, @NonNull String packageName,
-            int userId) throws RemoteException {
+    public int addAccessibilityInteractionConnection(@NonNull IWindow window,
+            @NonNull IBinder leashToken, @NonNull IAccessibilityInteractionConnection connection,
+            @NonNull String packageName, int userId) throws RemoteException {
         final int windowId;
         boolean shouldComputeWindows = false;
-        final int displayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken.asBinder());
+        final IBinder token = window.asBinder();
+        final int displayId = mWindowManagerInternal.getDisplayIdForWindow(token);
         synchronized (mLock) {
             // We treat calls from a profile as if made by its parent as profiles
             // share the accessibility state of the parent. The call below
@@ -947,35 +963,33 @@
                         windowId, connection, packageName, resolvedUid, UserHandle.USER_ALL);
                 wrapper.linkToDeath();
                 mGlobalInteractionConnections.put(windowId, wrapper);
-                mGlobalWindowTokens.put(windowId, windowToken.asBinder());
+                mGlobalWindowTokens.put(windowId, token);
                 if (DEBUG) {
                     Slog.i(LOG_TAG, "Added global connection for pid:" + Binder.getCallingPid()
-                            + " with windowId: " + windowId + " and  token: "
-                            + windowToken.asBinder());
+                            + " with windowId: " + windowId + " and token: " + token);
                 }
             } else {
                 RemoteAccessibilityConnection wrapper = new RemoteAccessibilityConnection(
                         windowId, connection, packageName, resolvedUid, resolvedUserId);
                 wrapper.linkToDeath();
                 getInteractionConnectionsForUserLocked(resolvedUserId).put(windowId, wrapper);
-                getWindowTokensForUserLocked(resolvedUserId).put(windowId, windowToken.asBinder());
+                getWindowTokensForUserLocked(resolvedUserId).put(windowId, token);
                 if (DEBUG) {
                     Slog.i(LOG_TAG, "Added user connection for pid:" + Binder.getCallingPid()
-                            + " with windowId: " + windowId
-                            + " and  token: " + windowToken.asBinder());
+                            + " with windowId: " + windowId + " and  token: " + token);
                 }
             }
 
             if (isTrackingWindowsLocked(displayId)) {
                 shouldComputeWindows = true;
             }
+            registerIdLocked(leashToken, windowId);
         }
         if (shouldComputeWindows) {
             mWindowManagerInternal.computeWindowsForAccessibility(displayId);
         }
 
-        mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata(
-                windowToken.asBinder(), windowId);
+        mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata(token, windowId);
         return windowId;
     }
 
@@ -1098,6 +1112,7 @@
      * Invoked when accessibility interaction connection of window is removed.
      *
      * @param windowId Removed windowId
+     * @param binder Removed window token
      */
     private void onAccessibilityInteractionConnectionRemovedLocked(
             int windowId, @Nullable IBinder binder) {
@@ -1110,6 +1125,7 @@
             mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata(
                     binder, AccessibilityWindowInfo.UNDEFINED_WINDOW_ID);
         }
+        unregisterIdLocked(windowId);
     }
 
     /**
@@ -1132,7 +1148,7 @@
      * Returns the userId that owns the given window token, {@link UserHandle#USER_NULL}
      * if not found.
      *
-     * @param windowToken The winodw token
+     * @param windowToken The window token
      * @return The userId
      */
     public int getWindowOwnerUserId(@NonNull IBinder windowToken) {
@@ -1161,6 +1177,50 @@
     }
 
     /**
+     * Establish the relationship between the host and the embedded view hierarchy.
+     *
+     * @param host The token of host hierarchy
+     * @param embedded The token of the embedded hierarchy
+     */
+    public void associateEmbeddedHierarchyLocked(@NonNull IBinder host, @NonNull IBinder embedded) {
+        // Use embedded window as key, since one host window may have multiple embedded windows.
+        associateLocked(embedded, host);
+    }
+
+    /**
+     * Clear the relationship by given token.
+     *
+     * @param token The token
+     */
+    public void disassociateEmbeddedHierarchyLocked(@NonNull IBinder token) {
+        disassociateLocked(token);
+    }
+
+    /**
+     * Gets the parent windowId of the window according to the specified windowId.
+     *
+     * @param windowId The windowId to check
+     * @return The windowId of the parent window, or self if no parent exists
+     */
+    public int resolveParentWindowIdLocked(int windowId) {
+        final IBinder token = getTokenLocked(windowId);
+        if (token == null) {
+            return windowId;
+        }
+        final IBinder resolvedToken = resolveTopParentTokenLocked(token);
+        final int resolvedWindowId = getWindowIdLocked(resolvedToken);
+        return resolvedWindowId != -1 ? resolvedWindowId : windowId;
+    }
+
+    private IBinder resolveTopParentTokenLocked(IBinder token) {
+        final IBinder hostToken = getHostTokenLocked(token);
+        if (hostToken == null) {
+            return token;
+        }
+        return resolveTopParentTokenLocked(hostToken);
+    }
+
+    /**
      * Computes partial interactive region of given windowId.
      *
      * @param windowId The windowId
@@ -1357,6 +1417,7 @@
      */
     @Nullable
     public AccessibilityWindowInfo findA11yWindowInfoByIdLocked(int windowId) {
+        windowId = resolveParentWindowIdLocked(windowId);
         final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked(windowId);
         if (observer != null) {
             return observer.findA11yWindowInfoByIdLocked(windowId);
@@ -1584,6 +1645,88 @@
     }
 
     /**
+     * Associate the token of the embedded view hierarchy to the host view hierarchy.
+     *
+     * @param embedded The leash token from the view root of embedded hierarchy
+     * @param host The leash token from the view root of host hierarchy
+     */
+    void associateLocked(IBinder embedded, IBinder host) {
+        mHostEmbeddedMap.put(embedded, host);
+    }
+
+    /**
+     * Clear the relationship of given token.
+     *
+     * @param token The leash token
+     */
+    void disassociateLocked(IBinder token) {
+        mHostEmbeddedMap.remove(token);
+        for (int i = mHostEmbeddedMap.size() - 1; i >= 0; i--) {
+            if (mHostEmbeddedMap.valueAt(i).equals(token)) {
+                mHostEmbeddedMap.removeAt(i);
+            }
+        }
+    }
+
+    /**
+     * Register the leash token with its windowId.
+     *
+     * @param token The token.
+     * @param windowId The windowID.
+     */
+    void registerIdLocked(IBinder token, int windowId) {
+        mWindowIdMap.put(windowId, token);
+    }
+
+    /**
+     * Unregister the windowId and also disassociate its token.
+     *
+     * @param windowId The windowID
+     */
+    void unregisterIdLocked(int windowId) {
+        final IBinder token = mWindowIdMap.get(windowId);
+        if (token == null) {
+            return;
+        }
+        disassociateLocked(token);
+        mWindowIdMap.remove(windowId);
+    }
+
+    /**
+     * Get the leash token by given windowID.
+     *
+     * @param windowId The windowID.
+     * @return The token, or {@code NULL} if this windowID doesn't exist
+     */
+    IBinder getTokenLocked(int windowId) {
+        return mWindowIdMap.get(windowId);
+    }
+
+    /**
+     * Get the windowId by given leash token.
+     *
+     * @param token The token
+     * @return The windowID, or -1 if the token doesn't exist
+     */
+    int getWindowIdLocked(IBinder token) {
+        final int index = mWindowIdMap.indexOfValue(token);
+        if (index == -1) {
+            return index;
+        }
+        return mWindowIdMap.keyAt(index);
+    }
+
+    /**
+     * Get the leash token of the host hierarchy by given token.
+     *
+     * @param token The token
+     * @return The token of host hierarchy, or {@code NULL} if no host exists
+     */
+    IBinder getHostTokenLocked(IBinder token) {
+        return mHostEmbeddedMap.get(token);
+    }
+
+    /**
      * Dumps all {@link AccessibilityWindowInfo}s here.
      */
     public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java
index 5844f98..1c4db12 100644
--- a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java
@@ -18,12 +18,14 @@
 
 import static android.Manifest.permission.MANAGE_APP_PREDICTIONS;
 import static android.Manifest.permission.PACKAGE_USAGE_STATS;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
 import static android.content.Context.APP_PREDICTION_SERVICE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.app.ActivityManagerInternal;
 import android.app.prediction.AppPredictionContext;
 import android.app.prediction.AppPredictionSessionId;
 import android.app.prediction.AppTargetEvent;
@@ -34,7 +36,6 @@
 import android.os.Binder;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
-import android.os.UserHandle;
 import android.util.Slog;
 
 import com.android.server.LocalServices;
@@ -108,21 +109,21 @@
         @Override
         public void createPredictionSession(@NonNull AppPredictionContext context,
                 @NonNull AppPredictionSessionId sessionId) {
-            runForUserLocked("createPredictionSession",
+            runForUserLocked("createPredictionSession", sessionId,
                     (service) -> service.onCreatePredictionSessionLocked(context, sessionId));
         }
 
         @Override
         public void notifyAppTargetEvent(@NonNull AppPredictionSessionId sessionId,
                 @NonNull AppTargetEvent event) {
-            runForUserLocked("notifyAppTargetEvent",
+            runForUserLocked("notifyAppTargetEvent", sessionId,
                     (service) -> service.notifyAppTargetEventLocked(sessionId, event));
         }
 
         @Override
         public void notifyLaunchLocationShown(@NonNull AppPredictionSessionId sessionId,
                 @NonNull String launchLocation, @NonNull ParceledListSlice targetIds) {
-            runForUserLocked("notifyLaunchLocationShown", (service) ->
+            runForUserLocked("notifyLaunchLocationShown", sessionId, (service) ->
                     service.notifyLaunchLocationShownLocked(sessionId, launchLocation, targetIds));
         }
 
@@ -130,32 +131,32 @@
         public void sortAppTargets(@NonNull AppPredictionSessionId sessionId,
                 @NonNull ParceledListSlice targets,
                 IPredictionCallback callback) {
-            runForUserLocked("sortAppTargets",
+            runForUserLocked("sortAppTargets", sessionId,
                     (service) -> service.sortAppTargetsLocked(sessionId, targets, callback));
         }
 
         @Override
         public void registerPredictionUpdates(@NonNull AppPredictionSessionId sessionId,
                 @NonNull IPredictionCallback callback) {
-            runForUserLocked("registerPredictionUpdates",
+            runForUserLocked("registerPredictionUpdates", sessionId,
                     (service) -> service.registerPredictionUpdatesLocked(sessionId, callback));
         }
 
         public void unregisterPredictionUpdates(@NonNull AppPredictionSessionId sessionId,
                 @NonNull IPredictionCallback callback) {
-            runForUserLocked("unregisterPredictionUpdates",
+            runForUserLocked("unregisterPredictionUpdates", sessionId,
                     (service) -> service.unregisterPredictionUpdatesLocked(sessionId, callback));
         }
 
         @Override
         public void requestPredictionUpdate(@NonNull AppPredictionSessionId sessionId) {
-            runForUserLocked("requestPredictionUpdate",
+            runForUserLocked("requestPredictionUpdate", sessionId,
                     (service) -> service.requestPredictionUpdateLocked(sessionId));
         }
 
         @Override
         public void onDestroyPredictionSession(@NonNull AppPredictionSessionId sessionId) {
-            runForUserLocked("onDestroyPredictionSession",
+            runForUserLocked("onDestroyPredictionSession", sessionId,
                     (service) -> service.onDestroyPredictionSessionLocked(sessionId));
         }
 
@@ -167,9 +168,12 @@
                     .exec(this, in, out, err, args, callback, resultReceiver);
         }
 
-        private void runForUserLocked(@NonNull String func,
-                @NonNull Consumer<AppPredictionPerUserService> c) {
-            final int userId = UserHandle.getCallingUserId();
+        private void runForUserLocked(@NonNull final String func,
+                @NonNull final AppPredictionSessionId sessionId,
+                @NonNull final Consumer<AppPredictionPerUserService> c) {
+            ActivityManagerInternal am = LocalServices.getService(ActivityManagerInternal.class);
+            final int userId = am.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+                    sessionId.getUserId(), false, ALLOW_NON_FULL, null, null);
 
             Context ctx = getContext();
             if (!(ctx.checkCallingPermission(PACKAGE_USAGE_STATS) == PERMISSION_GRANTED
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
index 48d976b..4f49fb7 100644
--- a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
@@ -32,14 +32,11 @@
 import android.os.RemoteException;
 import android.service.appprediction.AppPredictionService;
 import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.infra.AbstractPerUserSystemService;
 
-import java.util.Map;
-import java.util.Set;
 import java.util.function.Consumer;
 
 /**
@@ -51,13 +48,9 @@
 
     private static final String TAG = AppPredictionPerUserService.class.getSimpleName();
 
-    /**
-     * A lookup of remote services in respect to their {@link ComponentName}.
-     */
-    @NonNull
+    @Nullable
     @GuardedBy("mLock")
-    private final ArrayMap<ComponentName, RemoteAppPredictionService> mRemoteServices =
-            new ArrayMap<>();
+    private RemoteAppPredictionService mRemoteService;
 
     /**
      * When {@code true}, remote service died but service state is kept so it's restored after
@@ -99,7 +92,7 @@
         if (enabledChanged) {
             if (!isEnabledLocked()) {
                 // Clear the remote service for the next call
-                mRemoteServices.clear();
+                mRemoteService = null;
             }
         }
         return enabledChanged;
@@ -111,14 +104,14 @@
     @GuardedBy("mLock")
     public void onCreatePredictionSessionLocked(@NonNull AppPredictionContext context,
             @NonNull AppPredictionSessionId sessionId) {
-        if (!mSessionInfos.containsKey(sessionId)) {
-            mSessionInfos.put(sessionId, new AppPredictionSessionInfo(sessionId, context,
-                    resolveComponentName(context), this::removeAppPredictionSessionInfo));
-        }
-        final RemoteAppPredictionService service = getRemoteServiceLocked(
-                getComponentName(sessionId));
+        final RemoteAppPredictionService service = getRemoteServiceLocked();
         if (service != null) {
             service.onCreatePredictionSession(context, sessionId);
+
+            if (!mSessionInfos.containsKey(sessionId)) {
+                mSessionInfos.put(sessionId, new AppPredictionSessionInfo(sessionId, context,
+                        this::removeAppPredictionSessionInfo));
+            }
         }
     }
 
@@ -128,8 +121,7 @@
     @GuardedBy("mLock")
     public void notifyAppTargetEventLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull AppTargetEvent event) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked(
-                getComponentName(sessionId));
+        final RemoteAppPredictionService service = getRemoteServiceLocked();
         if (service != null) {
             service.notifyAppTargetEvent(sessionId, event);
         }
@@ -141,8 +133,7 @@
     @GuardedBy("mLock")
     public void notifyLaunchLocationShownLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull String launchLocation, @NonNull ParceledListSlice targetIds) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked(
-                getComponentName(sessionId));
+        final RemoteAppPredictionService service = getRemoteServiceLocked();
         if (service != null) {
             service.notifyLaunchLocationShown(sessionId, launchLocation, targetIds);
         }
@@ -154,8 +145,7 @@
     @GuardedBy("mLock")
     public void sortAppTargetsLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull ParceledListSlice targets, @NonNull IPredictionCallback callback) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked(
-                getComponentName(sessionId));
+        final RemoteAppPredictionService service = getRemoteServiceLocked();
         if (service != null) {
             service.sortAppTargets(sessionId, targets, callback);
         }
@@ -167,8 +157,7 @@
     @GuardedBy("mLock")
     public void registerPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull IPredictionCallback callback) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked(
-                getComponentName(sessionId));
+        final RemoteAppPredictionService service = getRemoteServiceLocked();
         if (service != null) {
             service.registerPredictionUpdates(sessionId, callback);
 
@@ -185,8 +174,7 @@
     @GuardedBy("mLock")
     public void unregisterPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull IPredictionCallback callback) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked(
-                getComponentName(sessionId));
+        final RemoteAppPredictionService service = getRemoteServiceLocked();
         if (service != null) {
             service.unregisterPredictionUpdates(sessionId, callback);
 
@@ -202,8 +190,7 @@
      */
     @GuardedBy("mLock")
     public void requestPredictionUpdateLocked(@NonNull AppPredictionSessionId sessionId) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked(
-                getComponentName(sessionId));
+        final RemoteAppPredictionService service = getRemoteServiceLocked();
         if (service != null) {
             service.requestPredictionUpdate(sessionId);
         }
@@ -214,8 +201,7 @@
      */
     @GuardedBy("mLock")
     public void onDestroyPredictionSessionLocked(@NonNull AppPredictionSessionId sessionId) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked(
-                getComponentName(sessionId));
+        final RemoteAppPredictionService service = getRemoteServiceLocked();
         if (service != null) {
             service.onDestroyPredictionSession(sessionId);
 
@@ -243,7 +229,7 @@
             synchronized (mLock) {
                 if (mZombie) {
                     // Sanity check - shouldn't happen
-                    if (mRemoteServices.isEmpty()) {
+                    if (mRemoteService == null) {
                         Slog.w(TAG, "Cannot resurrect sessions because remote service is null");
                         return;
                     }
@@ -280,30 +266,22 @@
     }
 
     private void destroyAndRebindRemoteService() {
-        if (mRemoteServices.isEmpty()) {
+        if (mRemoteService == null) {
             return;
         }
 
         if (isDebug()) {
             Slog.d(TAG, "Destroying the old remote service.");
         }
-        final Set<Map.Entry<ComponentName, RemoteAppPredictionService>> services =
-                new ArraySet<>(mRemoteServices.entrySet());
-        mRemoteServices.clear();
-        services.stream().forEach(entry -> destroyAndRebindRemoteService(
-                entry.getKey(), entry.getValue()));
-    }
+        mRemoteService.destroy();
+        mRemoteService = null;
 
-    private void destroyAndRebindRemoteService(
-            @NonNull final ComponentName component,
-            @NonNull final RemoteAppPredictionService service) {
-        service.destroy();
-        final RemoteAppPredictionService newService = getRemoteServiceLocked(component);
-        if (newService != null) {
+        mRemoteService = getRemoteServiceLocked();
+        if (mRemoteService != null) {
             if (isDebug()) {
                 Slog.d(TAG, "Rebinding to the new remote service.");
             }
-            newService.reconnect();
+            mRemoteService.reconnect();
         }
     }
 
@@ -314,7 +292,7 @@
     private void resurrectSessionsLocked() {
         final int numSessions = mSessionInfos.size();
         if (isDebug()) {
-            Slog.d(TAG, "Resurrecting remote service (" + mRemoteServices + ") on "
+            Slog.d(TAG, "Resurrecting remote service (" + mRemoteService + ") on "
                     + numSessions + " sessions.");
         }
 
@@ -332,49 +310,32 @@
         }
     }
 
-    @Nullable
-    private ComponentName resolveComponentName(@NonNull final AppPredictionContext context) {
-        // TODO: add logic to determine serviceName based on context
-        final String serviceName = getComponentNameLocked();
-        if (serviceName == null) {
-            if (mMaster.verbose) {
-                Slog.v(TAG, "getRemoteServiceLocked(): not set, context = " + context);
-            }
-            return null;
-        }
-        return ComponentName.unflattenFromString(serviceName);
-    }
-
-    @Nullable
-    private ComponentName getComponentName(@NonNull final AppPredictionSessionId sessionId) {
-        AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
-        return sessionInfo == null ? null : sessionInfo.mComponentName;
-    }
-
     @GuardedBy("mLock")
     @Nullable
-    private RemoteAppPredictionService getRemoteServiceLocked(
-            @Nullable final ComponentName serviceComponent) {
-        if (serviceComponent == null) return null;
-        if (!mRemoteServices.containsKey(serviceComponent)) {
-            mRemoteServices.put(serviceComponent, new RemoteAppPredictionService(getContext(),
+    private RemoteAppPredictionService getRemoteServiceLocked() {
+        if (mRemoteService == null) {
+            final String serviceName = getComponentNameLocked();
+            if (serviceName == null) {
+                if (mMaster.verbose) {
+                    Slog.v(TAG, "getRemoteServiceLocked(): not set");
+                }
+                return null;
+            }
+            ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
+
+            mRemoteService = new RemoteAppPredictionService(getContext(),
                     AppPredictionService.SERVICE_INTERFACE, serviceComponent, mUserId, this,
-                    mMaster.isBindInstantServiceAllowed(), mMaster.verbose));
+                    mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
         }
 
-        return mRemoteServices.get(serviceComponent);
+        return mRemoteService;
     }
 
     private static final class AppPredictionSessionInfo {
         private static final boolean DEBUG = false;  // Do not submit with true
 
-        @NonNull
         private final AppPredictionSessionId mSessionId;
-        @NonNull
         private final AppPredictionContext mPredictionContext;
-        @Nullable
-        private final ComponentName mComponentName;
-        @NonNull
         private final Consumer<AppPredictionSessionId> mRemoveSessionInfoAction;
 
         private final RemoteCallbackList<IPredictionCallback> mCallbacks =
@@ -391,17 +352,13 @@
                     }
                 };
 
-        AppPredictionSessionInfo(
-                @NonNull final AppPredictionSessionId id,
-                @NonNull final AppPredictionContext predictionContext,
-                @Nullable final ComponentName componentName,
-                @NonNull final Consumer<AppPredictionSessionId> removeSessionInfoAction) {
+        AppPredictionSessionInfo(AppPredictionSessionId id, AppPredictionContext predictionContext,
+                Consumer<AppPredictionSessionId> removeSessionInfoAction) {
             if (DEBUG) {
                 Slog.d(TAG, "Creating AppPredictionSessionInfo for session Id=" + id);
             }
             mSessionId = id;
             mPredictionContext = predictionContext;
-            mComponentName = componentName;
             mRemoveSessionInfoAction = removeSessionInfoAction;
         }
 
@@ -433,8 +390,8 @@
         void resurrectSessionLocked(AppPredictionPerUserService service) {
             int callbackCount = mCallbacks.getRegisteredCallbackCount();
             if (DEBUG) {
-                Slog.d(TAG, "Resurrecting remote service (" + service.getRemoteServiceLocked(
-                        mComponentName) + ") for session Id=" + mSessionId + " and "
+                Slog.d(TAG, "Resurrecting remote service (" + service.getRemoteServiceLocked()
+                        + ") for session Id=" + mSessionId + " and "
                         + callbackCount + " callbacks.");
             }
             service.onCreatePredictionSessionLocked(mPredictionContext, mSessionId);
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 53f85ea..72029d1 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2436,65 +2436,9 @@
                     forceRemoveSelfLocked(AutofillManager.STATE_UNKNOWN_COMPAT_MODE);
                     return;
                 }
-
                 if (!Objects.equals(value, viewState.getCurrentValue())) {
-                    if ((value == null || value.isEmpty())
-                            && viewState.getCurrentValue() != null
-                            && viewState.getCurrentValue().isText()
-                            && viewState.getCurrentValue().getTextValue() != null
-                            && getSaveInfoLocked() != null) {
-                        final int length = viewState.getCurrentValue().getTextValue().length();
-                        if (sDebug) {
-                            Slog.d(TAG, "updateLocked(" + id + "): resetting value that was "
-                                    + length + " chars long");
-                        }
-                        final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_VALUE_RESET)
-                                .addTaggedData(MetricsEvent.FIELD_AUTOFILL_PREVIOUS_LENGTH, length);
-                        mMetricsLogger.write(log);
-                    }
-
-                    // Always update the internal state.
-                    viewState.setCurrentValue(value);
-
-                    // Must check if this update was caused by autofilling the view, in which
-                    // case we just update the value, but not the UI.
-                    final AutofillValue filledValue = viewState.getAutofilledValue();
-                    if (filledValue != null) {
-                        if (filledValue.equals(value)) {
-                            if (sVerbose) {
-                                Slog.v(TAG, "ignoring autofilled change on id " + id);
-                            }
-                            viewState.resetState(ViewState.STATE_CHANGED);
-                            return;
-                        }
-                        else {
-                            if ((viewState.id.equals(this.mCurrentViewId)) &&
-                                    (viewState.getState() & ViewState.STATE_AUTOFILLED) != 0) {
-                                // Remove autofilled state once field is changed after autofilling.
-                                if (sVerbose) {
-                                    Slog.v(TAG, "field changed after autofill on id " + id);
-                                }
-                                viewState.resetState(ViewState.STATE_AUTOFILLED);
-                                final ViewState currentView = mViewStates.get(mCurrentViewId);
-                                currentView.maybeCallOnFillReady(flags);
-                            }
-                        }
-                    }
-
-                    // Update the internal state...
-                    viewState.setState(ViewState.STATE_CHANGED);
-
-                    //..and the UI
-                    final String filterText;
-                    if (value == null || !value.isText()) {
-                        filterText = null;
-                    } else {
-                        final CharSequence text = value.getTextValue();
-                        // Text should never be null, but it doesn't hurt to check to avoid a
-                        // system crash...
-                        filterText = (text == null) ? null : text.toString();
-                    }
-                    getUiForShowing().filterFillUi(filterText, this);
+                    logIfViewClearedLocked(id, value, viewState);
+                    updateViewStateAndUiOnValueChangedLocked(id, value, viewState, flags);
                 }
                 break;
             case ACTION_VIEW_ENTERED:
@@ -2573,6 +2517,68 @@
         return ArrayUtils.contains(response.getIgnoredIds(), id);
     }
 
+    @GuardedBy("mLock")
+    private void logIfViewClearedLocked(AutofillId id, AutofillValue value, ViewState viewState) {
+        if ((value == null || value.isEmpty())
+                && viewState.getCurrentValue() != null
+                && viewState.getCurrentValue().isText()
+                && viewState.getCurrentValue().getTextValue() != null
+                && getSaveInfoLocked() != null) {
+            final int length = viewState.getCurrentValue().getTextValue().length();
+            if (sDebug) {
+                Slog.d(TAG, "updateLocked(" + id + "): resetting value that was "
+                        + length + " chars long");
+            }
+            final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_VALUE_RESET)
+                    .addTaggedData(MetricsEvent.FIELD_AUTOFILL_PREVIOUS_LENGTH, length);
+            mMetricsLogger.write(log);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void updateViewStateAndUiOnValueChangedLocked(AutofillId id, AutofillValue value,
+            ViewState viewState, int flags) {
+        viewState.setCurrentValue(value);
+
+        final String filterText;
+        if (value == null || !value.isText()) {
+            filterText = null;
+        } else {
+            final CharSequence text = value.getTextValue();
+            // Text should never be null, but it doesn't hurt to check to avoid a
+            // system crash...
+            filterText = (text == null) ? null : text.toString();
+        }
+
+        final AutofillValue filledValue = viewState.getAutofilledValue();
+        if (filledValue != null) {
+            if (filledValue.equals(value)) {
+                // When the update is caused by autofilling the view, just update the
+                // value, not the UI.
+                if (sVerbose) {
+                    Slog.v(TAG, "ignoring autofilled change on id " + id);
+                }
+                viewState.resetState(ViewState.STATE_CHANGED);
+                return;
+            } else if ((viewState.id.equals(this.mCurrentViewId))
+                    && (viewState.getState() & ViewState.STATE_AUTOFILLED) != 0) {
+                // Remove autofilled state once field is changed after autofilling.
+                if (sVerbose) {
+                    Slog.v(TAG, "field changed after autofill on id " + id);
+                }
+                viewState.resetState(ViewState.STATE_AUTOFILLED);
+                final ViewState currentView = mViewStates.get(mCurrentViewId);
+                currentView.maybeCallOnFillReady(flags);
+            }
+        } else if (viewState.id.equals(this.mCurrentViewId)
+                && (viewState.getState() & ViewState.STATE_INLINE_SHOWN) != 0) {
+            requestShowInlineSuggestionsLocked(viewState.getResponse(), filterText);
+        }
+
+        viewState.setState(ViewState.STATE_CHANGED);
+        getUiForShowing().filterFillUi(filterText, this);
+    }
+
     @Override
     public void onFillReady(@NonNull FillResponse response, @NonNull AutofillId filledId,
             @Nullable AutofillValue value) {
@@ -2602,7 +2608,9 @@
 
         if (response.supportsInlineSuggestions()) {
             synchronized (mLock) {
-                if (requestShowInlineSuggestionsLocked(response)) {
+                if (requestShowInlineSuggestionsLocked(response, filterText)) {
+                    final ViewState currentView = mViewStates.get(mCurrentViewId);
+                    currentView.setState(ViewState.STATE_INLINE_SHOWN);
                     //TODO(b/137800469): Fix it to log showed only when IME asks for inflation,
                     // rather than here where framework sends back the response.
                     mService.logDatasetShown(id, mClientState);
@@ -2645,7 +2653,8 @@
     /**
      * Returns whether we made a request to show inline suggestions.
      */
-    private boolean requestShowInlineSuggestionsLocked(@NonNull FillResponse response) {
+    private boolean requestShowInlineSuggestionsLocked(@NonNull FillResponse response,
+            @Nullable String filterText) {
         final List<Dataset> datasets = response.getDatasets();
         if (datasets == null) {
             Log.w(TAG, "response returned null datasets");
@@ -2663,7 +2672,7 @@
         InlineSuggestionsResponse inlineSuggestionsResponse =
                 InlineSuggestionFactory.createInlineSuggestionsResponse(request,
                         response.getRequestId(),
-                        datasets.toArray(new Dataset[]{}), response.getInlineActions(),
+                        datasets.toArray(new Dataset[]{}), filterText, response.getInlineActions(),
                         mCurrentViewId, mContext, this, () -> {
                             synchronized (mLock) {
                                 requestHideFillUi(mCurrentViewId);
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index 84886f8..f7c24f0 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -74,6 +74,8 @@
     public static final int STATE_AUTOFILLED_ONCE = 0x800;
     /** View triggered the latest augmented autofill request. */
     public static final int STATE_TRIGGERED_AUGMENTED_AUTOFILL = 0x1000;
+    /** Inline suggestions were shown for this View. */
+    public static final int STATE_INLINE_SHOWN = 0x2000;
 
     public final AutofillId id;
 
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 5796142..5dc43ef 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -313,6 +313,8 @@
                         Slog.e(TAG, "Error inflating remote views", e);
                         continue;
                     }
+                    // TODO: Extract the shared filtering logic here and in FillUi to a common
+                    //  method.
                     final DatasetFieldFilter filter = dataset.getFilter(index);
                     Pattern filterPattern = null;
                     String valueText = null;
@@ -602,6 +604,7 @@
          * Returns whether this item matches the value input by the user so it can be included
          * in the filtered datasets.
          */
+        // TODO: Extract the shared filtering logic here and in FillUi to a common method.
         public boolean matches(CharSequence filterText) {
             if (TextUtils.isEmpty(filterText)) {
                 // Always show item when the user input is empty
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index 95a4a19..5f6e47b 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -17,6 +17,7 @@
 package com.android.server.autofill.ui;
 
 import static com.android.server.autofill.Helper.sDebug;
+import static com.android.server.autofill.Helper.sVerbose;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -24,10 +25,12 @@
 import android.os.RemoteException;
 import android.service.autofill.Dataset;
 import android.service.autofill.InlinePresentation;
+import android.text.TextUtils;
 import android.util.Slog;
 import android.view.SurfaceControl;
 import android.view.View;
 import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
 import android.view.inline.InlinePresentationSpec;
 import android.view.inputmethod.InlineSuggestion;
 import android.view.inputmethod.InlineSuggestionInfo;
@@ -42,6 +45,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.function.BiFunction;
+import java.util.regex.Pattern;
 
 public final class InlineSuggestionFactory {
     private static final String TAG = "InlineSuggestionFactory";
@@ -69,17 +73,19 @@
             @NonNull Runnable onErrorCallback) {
         if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
         return createInlineSuggestionsResponseInternal(/* isAugmented= */ true, request,
-                datasets, /* inlineActions= */ null, autofillId, context, onErrorCallback,
+                datasets, /* filterText= */ null, /* inlineActions= */ null, autofillId, context,
+                onErrorCallback,
                 (dataset, filedIndex) -> (v -> inlineSuggestionUiCallback.autofill(dataset)));
     }
 
     /**
      * Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by the
-     * autofill service.
+     * autofill service, potentially filtering the datasets.
      */
     public static InlineSuggestionsResponse createInlineSuggestionsResponse(
             @NonNull InlineSuggestionsRequest request, int requestId,
             @NonNull Dataset[] datasets,
+            @Nullable String filterText,
             @Nullable List<InlinePresentation> inlineActions,
             @NonNull AutofillId autofillId,
             @NonNull Context context,
@@ -87,15 +93,15 @@
             @NonNull Runnable onErrorCallback) {
         if (sDebug) Slog.d(TAG, "createInlineSuggestionsResponse called");
         return createInlineSuggestionsResponseInternal(/* isAugmented= */ false, request, datasets,
-                inlineActions, autofillId, context, onErrorCallback,
+                filterText, inlineActions, autofillId, context, onErrorCallback,
                 (dataset, filedIndex) -> (v -> client.fill(requestId, filedIndex, dataset)));
     }
 
     private static InlineSuggestionsResponse createInlineSuggestionsResponseInternal(
             boolean isAugmented, @NonNull InlineSuggestionsRequest request,
-            @NonNull Dataset[] datasets, @Nullable List<InlinePresentation> inlineActions,
-            @NonNull AutofillId autofillId, @NonNull Context context,
-            @NonNull Runnable onErrorCallback,
+            @NonNull Dataset[] datasets, @Nullable String filterText,
+            @Nullable List<InlinePresentation> inlineActions, @NonNull AutofillId autofillId,
+            @NonNull Context context, @NonNull Runnable onErrorCallback,
             @NonNull BiFunction<Dataset, Integer, View.OnClickListener> onClickListenerFactory) {
         final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>();
         final InlineSuggestionUi inlineSuggestionUi = new InlineSuggestionUi(context,
@@ -113,6 +119,9 @@
                 Slog.w(TAG, "InlinePresentation not found in dataset");
                 return null;
             }
+            if (!includeDataset(dataset, fieldIndex, filterText)) {
+                continue;
+            }
             InlineSuggestion inlineSuggestion = createInlineSuggestion(isAugmented, dataset,
                     fieldIndex, mergedInlinePresentation(request, i, inlinePresentation),
                     inlineSuggestionUi, onClickListenerFactory);
@@ -129,6 +138,38 @@
         return new InlineSuggestionsResponse(inlineSuggestions);
     }
 
+    // TODO: Extract the shared filtering logic here and in FillUi to a common method.
+    private static boolean includeDataset(Dataset dataset, int fieldIndex,
+            @Nullable String filterText) {
+        // Show everything when the user input is empty.
+        if (TextUtils.isEmpty(filterText)) {
+            return true;
+        }
+
+        final String constraintLowerCase = filterText.toString().toLowerCase();
+
+        // Use the filter provided by the service, if available.
+        final Dataset.DatasetFieldFilter filter = dataset.getFilter(fieldIndex);
+        if (filter != null) {
+            Pattern filterPattern = filter.pattern;
+            if (filterPattern == null) {
+                if (sVerbose) {
+                    Slog.v(TAG, "Explicitly disabling filter for dataset id" + dataset.getId());
+                }
+                return true;
+            }
+            return filterPattern.matcher(constraintLowerCase).matches();
+        }
+
+        final AutofillValue value = dataset.getFieldValues().get(fieldIndex);
+        if (value == null || !value.isText()) {
+            return dataset.getAuthentication() == null;
+        }
+        final String valueText = value.getTextValue().toString().toLowerCase();
+        return valueText.toLowerCase().startsWith(constraintLowerCase);
+    }
+
+
     private static InlineSuggestion createInlineAction(boolean isAugmented,
             @NonNull Context context,
             @NonNull InlinePresentation inlinePresentation,
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 687fb7d..c47cde3 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -153,7 +153,6 @@
 import android.security.KeyStore;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
-import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.LocalLog;
 import android.util.Log;
@@ -193,6 +192,7 @@
 import com.android.server.connectivity.NetworkDiagnostics;
 import com.android.server.connectivity.NetworkNotificationManager;
 import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
+import com.android.server.connectivity.NetworkRanker;
 import com.android.server.connectivity.PermissionMonitor;
 import com.android.server.connectivity.ProxyTracker;
 import com.android.server.connectivity.Vpn;
@@ -229,6 +229,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.SortedSet;
+import java.util.StringJoiner;
 import java.util.TreeSet;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -577,6 +578,7 @@
     final ConnectivityDiagnosticsHandler mConnectivityDiagnosticsHandler;
 
     private final DnsManager mDnsManager;
+    private final NetworkRanker mNetworkRanker;
 
     private boolean mSystemReady;
     private Intent mInitialBroadcast;
@@ -725,9 +727,9 @@
         private void maybeLogBroadcast(NetworkAgentInfo nai, DetailedState state, int type,
                 boolean isDefaultNetwork) {
             if (DBG) {
-                log("Sending " + state +
-                        " broadcast for type " + type + " " + nai.name() +
-                        " isDefaultNetwork=" + isDefaultNetwork);
+                log("Sending " + state
+                        + " broadcast for type " + type + " " + nai.toShortString()
+                        + " isDefaultNetwork=" + isDefaultNetwork);
             }
         }
 
@@ -807,14 +809,6 @@
             }
         }
 
-        private String naiToString(NetworkAgentInfo nai) {
-            String name = nai.name();
-            String state = (nai.networkInfo != null) ?
-                    nai.networkInfo.getState() + "/" + nai.networkInfo.getDetailedState() :
-                    "???/???";
-            return name + " " + state;
-        }
-
         public void dump(IndentingPrintWriter pw) {
             pw.println("mLegacyTypeTracker:");
             pw.increaseIndent();
@@ -829,7 +823,7 @@
                 for (int type = 0; type < mTypeLists.length; type++) {
                     if (mTypeLists[type] == null || mTypeLists[type].isEmpty()) continue;
                     for (NetworkAgentInfo nai : mTypeLists[type]) {
-                        pw.println(type + " " + naiToString(nai));
+                        pw.println(type + " " + nai.toShortString());
                     }
                 }
             }
@@ -965,6 +959,7 @@
 
         mMetricsLog = logger;
         mDefaultRequest = createDefaultInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST);
+        mNetworkRanker = new NetworkRanker();
         NetworkRequestInfo defaultNRI = new NetworkRequestInfo(null, mDefaultRequest, new Binder());
         mNetworkRequests.put(mDefaultRequest, defaultNRI);
         mNetworkRequestInfoLogs.log("REGISTER " + defaultNRI);
@@ -2796,7 +2791,7 @@
                         nai.everCaptivePortalDetected |= visible;
                         if (nai.lastCaptivePortalDetected &&
                             Settings.Global.CAPTIVE_PORTAL_MODE_AVOID == getCaptivePortalMode()) {
-                            if (DBG) log("Avoiding captive portal network: " + nai.name());
+                            if (DBG) log("Avoiding captive portal network: " + nai.toShortString());
                             nai.asyncChannel.sendMessage(
                                     NetworkAgent.CMD_PREVENT_AUTOMATIC_RECONNECT);
                             teardownUnneededNetwork(nai);
@@ -2855,7 +2850,7 @@
                 final String logMsg = !TextUtils.isEmpty(redirectUrl)
                         ? " with redirect to " + redirectUrl
                         : "";
-                log(nai.name() + " validation " + (valid ? "passed" : "failed") + logMsg);
+                log(nai.toShortString() + " validation " + (valid ? "passed" : "failed") + logMsg);
             }
             if (valid != nai.lastValidated) {
                 if (wasDefault) {
@@ -3136,13 +3131,13 @@
         //    one lingered request, start lingering.
         nai.updateLingerTimer();
         if (nai.isLingering() && nai.numForegroundNetworkRequests() > 0) {
-            if (DBG) log("Unlingering " + nai.name());
+            if (DBG) log("Unlingering " + nai.toShortString());
             nai.unlinger();
             logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER);
         } else if (unneeded(nai, UnneededFor.LINGER) && nai.getLingerExpiry() > 0) {
             if (DBG) {
                 final int lingerTime = (int) (nai.getLingerExpiry() - now);
-                log("Lingering " + nai.name() + " for " + lingerTime + "ms");
+                log("Lingering " + nai.toShortString() + " for " + lingerTime + "ms");
             }
             nai.linger();
             logNetworkEvent(nai, NetworkEvent.NETWORK_LINGER);
@@ -3206,7 +3201,7 @@
     private void disconnectAndDestroyNetwork(NetworkAgentInfo nai) {
         ensureRunningOnConnectivityServiceThread();
         if (DBG) {
-            log(nai.name() + " got DISCONNECTED, was satisfying " + nai.numNetworkRequests());
+            log(nai.toShortString() + " disconnected, was satisfying " + nai.numNetworkRequests());
         }
         // Clear all notifications of this network.
         mNotifier.clearNotification(nai.network.netId);
@@ -3264,7 +3259,7 @@
             mDefaultNetworkNai = null;
             updateDataActivityTracking(null /* newNetwork */, nai);
             notifyLockdownVpn(nai);
-            ensureNetworkTransitionWakelock(nai.name());
+            ensureNetworkTransitionWakelock(nai.toShortString());
         }
         mLegacyTypeTracker.remove(nai, wasDefault);
         if (!nai.networkCapabilities.hasTransport(TRANSPORT_VPN)) {
@@ -3487,8 +3482,8 @@
                 boolean wasBackgroundNetwork = nai.isBackgroundNetwork();
                 nai.removeRequest(nri.request.requestId);
                 if (VDBG || DDBG) {
-                    log(" Removing from current network " + nai.name() +
-                            ", leaving " + nai.numNetworkRequests() + " requests.");
+                    log(" Removing from current network " + nai.toShortString()
+                            + ", leaving " + nai.numNetworkRequests() + " requests.");
                 }
                 // If there are still lingered requests on this network, don't tear it down,
                 // but resume lingering instead.
@@ -3497,7 +3492,7 @@
                     notifyNetworkLosing(nai, now);
                 }
                 if (unneeded(nai, UnneededFor.TEARDOWN)) {
-                    if (DBG) log("no live requests for " + nai.name() + "; disconnecting");
+                    if (DBG) log("no live requests for " + nai.toShortString() + "; disconnecting");
                     teardownUnneededNetwork(nai);
                 } else {
                     wasKept = true;
@@ -3832,7 +3827,7 @@
         pw.increaseIndent();
         for (NetworkAgentInfo nai : networksSortedById()) {
             if (nai.avoidUnvalidated) {
-                pw.println(nai.name());
+                pw.println(nai.toShortString());
             }
         }
         pw.decreaseIndent();
@@ -3944,7 +3939,7 @@
 
     private void handleNetworkUnvalidated(NetworkAgentInfo nai) {
         NetworkCapabilities nc = nai.networkCapabilities;
-        if (DBG) log("handleNetworkUnvalidated " + nai.name() + " cap=" + nc);
+        if (DBG) log("handleNetworkUnvalidated " + nai.toShortString() + " cap=" + nc);
 
         if (!nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
             return;
@@ -5359,7 +5354,7 @@
                 detail = reason;
             }
             log(String.format("updateSignalStrengthThresholds: %s, sending %s to %s",
-                    detail, Arrays.toString(thresholdsArray.toArray()), nai.name()));
+                    detail, Arrays.toString(thresholdsArray.toArray()), nai.toShortString()));
         }
 
         nai.asyncChannel.sendMessage(
@@ -6272,9 +6267,9 @@
         // newLp is already a defensive copy.
         newLp.ensureDirectlyConnectedRoutes();
         if (VDBG || DDBG) {
-            log("Update of LinkProperties for " + nai.name() +
-                    "; created=" + nai.created +
-                    "; everConnected=" + nai.everConnected);
+            log("Update of LinkProperties for " + nai.toShortString()
+                    + "; created=" + nai.created
+                    + "; everConnected=" + nai.everConnected);
         }
         updateLinkProperties(nai, newLp, new LinkProperties(nai.linkProperties));
     }
@@ -6444,7 +6439,7 @@
             loge("Unknown NetworkAgentInfo in handleLingerComplete");
             return;
         }
-        if (DBG) log("handleLingerComplete for " + oldNetwork.name());
+        if (DBG) log("handleLingerComplete for " + oldNetwork.toShortString());
 
         // If we get here it means that the last linger timeout for this network expired. So there
         // must be no other active linger timers, and we must stop lingering.
@@ -6517,18 +6512,7 @@
     }
 
     // An accumulator class to gather the list of changes that result from a rematch.
-    // TODO : enrich to represent an entire set of changes to apply.
     private static class NetworkReassignment {
-        static class NetworkBgStatePair {
-            @NonNull final NetworkAgentInfo mNetwork;
-            final boolean mOldBackground;
-            NetworkBgStatePair(@NonNull final NetworkAgentInfo network,
-                    final boolean oldBackground) {
-                mNetwork = network;
-                mOldBackground = oldBackground;
-            }
-        }
-
         static class RequestReassignment {
             @NonNull public final NetworkRequestInfo mRequest;
             @Nullable public final NetworkAgentInfo mOldNetwork;
@@ -6540,44 +6524,34 @@
                 mOldNetwork = oldNetwork;
                 mNewNetwork = newNetwork;
             }
+
+            public String toString() {
+                return mRequest.request.requestId + " : "
+                        + (null != mOldNetwork ? mOldNetwork.network.netId : "null")
+                        + " → " + (null != mNewNetwork ? mNewNetwork.network.netId : "null");
+            }
         }
 
-        @NonNull private final Set<NetworkBgStatePair> mRematchedNetworks = new ArraySet<>();
-        @NonNull private final Map<NetworkRequestInfo, RequestReassignment> mReassignments =
-                new ArrayMap<>();
-
-        @NonNull Iterable<NetworkBgStatePair> getRematchedNetworks() {
-            return mRematchedNetworks;
-        }
+        @NonNull private final ArrayList<RequestReassignment> mReassignments = new ArrayList<>();
 
         @NonNull Iterable<RequestReassignment> getRequestReassignments() {
-            return mReassignments.values();
+            return mReassignments;
         }
 
         void addRequestReassignment(@NonNull final RequestReassignment reassignment) {
-            final RequestReassignment oldChange = mReassignments.get(reassignment.mRequest);
-            if (null == oldChange) {
-                mReassignments.put(reassignment.mRequest, reassignment);
-                return;
+            if (!Build.IS_USER) {
+                // The code is never supposed to add two reassignments of the same request. Make
+                // sure this stays true, but without imposing this expensive check on all
+                // reassignments on all user devices.
+                for (final RequestReassignment existing : mReassignments) {
+                    if (existing.mRequest.equals(reassignment.mRequest)) {
+                        throw new IllegalStateException("Trying to reassign ["
+                                + reassignment + "] but already have ["
+                                + existing + "]");
+                    }
+                }
             }
-            if (oldChange.mNewNetwork != reassignment.mOldNetwork) {
-                throw new IllegalArgumentException("Reassignment <" + reassignment.mRequest + "> ["
-                        + reassignment.mOldNetwork + " -> " + reassignment.mNewNetwork
-                        + "] conflicts with ["
-                        + oldChange.mOldNetwork + " -> " + oldChange.mNewNetwork + "]");
-            }
-            // There was already a note to reassign this request from a network A to a network B,
-            // and a reassignment is added from network B to some other network C. The following
-            // synthesizes the merged reassignment that goes A -> C. An interesting (but not
-            // special) case to think about is when B is null, which can happen when the rematch
-            // loop notices the current satisfier doesn't satisfy the request any more, but
-            // hasn't yet encountered another network that could.
-            mReassignments.put(reassignment.mRequest, new RequestReassignment(reassignment.mRequest,
-                    oldChange.mOldNetwork, reassignment.mNewNetwork));
-        }
-
-        void addRematchedNetwork(@NonNull final NetworkBgStatePair network) {
-            mRematchedNetworks.add(network);
+            mReassignments.add(reassignment);
         }
 
         // Will return null if this reassignment does not change the network assigned to
@@ -6589,98 +6563,25 @@
             }
             return null;
         }
-    }
 
-    // TODO : remove this when it's useless
-    @NonNull private NetworkReassignment computeInitialReassignment() {
-        final NetworkReassignment change = new NetworkReassignment();
-        for (NetworkRequestInfo nri : mNetworkRequests.values()) {
-            change.addRequestReassignment(new NetworkReassignment.RequestReassignment(nri,
-                    nri.mSatisfier, nri.mSatisfier));
-        }
-        return change;
-    }
-
-    private ArrayMap<NetworkRequestInfo, NetworkAgentInfo> computeRequestReassignmentForNetwork(
-            @NonNull final NetworkReassignment changes,
-            @NonNull final NetworkAgentInfo newNetwork) {
-        final int score = newNetwork.getCurrentScore();
-        final ArrayMap<NetworkRequestInfo, NetworkAgentInfo> reassignedRequests = new ArrayMap<>();
-        for (NetworkRequestInfo nri : mNetworkRequests.values()) {
-            // Process requests in the first pass and listens in the second pass. This allows us to
-            // change a network's capabilities depending on which requests it has. This is only
-            // correct if the change in capabilities doesn't affect whether the network satisfies
-            // requests or not, and doesn't affect the network's score.
-            if (nri.request.isListen()) continue;
-
-            // The reassignment has been seeded with the initial assignment, therefore
-            // getReassignment can't be null and mNewNetwork is only null if there was no
-            // satisfier in the first place or there was an explicit reassignment to null.
-            final NetworkAgentInfo currentNetwork = changes.getReassignment(nri).mNewNetwork;
-            final boolean satisfies = newNetwork.satisfies(nri.request);
-            if (newNetwork == currentNetwork && satisfies) continue;
-
-            // check if it satisfies the NetworkCapabilities
-            if (VDBG) log("  checking if request is satisfied: " + nri.request);
-            if (satisfies) {
-                // next check if it's better than any current network we're using for
-                // this request
-                if (VDBG || DDBG) {
-                    log("currentScore = "
-                            + (currentNetwork != null ? currentNetwork.getCurrentScore() : 0)
-                            + ", newScore = " + score);
-                }
-                if (currentNetwork == null || currentNetwork.getCurrentScore() < score) {
-                    reassignedRequests.put(nri, newNetwork);
-                    changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
-                            nri, currentNetwork, newNetwork));
-                }
-            } else if (newNetwork == currentNetwork) {
-                reassignedRequests.put(nri, null);
-                changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
-                        nri, currentNetwork, null));
+        public String toString() {
+            final StringJoiner sj = new StringJoiner(", " /* delimiter */,
+                    "NetReassign [" /* prefix */, "]" /* suffix */);
+            if (mReassignments.isEmpty()) return sj.add("no changes").toString();
+            for (final RequestReassignment rr : getRequestReassignments()) {
+                sj.add(rr.toString());
             }
+            return sj.toString();
         }
 
-        return reassignedRequests;
-    }
-
-    // Handles a network appearing or improving its score.
-    //
-    // - Evaluates all current NetworkRequests that can be
-    //   satisfied by newNetwork, and reassigns to newNetwork
-    //   any such requests for which newNetwork is the best.
-    //
-    // - Writes into the passed reassignment object all changes that should be done for
-    //   rematching this network with all requests, to be applied later.
-    //
-    // TODO : stop writing to the passed reassignment. This is temporarily more useful, but
-    // it's unidiomatic Java and it's hard to read.
-    //
-    // @param changes a currently-building list of changes to write to
-    // @param newNetwork is the network to be matched against NetworkRequests.
-    // @param now the time the rematch starts, as returned by SystemClock.elapsedRealtime();
-    private void rematchNetworkAndRequests(@NonNull final NetworkReassignment changes,
-            @NonNull final NetworkAgentInfo newNetwork, final long now) {
-        ensureRunningOnConnectivityServiceThread();
-        if (!newNetwork.everConnected) return;
-
-        changes.addRematchedNetwork(new NetworkReassignment.NetworkBgStatePair(newNetwork,
-                newNetwork.isBackgroundNetwork()));
-
-        if (VDBG || DDBG) log("rematching " + newNetwork.name());
-
-        final ArrayMap<NetworkRequestInfo, NetworkAgentInfo> reassignedRequests =
-                computeRequestReassignmentForNetwork(changes, newNetwork);
-
-        // Find and migrate to this Network any NetworkRequests for
-        // which this network is now the best.
-        for (final Map.Entry<NetworkRequestInfo, NetworkAgentInfo> entry :
-                reassignedRequests.entrySet()) {
-            final NetworkRequestInfo nri = entry.getKey();
-            final NetworkAgentInfo previousSatisfier = nri.mSatisfier;
-            final NetworkAgentInfo newSatisfier = entry.getValue();
-            updateSatisfiersForRematchRequest(nri, previousSatisfier, newSatisfier, now);
+        public String debugString() {
+            final StringBuilder sb = new StringBuilder();
+            sb.append("NetworkReassignment :");
+            if (mReassignments.isEmpty()) return sb.append(" no changes").toString();
+            for (final RequestReassignment rr : getRequestReassignments()) {
+                sb.append("\n  ").append(rr);
+            }
+            return sb.append("\n").toString();
         }
     }
 
@@ -6689,10 +6590,10 @@
             @Nullable final NetworkAgentInfo newSatisfier,
             final long now) {
         if (newSatisfier != null) {
-            if (VDBG) log("rematch for " + newSatisfier.name());
+            if (VDBG) log("rematch for " + newSatisfier.toShortString());
             if (previousSatisfier != null) {
                 if (VDBG || DDBG) {
-                    log("   accepting network in place of " + previousSatisfier.name());
+                    log("   accepting network in place of " + previousSatisfier.toShortString());
                 }
                 previousSatisfier.removeRequest(nri.request.requestId);
                 previousSatisfier.lingerRequest(nri.request, now, mLingerDelayMs);
@@ -6701,11 +6602,12 @@
             }
             newSatisfier.unlingerRequest(nri.request);
             if (!newSatisfier.addRequest(nri.request)) {
-                Slog.wtf(TAG, "BUG: " + newSatisfier.name() + " already has " + nri.request);
+                Slog.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
+                        + nri.request);
             }
         } else {
             if (DBG) {
-                log("Network " + previousSatisfier.name() + " stopped satisfying"
+                log("Network " + previousSatisfier.toShortString() + " stopped satisfying"
                         + " request " + nri.request.requestId);
             }
             previousSatisfier.removeRequest(nri.request.requestId);
@@ -6713,30 +6615,67 @@
         nri.mSatisfier = newSatisfier;
     }
 
+    @NonNull
+    private NetworkReassignment computeNetworkReassignment() {
+        ensureRunningOnConnectivityServiceThread();
+        final NetworkReassignment changes = new NetworkReassignment();
+
+        // Gather the list of all relevant agents and sort them by score.
+        final ArrayList<NetworkAgentInfo> nais = new ArrayList<>();
+        for (final NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+            if (!nai.everConnected) continue;
+            nais.add(nai);
+        }
+
+        for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
+            if (nri.request.isListen()) continue;
+            final NetworkAgentInfo bestNetwork = mNetworkRanker.getBestNetwork(nri.request, nais);
+            if (bestNetwork != nri.mSatisfier) {
+                // bestNetwork may be null if no network can satisfy this request.
+                changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
+                        nri, nri.mSatisfier, bestNetwork));
+            }
+        }
+        return changes;
+    }
+
     /**
      * Attempt to rematch all Networks with NetworkRequests.  This may result in Networks
      * being disconnected.
      */
     private void rematchAllNetworksAndRequests() {
-        // TODO: This may be slow, and should be optimized. Unfortunately at this moment the
-        // processing is network-major instead of request-major (the code iterates through all
-        // networks, then for each it iterates for all requests), which is a problem for re-scoring
-        // requests. Once the code has switched to a request-major iteration style, this can
-        // be optimized to only do the processing needed.
+        // TODO: This may be slow, and should be optimized.
         final long now = SystemClock.elapsedRealtime();
-        final NetworkAgentInfo oldDefaultNetwork = getDefaultNetwork();
+        final NetworkReassignment changes = computeNetworkReassignment();
+        if (VDBG || DDBG) {
+            log(changes.debugString());
+        } else if (DBG) {
+            log(changes.toString()); // Shorter form, only one line of log
+        }
+        applyNetworkReassignment(changes, now);
+    }
 
-        final NetworkAgentInfo[] nais = mNetworkAgentInfos.values().toArray(
-                new NetworkAgentInfo[mNetworkAgentInfos.size()]);
-        // Rematch higher scoring networks first to prevent requests first matching a lower
-        // scoring network and then a higher scoring network, which could produce multiple
-        // callbacks.
-        Arrays.sort(nais);
-        final NetworkReassignment changes = computeInitialReassignment();
+    private void applyNetworkReassignment(@NonNull final NetworkReassignment changes,
+            final long now) {
+        final Collection<NetworkAgentInfo> nais = mNetworkAgentInfos.values();
+
+        // Since most of the time there are only 0 or 1 background networks, it would probably
+        // be more efficient to just use an ArrayList here. TODO : measure performance
+        final ArraySet<NetworkAgentInfo> oldBgNetworks = new ArraySet<>();
         for (final NetworkAgentInfo nai : nais) {
-            rematchNetworkAndRequests(changes, nai, now);
+            if (nai.isBackgroundNetwork()) oldBgNetworks.add(nai);
         }
 
+        // First, update the lists of satisfied requests in the network agents. This is necessary
+        // because some code later depends on this state to be correct, most prominently computing
+        // the linger status.
+        for (final NetworkReassignment.RequestReassignment event :
+                changes.getRequestReassignments()) {
+            updateSatisfiersForRematchRequest(event.mRequest, event.mOldNetwork,
+                    event.mNewNetwork, now);
+        }
+
+        final NetworkAgentInfo oldDefaultNetwork = getDefaultNetwork();
         final NetworkRequestInfo defaultRequestInfo = mNetworkRequests.get(mDefaultRequest);
         final NetworkReassignment.RequestReassignment reassignment =
                 changes.getReassignment(defaultRequestInfo);
@@ -6761,8 +6700,6 @@
         // before LegacyTypeTracker sends legacy broadcasts
         for (final NetworkReassignment.RequestReassignment event :
                 changes.getRequestReassignments()) {
-            if (event.mOldNetwork == event.mNewNetwork) continue;
-
             // Tell NetworkProviders about the new score, so they can stop
             // trying to connect if they know they cannot match it.
             // TODO - this could get expensive if there are a lot of outstanding requests for this
@@ -6777,17 +6714,10 @@
             }
         }
 
-        for (final NetworkReassignment.NetworkBgStatePair event : changes.getRematchedNetworks()) {
-            // Process listen requests and update capabilities if the background state has
-            // changed for this network. For consistency with previous behavior, send onLost
-            // callbacks before onAvailable.
-            processNewlyLostListenRequests(event.mNetwork);
-            if (event.mOldBackground != event.mNetwork.isBackgroundNetwork()) {
-                applyBackgroundChangeForRematch(event.mNetwork);
-            }
-            processNewlySatisfiedListenRequests(event.mNetwork);
-        }
-
+        // Update the linger state before processing listen callbacks, because the background
+        // computation depends on whether the network is lingering. Don't send the LOSING callbacks
+        // just yet though, because they have to be sent after the listens are processed to keep
+        // backward compatibility.
         final ArrayList<NetworkAgentInfo> lingeredNetworks = new ArrayList<>();
         for (final NetworkAgentInfo nai : nais) {
             // Rematching may have altered the linger state of some networks, so update all linger
@@ -6800,6 +6730,19 @@
             }
         }
 
+        for (final NetworkAgentInfo nai : nais) {
+            if (!nai.everConnected) continue;
+            final boolean oldBackground = oldBgNetworks.contains(nai);
+            // Process listen requests and update capabilities if the background state has
+            // changed for this network. For consistency with previous behavior, send onLost
+            // callbacks before onAvailable.
+            processNewlyLostListenRequests(nai);
+            if (oldBackground != nai.isBackgroundNetwork()) {
+                applyBackgroundChangeForRematch(nai);
+            }
+            processNewlySatisfiedListenRequests(nai);
+        }
+
         for (final NetworkAgentInfo nai : lingeredNetworks) {
             notifyNetworkLosing(nai, now);
         }
@@ -6821,7 +6764,7 @@
                         notifyNetworkLosing(nai, now);
                     }
                 } else {
-                    if (DBG) log("Reaping " + nai.name());
+                    if (DBG) log("Reaping " + nai.toShortString());
                     teardownUnneededNetwork(nai);
                 }
             }
@@ -6849,7 +6792,7 @@
     private void updateLegacyTypeTrackerAndVpnLockdownForRematch(
             @Nullable final NetworkAgentInfo oldDefaultNetwork,
             @Nullable final NetworkAgentInfo newDefaultNetwork,
-            @NonNull final NetworkAgentInfo[] nais) {
+            @NonNull final Collection<NetworkAgentInfo> nais) {
         if (oldDefaultNetwork != newDefaultNetwork) {
             // Maintain the illusion : since the legacy API only understands one network at a time,
             // if the default network changed, apps should see a disconnected broadcast for the
@@ -6888,7 +6831,9 @@
         // they may get old info. Reverse this after the old startUsing api is removed.
         // This is on top of the multiple intent sequencing referenced in the todo above.
         for (NetworkAgentInfo nai : nais) {
-            addNetworkToLegacyTypeTracker(nai);
+            if (nai.everConnected) {
+                addNetworkToLegacyTypeTracker(nai);
+            }
         }
     }
 
@@ -6969,8 +6914,8 @@
         notifyLockdownVpn(networkAgent);
 
         if (DBG) {
-            log(networkAgent.name() + " EVENT_NETWORK_INFO_CHANGED, going from " +
-                    oldInfo.getState() + " to " + state);
+            log(networkAgent.toShortString() + " EVENT_NETWORK_INFO_CHANGED, going from "
+                    + oldInfo.getState() + " to " + state);
         }
 
         if (!networkAgent.created
@@ -6988,7 +6933,7 @@
             networkAgent.everConnected = true;
 
             if (networkAgent.linkProperties == null) {
-                Slog.wtf(TAG, networkAgent.name() + " connected with null LinkProperties");
+                Slog.wtf(TAG, networkAgent.toShortString() + " connected with null LinkProperties");
             }
 
             // NetworkCapabilities need to be set before sending the private DNS config to
@@ -7048,7 +6993,7 @@
     }
 
     private void updateNetworkScore(NetworkAgentInfo nai, NetworkScore ns) {
-        if (VDBG || DDBG) log("updateNetworkScore for " + nai.name() + " to " + ns);
+        if (VDBG || DDBG) log("updateNetworkScore for " + nai.toShortString() + " to " + ns);
         nai.setNetworkScore(ns);
         rematchAllNetworksAndRequests();
         sendUpdatedScoreToFactories(nai);
@@ -7194,14 +7139,12 @@
     protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType, int arg1) {
         if (VDBG || DDBG) {
             String notification = ConnectivityManager.getCallbackName(notifyType);
-            log("notifyType " + notification + " for " + networkAgent.name());
+            log("notifyType " + notification + " for " + networkAgent.toShortString());
         }
         for (int i = 0; i < networkAgent.numNetworkRequests(); i++) {
             NetworkRequest nr = networkAgent.requestAt(i);
             NetworkRequestInfo nri = mNetworkRequests.get(nr);
             if (VDBG) log(" sending notification for " + nr);
-            // TODO: if we're in the middle of a rematch, can we send a CAP_CHANGED callback for
-            // a network that no longer satisfies the listen?
             if (nri.mPendingIntent == null) {
                 callCallbackForRequest(nri, networkAgent, notifyType, arg1);
             } else {
@@ -7821,7 +7764,13 @@
             @NonNull NetworkAgentInfo nai, long timestampMillis, int detectionMethod,
             @NonNull PersistableBundle extras) {
         final DataStallReport report =
-                new DataStallReport(nai.network, timestampMillis, detectionMethod, extras);
+                new DataStallReport(
+                        nai.network,
+                        timestampMillis,
+                        detectionMethod,
+                        nai.linkProperties,
+                        nai.networkCapabilities,
+                        extras);
         final List<IConnectivityDiagnosticsCallback> results =
                 getMatchingPermissionedCallbacks(nai);
         for (final IConnectivityDiagnosticsCallback cb : results) {
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 63cddac..7aa4661 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -27,9 +27,12 @@
 import static android.location.LocationManager.PASSIVE_PROVIDER;
 import static android.os.PowerManager.locationPowerSaveModeToString;
 
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.PendingIntent;
@@ -43,7 +46,9 @@
 import android.location.GeocoderParams;
 import android.location.Geofence;
 import android.location.GnssMeasurementCorrections;
+import android.location.GnssRequest;
 import android.location.IBatchedLocationCallback;
+import android.location.IGnssAntennaInfoListener;
 import android.location.IGnssMeasurementsListener;
 import android.location.IGnssNavigationMessageListener;
 import android.location.IGnssStatusListener;
@@ -74,7 +79,6 @@
 import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Log;
-import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
 
@@ -105,6 +109,7 @@
 import com.android.server.location.PassiveProvider;
 import com.android.server.location.SettingsHelper;
 import com.android.server.location.UserInfoHelper;
+import com.android.server.location.UserInfoHelper.UserListener;
 import com.android.server.location.gnss.GnssManagerService;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 
@@ -123,7 +128,6 @@
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.TimeUnit;
 
 /**
  * The service class that manages LocationProviders and issues location
@@ -153,7 +157,7 @@
             if (phase == PHASE_SYSTEM_SERVICES_READY) {
                 // the location service must be functioning after this boot phase
                 mService.onSystemReady();
-            } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
+            } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
                 // some providers rely on third party code, so we wait to initialize
                 // providers until third party code is allowed to run
                 mService.onSystemThirdPartyAppsCanStart();
@@ -178,6 +182,9 @@
     // The maximum interval a location request can have and still be considered "high power".
     private static final long HIGH_POWER_INTERVAL_MS = 5 * 60 * 1000;
 
+    // The fastest interval that applications can receive coarse locations
+    private static final long FASTEST_COARSE_INTERVAL_MS = 10 * 60 * 1000;
+
     // maximum age of a location before it is no longer considered "current"
     private static final long MAX_CURRENT_LOCATION_AGE_MS = 10 * 1000;
 
@@ -206,8 +213,12 @@
     private PackageManager mPackageManager;
     private PowerManager mPowerManager;
 
-    private GeofenceManager mGeofenceManager;
+    // TODO: sharing a location fudger with mock providers can leak information as the mock provider
+    //   can be used to retrieve offset information. the fudger should likely be reset whenever mock
+    //   providers are added or removed
     private LocationFudger mLocationFudger;
+
+    private GeofenceManager mGeofenceManager;
     private GeocoderProxy mGeocodeProvider;
 
     @GuardedBy("mLock")
@@ -227,16 +238,6 @@
 
     private final LocationRequestStatistics mRequestStatistics = new LocationRequestStatistics();
 
-    // mapping from provider name to last known location
-    @GuardedBy("mLock")
-    private final HashMap<String, Location> mLastLocation = new HashMap<>();
-
-    // same as mLastLocation, but is not updated faster than LocationFudger.FASTEST_INTERVAL_MS.
-    // locations stored here are not fudged for coarse permissions.
-    @GuardedBy("mLock")
-    private final HashMap<String, Location> mLastLocationCoarseInterval =
-            new HashMap<>();
-
     @GuardedBy("mLock")
     @PowerManager.LocationPowerSaveMode
     private int mBatterySaverMode;
@@ -253,7 +254,7 @@
         mAppForegroundHelper = new AppForegroundHelper(mContext);
         mLocationUsageLogger = new LocationUsageLogger();
 
-        // set up passive provider -  we do this early because it has no dependencies on system
+        // set up passive provider - we do this early because it has no dependencies on system
         // services or external code that isn't ready yet, and because this allows the variable to
         // be final. other more complex providers are initialized later, when system services are
         // ready
@@ -280,18 +281,12 @@
         mSettingsHelper.onSystemReady();
         mAppForegroundHelper.onSystemReady();
 
-        if (GnssManagerService.isGnssSupported()) {
-            mGnssManagerService = new GnssManagerService(mContext, mSettingsHelper,
-                    mAppForegroundHelper, mLocationUsageLogger);
-            mGnssManagerService.onSystemReady();
-        }
-
         synchronized (mLock) {
             mPackageManager = mContext.getPackageManager();
             mAppOps = mContext.getSystemService(AppOpsManager.class);
             mPowerManager = mContext.getSystemService(PowerManager.class);
 
-            mLocationFudger = new LocationFudger(mContext, mHandler);
+            mLocationFudger = new LocationFudger(mSettingsHelper.getCoarseLocationAccuracyM());
             mGeofenceManager = new GeofenceManager(mContext, mSettingsHelper);
 
             PowerManagerInternal localPowerManager =
@@ -375,10 +370,11 @@
                 }
             }, UserHandle.ALL, intentFilter, null, mHandler);
 
-            // switching the user from null to current here performs the bulk of the initialization
-            // work. the user being changed will cause a reload of all user specific settings, which
-            // causes initialization, and propagates changes until a steady state is reached
-            onUserChanged(UserHandle.USER_NULL, mUserInfoHelper.getCurrentUserId());
+            // initialize the current users. we would get the user started notifications for these
+            // users eventually anyways, but this takes care of it as early as possible.
+            for (int userId: mUserInfoHelper.getCurrentUserIds()) {
+                onUserChanged(userId, UserListener.USER_STARTED);
+            }
         }
     }
 
@@ -426,7 +422,7 @@
         }
 
         if (D) {
-            Slog.d(TAG,
+            Log.d(TAG,
                     "Battery Saver location mode changed from "
                             + locationPowerSaveModeToString(mBatterySaverMode) + " to "
                             + locationPowerSaveModeToString(newLocationMode));
@@ -455,15 +451,15 @@
             Log.d(TAG, "[u" + userId + "] location enabled = " + enabled);
         }
 
-        synchronized (mLock) {
-            Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION)
-                    .putExtra(LocationManager.EXTRA_LOCATION_ENABLED, enabled)
-                    .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
-                    .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-            mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
+        Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION)
+                .putExtra(LocationManager.EXTRA_LOCATION_ENABLED, enabled)
+                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
+                .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
 
+        synchronized (mLock) {
             for (LocationProviderManager manager : mProviderManagers) {
-            manager.onEnabledChangedLocked(userId);
+                manager.onEnabledChangedLocked(userId);
             }
         }
     }
@@ -537,12 +533,6 @@
 
     @GuardedBy("mLock")
     private void initializeProvidersLocked() {
-        if (mGnssManagerService != null) {
-            LocationProviderManager gnssManager = new LocationProviderManager(GPS_PROVIDER);
-            mProviderManagers.add(gnssManager);
-            gnssManager.setRealProvider(mGnssManagerService.getGnssLocationProvider());
-        }
-
         LocationProviderProxy networkProvider = LocationProviderProxy.createAndRegister(
                 mContext,
                 NETWORK_LOCATION_SERVICE_ACTION,
@@ -553,7 +543,7 @@
             mProviderManagers.add(networkManager);
             networkManager.setRealProvider(networkProvider);
         } else {
-            Slog.w(TAG, "no network location provider found");
+            Log.w(TAG, "no network location provider found");
         }
 
         // ensure that a fused provider exists which will work in direct boot
@@ -573,14 +563,13 @@
             mProviderManagers.add(fusedManager);
             fusedManager.setRealProvider(fusedProvider);
         } else {
-            Slog.e(TAG, "no fused location provider found",
-                    new IllegalStateException("Location service needs a fused location provider"));
+            Log.e(TAG, "no fused location provider found");
         }
 
         // bind to geocoder provider
         mGeocodeProvider = GeocoderProxy.createAndRegister(mContext);
         if (mGeocodeProvider == null) {
-            Slog.e(TAG, "no geocoder provider found");
+            Log.e(TAG, "no geocoder provider found");
         }
 
         // bind to geofence proxy
@@ -589,7 +578,7 @@
             if (gpsGeofenceHardware != null) {
                 GeofenceProxy provider = GeofenceProxy.createAndBind(mContext, gpsGeofenceHardware);
                 if (provider == null) {
-                    Slog.d(TAG, "unable to bind to GeofenceProxy");
+                    Log.e(TAG, "unable to bind to GeofenceProxy");
                 }
             }
         }
@@ -618,22 +607,52 @@
                     Integer.parseInt(fragments[9]) /* accuracy */);
             addTestProvider(name, properties, mContext.getOpPackageName());
         }
+
+        // initialize gnss last because it has no awareness of boot phases and blindly assumes that
+        // all other location providers are loaded at initialization
+        if (GnssManagerService.isGnssSupported()) {
+            mGnssManagerService = new GnssManagerService(mContext, mSettingsHelper,
+                    mAppForegroundHelper, mLocationUsageLogger);
+            mGnssManagerService.onSystemReady();
+
+            LocationProviderManager gnssManager = new LocationProviderManager(GPS_PROVIDER);
+            mProviderManagers.add(gnssManager);
+            gnssManager.setRealProvider(mGnssManagerService.getGnssLocationProvider());
+        }
     }
 
-    private void onUserChanged(int oldUserId, int newUserId) {
-        if (D) {
-            Log.d(TAG, "foreground user is changing to " + newUserId);
-        }
-
-        synchronized (mLock) {
-            for (LocationProviderManager manager : mProviderManagers) {
-                // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility
-                mSettingsHelper.setLocationProviderAllowed(manager.getName(),
-                        manager.isEnabled(newUserId), newUserId);
-
-                manager.onEnabledChangedLocked(oldUserId);
-                manager.onEnabledChangedLocked(newUserId);
-            }
+    private void onUserChanged(@UserIdInt int userId, @UserListener.UserChange int change) {
+        switch (change) {
+            case UserListener.USER_SWITCHED:
+                if (D) {
+                    Log.d(TAG, "user " + userId + " current status changed");
+                }
+                synchronized (mLock) {
+                    for (LocationProviderManager manager : mProviderManagers) {
+                        manager.onEnabledChangedLocked(userId);
+                    }
+                }
+                break;
+            case UserListener.USER_STARTED:
+                if (D) {
+                    Log.d(TAG, "user " + userId + " started");
+                }
+                synchronized (mLock) {
+                    for (LocationProviderManager manager : mProviderManagers) {
+                        manager.onUserStarted(userId);
+                    }
+                }
+                break;
+            case UserListener.USER_STOPPED:
+                if (D) {
+                    Log.d(TAG, "user " + userId + " stopped");
+                }
+                synchronized (mLock) {
+                    for (LocationProviderManager manager : mProviderManagers) {
+                        manager.onUserStopped(userId);
+                    }
+                }
+                break;
         }
     }
 
@@ -644,25 +663,29 @@
 
         private final String mName;
 
-        // acquiring mLock makes operations on mProvider atomic, but is otherwise unnecessary
-        protected final MockableLocationProvider mProvider;
-
-        // enabled state for parent user ids, no entry implies false. location state is only kept
-        // for parent user ids, the location state for a profile user id is assumed to be the same
-        // as for the parent. if querying this structure, ensure that the user id being used is a
-        // parent id or the results may be incorrect.
+        // if the provider is enabled for a given user id - null or not present means unknown
         @GuardedBy("mLock")
         private final SparseArray<Boolean> mEnabled;
 
+        // last location for a given user
+        @GuardedBy("mLock")
+        private final SparseArray<Location> mLastLocation;
+
+        // last coarse location for a given user
+        @GuardedBy("mLock")
+        private final SparseArray<Location> mLastCoarseLocation;
+
+        // acquiring mLock makes operations on mProvider atomic, but is otherwise unnecessary
+        protected final MockableLocationProvider mProvider;
+
         private LocationProviderManager(String name) {
             mName = name;
-            mEnabled = new SparseArray<>(1);
+            mEnabled = new SparseArray<>(2);
+            mLastLocation = new SparseArray<>(2);
+            mLastCoarseLocation = new SparseArray<>(2);
 
             // initialize last since this lets our reference escape
             mProvider = new MockableLocationProvider(mLock, this);
-
-            // we can assume all users start with disabled location state since the initial state
-            // of all providers is disabled. no need to initialize mEnabled further.
         }
 
         public String getName() {
@@ -678,7 +701,26 @@
         }
 
         public void setMockProvider(@Nullable MockProvider provider) {
-            mProvider.setMockProvider(provider);
+            synchronized (mLock) {
+                mProvider.setMockProvider(provider);
+
+                // when removing a mock provider, also clear any mock last locations
+                if (provider == null) {
+                    for (int i = 0; i < mLastLocation.size(); i++) {
+                        Location lastLocation = mLastLocation.valueAt(i);
+                        if (lastLocation != null && lastLocation.isFromMockProvider()) {
+                            mLastLocation.setValueAt(i, null);
+                        }
+                    }
+
+                    for (int i = 0; i < mLastCoarseLocation.size(); i++) {
+                        Location lastCoarseLocation = mLastCoarseLocation.valueAt(i);
+                        if (lastCoarseLocation != null && lastCoarseLocation.isFromMockProvider()) {
+                            mLastCoarseLocation.setValueAt(i, null);
+                        }
+                    }
+                }
+            }
         }
 
         public Set<String> getPackages() {
@@ -690,6 +732,45 @@
             return mProvider.getState().properties;
         }
 
+        @Nullable
+        public Location getLastFineLocation(int userId) {
+            synchronized (mLock) {
+                return mLastLocation.get(userId);
+            }
+        }
+
+        @Nullable
+        public Location getLastCoarseLocation(int userId) {
+            synchronized (mLock) {
+                return mLastCoarseLocation.get(userId);
+            }
+        }
+
+        public void injectLastLocation(Location location, int userId) {
+            synchronized (mLock) {
+                if (mLastLocation.get(userId) == null) {
+                    setLastLocation(location, userId);
+                }
+            }
+        }
+
+        private void setLastLocation(Location location, int userId) {
+            synchronized (mLock) {
+                mLastLocation.put(userId, location);
+
+                // update last coarse interval only if enough time has passed
+                long timeDeltaMs = Long.MAX_VALUE;
+                Location coarseLocation = mLastCoarseLocation.get(userId);
+                if (coarseLocation != null) {
+                    timeDeltaMs = NANOSECONDS.toMillis(location.getElapsedRealtimeNanos())
+                            - NANOSECONDS.toMillis(coarseLocation.getElapsedRealtimeNanos());
+                }
+                if (timeDeltaMs > FASTEST_COARSE_INTERVAL_MS) {
+                    mLastCoarseLocation.put(userId, mLocationFudger.createCoarse(location));
+                }
+            }
+        }
+
         public void setMockProviderAllowed(boolean enabled) {
             synchronized (mLock) {
                 if (!mProvider.isMock()) {
@@ -742,23 +823,31 @@
             // don't validate mock locations
             if (!location.isFromMockProvider()) {
                 if (location.getLatitude() == 0 && location.getLongitude() == 0) {
-                    Slog.w(TAG, "blocking 0,0 location from " + mName + " provider");
+                    Log.w(TAG, "blocking 0,0 location from " + mName + " provider");
                     return;
                 }
             }
 
-            handleLocationChangedLocked(location, this);
+            if (!location.isComplete()) {
+                Log.w(TAG, "blocking incomplete location from " + mName + " provider");
+                return;
+            }
+
+            // update last location if the provider is enabled or if servicing a bypass request
+            boolean locationSettingsIgnored = mProvider.getCurrentRequest().locationSettingsIgnored;
+            for (int userId : mUserInfoHelper.getCurrentUserIds()) {
+                if (locationSettingsIgnored || isEnabled(userId)) {
+                    setLastLocation(location, userId);
+                }
+            }
+
+            handleLocationChangedLocked(this, location, mLocationFudger.createCoarse(location));
         }
 
         @GuardedBy("mLock")
         @Override
         public void onReportLocation(List<Location> locations) {
-            if (mGnssManagerService == null) {
-                return;
-            }
-
-            if (!GPS_PROVIDER.equals(mName) || !isEnabled()) {
-                Slog.w(TAG, "reportLocationBatch() called without user permission");
+            if (mGnssManagerService == null || !GPS_PROVIDER.equals(mName)) {
                 return;
             }
 
@@ -769,10 +858,7 @@
         @Override
         public void onStateChanged(State oldState, State newState) {
             if (oldState.allowed != newState.allowed) {
-                // it would be more correct to call this for all users, but we know this can
-                // only affect the current user since providers are disabled for non-current
-                // users
-                onEnabledChangedLocked(mUserInfoHelper.getCurrentUserId());
+                onEnabledChangedLocked(UserHandle.USER_ALL);
             }
         }
 
@@ -780,37 +866,74 @@
             mProvider.requestSetAllowed(allowed);
         }
 
-        public boolean isEnabled() {
-            return isEnabled(mUserInfoHelper.getCurrentUserId());
+        public void onUserStarted(int userId) {
+            synchronized (mLock) {
+                // clear the user's enabled state in order to force a reevalution of whether the
+                // provider is enabled or disabled for the given user. we clear the user's state
+                // first to ensure that a user starting never causes any change notifications. it's
+                // possible for us to observe a user before we observe it's been started (for
+                // example, another component gets a user started notification before us and
+                // registers a location request immediately), which would cause us to already have
+                // some state in place. when we eventually do get the user started notification
+                // ourselves we don't want to send a change notification based on the prior state
+                mEnabled.put(userId, null);
+                onEnabledChangedLocked(userId);
+            }
+        }
+
+        public void onUserStopped(int userId) {
+            synchronized (mLock) {
+                mEnabled.remove(userId);
+                mLastLocation.remove(userId);
+                mLastCoarseLocation.remove(userId);
+            }
         }
 
         public boolean isEnabled(int userId) {
+            if (userId == UserHandle.USER_NULL) {
+                // used during initialization - ignore since many lower level operations (checking
+                // settings for instance) do not support the null user
+                return false;
+            }
+
             synchronized (mLock) {
-                // normalize user id to always refer to parent since profile state is always the
-                // same as parent state
-                userId = mUserInfoHelper.getParentUserId(userId);
-                return mEnabled.get(userId, Boolean.FALSE);
+                Boolean enabled = mEnabled.get(userId);
+                if (enabled == null) {
+                    // this generally shouldn't occur, but might be possible due to race conditions
+                    // on when we are notified of new users
+                    Log.w(TAG, mName + " provider saw user " + userId + " unexpectedly");
+                    onEnabledChangedLocked(userId);
+                    enabled = Objects.requireNonNull(mEnabled.get(userId));
+                }
+
+                return enabled;
             }
         }
 
         @GuardedBy("mLock")
         public void onEnabledChangedLocked(int userId) {
             if (userId == UserHandle.USER_NULL) {
-                // only used during initialization - we don't care about the null user
+                // used during initialization - ignore since many lower level operations (checking
+                // settings for instance) do not support the null user
+                return;
+            } else if (userId == UserHandle.USER_ALL) {
+                // we know enabled changes can only happen for current users since providers are
+                // always disabled for all non-current users
+                for (int currentUserId : mUserInfoHelper.getCurrentUserIds()) {
+                    onEnabledChangedLocked(currentUserId);
+                }
                 return;
             }
 
-            // normalize user id to always refer to parent since profile state is always the same
-            // as parent state
-            userId = mUserInfoHelper.getParentUserId(userId);
-
             // if any property that contributes to "enabled" here changes state, it MUST result
             // in a direct or indrect call to onEnabledChangedLocked. this allows the provider to
             // guarantee that it will always eventually reach the correct state.
-            boolean enabled = (userId == mUserInfoHelper.getCurrentUserId())
-                    && mSettingsHelper.isLocationEnabled(userId) && mProvider.getState().allowed;
+            boolean enabled = mProvider.getState().allowed
+                    && mUserInfoHelper.isCurrentUserId(userId)
+                    && mSettingsHelper.isLocationEnabled(userId);
 
-            if (enabled == isEnabled(userId)) {
+            Boolean wasEnabled = mEnabled.get(userId);
+            if (wasEnabled != null && wasEnabled == enabled) {
                 return;
             }
 
@@ -820,28 +943,29 @@
                 Log.d(TAG, "[u" + userId + "] " + mName + " provider enabled = " + enabled);
             }
 
-            // fused and passive provider never get public updates for legacy reasons
-            if (!FUSED_PROVIDER.equals(mName) && !PASSIVE_PROVIDER.equals(mName)) {
-                // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility
-                mSettingsHelper.setLocationProviderAllowed(mName, enabled, userId);
-
-                Intent intent = new Intent(LocationManager.PROVIDERS_CHANGED_ACTION)
-                        .putExtra(LocationManager.EXTRA_PROVIDER_NAME, mName)
-                        .putExtra(LocationManager.EXTRA_PROVIDER_ENABLED, enabled)
-                        .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
-                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-                mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
+            // clear last locations if we become disabled and if not servicing a bypass request
+            if (!enabled && !mProvider.getCurrentRequest().locationSettingsIgnored) {
+                mLastLocation.put(userId, null);
+                mLastCoarseLocation.put(userId, null);
             }
 
-            if (!enabled) {
-                // If any provider has been disabled, clear all last locations for all
-                // providers. This is to be on the safe side in case a provider has location
-                // derived from this disabled provider.
-                mLastLocation.clear();
-                mLastLocationCoarseInterval.clear();
+            // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility
+            mSettingsHelper.setLocationProviderAllowed(mName, enabled, userId);
+
+            // do not send change notifications if we just saw this user for the first time
+            if (wasEnabled != null) {
+                // fused and passive provider never get public updates for legacy reasons
+                if (!FUSED_PROVIDER.equals(mName) && !PASSIVE_PROVIDER.equals(mName)) {
+                    Intent intent = new Intent(LocationManager.PROVIDERS_CHANGED_ACTION)
+                            .putExtra(LocationManager.EXTRA_PROVIDER_NAME, mName)
+                            .putExtra(LocationManager.EXTRA_PROVIDER_ENABLED, enabled)
+                            .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
+                            .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                    mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
+                }
             }
 
-            updateProviderEnabledLocked(this);
+            updateProviderEnabledLocked(this, enabled);
         }
 
         public void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) {
@@ -854,7 +978,11 @@
 
                 pw.increaseIndent();
 
-                pw.println("enabled=" + isEnabled());
+                // for now we only dump for the parent user
+                int userId = mUserInfoHelper.getCurrentUserIds()[0];
+                pw.println("last location=" + mLastLocation.get(userId));
+                pw.println("last coarse location=" + mLastCoarseLocation.get(userId));
+                pw.println("enabled=" + isEnabled(userId));
             }
 
             mProvider.dump(fd, pw, args);
@@ -1003,7 +1131,8 @@
                     if (manager == null) {
                         continue;
                     }
-                    if (!manager.isEnabled() && !isSettingsExempt(updateRecord)) {
+                    if (!manager.isEnabled(UserHandle.getUserId(mCallerIdentity.mUid))
+                            && !isSettingsExempt(updateRecord)) {
                         continue;
                     }
 
@@ -1419,7 +1548,7 @@
                 if (FUSED_PROVIDER.equals(name)) {
                     continue;
                 }
-                if (enabledOnly && !manager.isEnabled()) {
+                if (enabledOnly && !manager.isEnabled(UserHandle.getCallingUserId())) {
                     continue;
                 }
                 if (criteria != null
@@ -1461,15 +1590,12 @@
     }
 
     @GuardedBy("mLock")
-    private void updateProviderEnabledLocked(LocationProviderManager manager) {
-        boolean enabled = manager.isEnabled();
-
+    private void updateProviderEnabledLocked(LocationProviderManager manager, boolean enabled) {
         ArrayList<Receiver> deadReceivers = null;
-
         ArrayList<UpdateRecord> records = mRecordsByProvider.get(manager.getName());
         if (records != null) {
             for (UpdateRecord record : records) {
-                if (!mUserInfoHelper.isCurrentUserOrProfile(
+                if (!mUserInfoHelper.isCurrentUserId(
                         UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) {
                     continue;
                 }
@@ -1527,8 +1653,8 @@
             // initialize the low power mode to true and set to false if any of the records requires
             providerRequest.setLowPowerMode(true);
             for (UpdateRecord record : records) {
-                if (!mUserInfoHelper.isCurrentUserOrProfile(
-                        UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) {
+                int userId = UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid);
+                if (!mUserInfoHelper.isCurrentUserId(userId)) {
                     continue;
                 }
                 if (!checkLocationAccess(
@@ -1540,7 +1666,7 @@
                 }
                 final boolean isBatterySaverDisablingLocation = shouldThrottleRequests
                         || (isForegroundOnlyMode && !record.mIsForegroundUid);
-                if (!manager.isEnabled() || isBatterySaverDisablingLocation) {
+                if (!manager.isEnabled(userId) || isBatterySaverDisablingLocation) {
                     if (isSettingsExempt(record)) {
                         providerRequest.setLocationSettingsIgnored(true);
                         providerRequest.setLowPowerMode(false);
@@ -1586,7 +1712,7 @@
                 // TODO: overflow
                 long thresholdInterval = (providerRequest.getInterval() + 1000) * 3 / 2;
                 for (UpdateRecord record : records) {
-                    if (mUserInfoHelper.isCurrentUserOrProfile(
+                    if (mUserInfoHelper.isCurrentUserId(
                             UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) {
                         LocationRequest locationRequest = record.mRequest;
 
@@ -1696,11 +1822,8 @@
                 mStackTrace = new Throwable();
             }
 
-            ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
-            if (records == null) {
-                records = new ArrayList<>();
-                mRecordsByProvider.put(provider, records);
-            }
+            ArrayList<UpdateRecord> records = mRecordsByProvider.computeIfAbsent(provider,
+                    k -> new ArrayList<>());
             if (!records.contains(this)) {
                 records.add(this);
             }
@@ -1836,11 +1959,11 @@
                     break;
             }
             // throttle
-            if (sanitizedRequest.getInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
-                sanitizedRequest.setInterval(LocationFudger.FASTEST_INTERVAL_MS);
+            if (sanitizedRequest.getInterval() < FASTEST_COARSE_INTERVAL_MS) {
+                sanitizedRequest.setInterval(FASTEST_COARSE_INTERVAL_MS);
             }
-            if (sanitizedRequest.getFastestInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
-                sanitizedRequest.setFastestInterval(LocationFudger.FASTEST_INTERVAL_MS);
+            if (sanitizedRequest.getFastestInterval() < FASTEST_COARSE_INTERVAL_MS) {
+                sanitizedRequest.setFastestInterval(FASTEST_COARSE_INTERVAL_MS);
             }
         }
         // make getFastestInterval() the minimum of interval and fastest interval
@@ -1915,7 +2038,7 @@
                     receiver = getReceiverLocked(listener, pid, uid, packageName, featureId,
                             workSource, hideFromAppOps, listenerIdentifier);
                 }
-                requestLocationUpdatesLocked(sanitizedRequest, receiver, uid, packageName);
+                requestLocationUpdatesLocked(sanitizedRequest, receiver);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -1923,8 +2046,7 @@
     }
 
     @GuardedBy("mLock")
-    private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver,
-            int uid, String packageName) {
+    private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver) {
         // Figure out the provider. Either its explicitly request (legacy use cases), or
         // use the fused provider
         if (request == null) request = DEFAULT_LOCATION_REQUEST;
@@ -1939,20 +2061,14 @@
         }
 
         UpdateRecord record = new UpdateRecord(name, request, receiver);
-        if (D) {
-            Log.d(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver))
-                    + " " + name + " " + request + " from " + packageName + "(" + uid + " "
-                    + (record.mIsForegroundUid ? "foreground" : "background")
-                    + (isThrottlingExempt(receiver.mCallerIdentity)
-                    ? " [whitelisted]" : "") + ")");
-        }
 
         UpdateRecord oldRecord = receiver.mUpdateRecords.put(name, record);
         if (oldRecord != null) {
             oldRecord.disposeLocked(false);
         }
 
-        if (!manager.isEnabled() && !isSettingsExempt(record)) {
+        int userId = UserHandle.getUserId(receiver.mCallerIdentity.mUid);
+        if (!manager.isEnabled(userId) && !isSettingsExempt(record)) {
             // Notify the listener that updates are currently disabled - but only if the request
             // does not ignore location settings
             receiver.callProviderEnabledLocked(name, false);
@@ -2029,91 +2145,65 @@
     }
 
     @Override
-    public Location getLastLocation(LocationRequest r, String packageName, String featureId) {
+    public Location getLastLocation(LocationRequest request, String packageName, String featureId) {
+        if (request == null) {
+            request = DEFAULT_LOCATION_REQUEST;
+        }
+
         enforceCallingOrSelfLocationPermission();
         enforceCallingOrSelfPackageName(packageName);
 
-        synchronized (mLock) {
-            LocationRequest request = r != null ? r : DEFAULT_LOCATION_REQUEST;
-            int allowedResolutionLevel = getCallerAllowedResolutionLevel();
-            // no need to sanitize this request, as only the provider name is used
-
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                if (mSettingsHelper.isLocationPackageBlacklisted(UserHandle.getUserId(uid),
-                        packageName)) {
-                    if (D) {
-                        Log.d(TAG, "not returning last loc for blacklisted app: "
-                                + packageName);
-                    }
-                    return null;
-                }
-
-                // Figure out the provider. Either its explicitly request (deprecated API's),
-                // or use the fused provider
-                String name = request.getProvider();
-                if (name == null) name = LocationManager.FUSED_PROVIDER;
-                LocationProviderManager manager = getLocationProviderManager(name);
-                if (manager == null) return null;
-
-                // only the current user or location providers may get location this way
-                if (!mUserInfoHelper.isCurrentUserOrProfile(UserHandle.getUserId(uid))
-                        && !mLocalService.isProviderPackage(packageName)) {
-                    return null;
-                }
-
-                if (!manager.isEnabled()) {
-                    return null;
-                }
-
-                Location location;
-                if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
-                    // Make sure that an app with coarse permissions can't get frequent location
-                    // updates by calling LocationManager.getLastKnownLocation repeatedly.
-                    location = mLastLocationCoarseInterval.get(name);
-                } else {
-                    location = mLastLocation.get(name);
-                }
-                if (location == null) {
-                    return null;
-                }
-
-                // Don't return stale location to apps with foreground-only location permission.
-                String op = resolutionLevelToOpStr(allowedResolutionLevel);
-                long locationAgeMs = TimeUnit.NANOSECONDS.toMillis(
-                        SystemClock.elapsedRealtime() - location.getElapsedRealtimeNanos());
-                if (locationAgeMs > mSettingsHelper.getMaxLastLocationAgeMs()
-                        && (mAppOps.unsafeCheckOp(op, uid, packageName)
-                        == AppOpsManager.MODE_FOREGROUND)) {
-                    return null;
-                }
-
-                Location lastLocation = null;
-                if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
-                    Location noGPSLocation = location.getExtraLocation(
-                            Location.EXTRA_NO_GPS_LOCATION);
-                    if (noGPSLocation != null) {
-                        lastLocation = new Location(mLocationFudger.getOrCreate(noGPSLocation));
-                    }
-                } else {
-                    lastLocation = new Location(location);
-                }
-                // Don't report location access if there is no last location to deliver.
-                if (lastLocation != null) {
-                    if (!reportLocationAccessNoThrow(pid, uid, packageName, featureId,
-                            allowedResolutionLevel, null)) {
-                        if (D) {
-                            Log.d(TAG, "not returning last loc for no op app: " + packageName);
-                        }
-                        lastLocation = null;
-                    }
-                }
-                return lastLocation;
-            } finally {
-                Binder.restoreCallingIdentity(identity);
+        int allowedResolutionLevel = getCallerAllowedResolutionLevel();
+        if (!reportLocationAccessNoThrow(Binder.getCallingPid(), Binder.getCallingUid(),
+                packageName, featureId, allowedResolutionLevel, null)) {
+            if (D) {
+                Log.d(TAG, "not returning last loc for no op app: " + packageName);
             }
+            return null;
+        }
+
+        int userId = UserHandle.getCallingUserId();
+
+        if (mSettingsHelper.isLocationPackageBlacklisted(userId, packageName)) {
+            return null;
+        }
+
+        if (!mUserInfoHelper.isCurrentUserId(userId)) {
+            return null;
+        }
+
+        synchronized (mLock) {
+            LocationProviderManager manager = getLocationProviderManager(request.getProvider());
+            if (manager == null) {
+                return null;
+            }
+
+            if (!manager.isEnabled(userId) && !request.isLocationSettingsIgnored()) {
+                return null;
+            }
+
+            Location location;
+            if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
+                location = manager.getLastCoarseLocation(userId);
+            } else {
+                location = manager.getLastFineLocation(userId);
+            }
+            if (location == null) {
+                return null;
+            }
+
+            // Don't return stale location to apps with foreground-only location permission.
+            String op = resolutionLevelToOpStr(allowedResolutionLevel);
+            long locationAgeMs = NANOSECONDS.toMillis(
+                    SystemClock.elapsedRealtime() - location.getElapsedRealtimeNanos());
+            if (locationAgeMs > mSettingsHelper.getMaxLastLocationAgeMs()
+                    && (mAppOps.unsafeCheckOp(op, Binder.getCallingUid(), packageName)
+                    == AppOpsManager.MODE_FOREGROUND)) {
+                return null;
+            }
+
+            // make a defensive copy - the client could be in the same process as us
+            return new Location(location);
         }
     }
 
@@ -2124,7 +2214,7 @@
         // side effect of validating locationRequest and packageName
         Location lastLocation = getLastLocation(locationRequest, packageName, featureId);
         if (lastLocation != null) {
-            long locationAgeMs = TimeUnit.NANOSECONDS.toMillis(
+            long locationAgeMs = NANOSECONDS.toMillis(
                     SystemClock.elapsedRealtimeNanos() - lastLocation.getElapsedRealtimeNanos());
 
             if (locationAgeMs < MAX_CURRENT_LOCATION_AGE_MS) {
@@ -2159,38 +2249,36 @@
     @Override
     public LocationTime getGnssTimeMillis() {
         synchronized (mLock) {
-            Location location = mLastLocation.get(LocationManager.GPS_PROVIDER);
+            LocationProviderManager gpsManager = getLocationProviderManager(GPS_PROVIDER);
+            if (gpsManager == null) {
+                return null;
+            }
+
+            Location location = gpsManager.getLastFineLocation(UserHandle.getCallingUserId());
             if (location == null) {
                 return null;
             }
+
             long currentNanos = SystemClock.elapsedRealtimeNanos();
-            long deltaMs = (currentNanos - location.getElapsedRealtimeNanos()) / 1000000L;
+            long deltaMs = NANOSECONDS.toMillis(
+                    currentNanos - location.getElapsedRealtimeNanos());
             return new LocationTime(location.getTime() + deltaMs, currentNanos);
         }
     }
 
     @Override
-    public boolean injectLocation(Location location) {
+    public void injectLocation(Location location) {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, null);
         mContext.enforceCallingPermission(ACCESS_FINE_LOCATION, null);
 
         Preconditions.checkArgument(location.isComplete());
 
+        int userId = UserHandle.getCallingUserId();
         synchronized (mLock) {
             LocationProviderManager manager = getLocationProviderManager(location.getProvider());
-            if (manager == null || !manager.isEnabled()) {
-                return false;
+            if (manager != null && manager.isEnabled(userId)) {
+                manager.injectLastLocation(Objects.requireNonNull(location), userId);
             }
-
-            // NOTE: If last location is already available, location is not injected.  If
-            // provider's normal source (like a GPS chipset) have already provided an output
-            // there is no need to inject this location.
-            if (mLastLocation.get(manager.getName()) != null) {
-                return false;
-            }
-
-            updateLastLocationLocked(location, manager.getName());
-            return true;
         }
     }
 
@@ -2287,12 +2375,14 @@
     }
 
     @Override
-    public boolean addGnssMeasurementsListener(IGnssMeasurementsListener listener,
-            String packageName, String featureId, String listenerIdentifier) {
+    public boolean addGnssMeasurementsListener(@Nullable GnssRequest request,
+            IGnssMeasurementsListener listener,
+            String packageName, String featureId,
+            String listenerIdentifier) {
         Objects.requireNonNull(listenerIdentifier);
 
         return mGnssManagerService != null && mGnssManagerService.addGnssMeasurementsListener(
-                listener, packageName, featureId, listenerIdentifier);
+                request, listener, packageName, featureId, listenerIdentifier);
     }
 
     @Override
@@ -2319,6 +2409,22 @@
     }
 
     @Override
+    public boolean addGnssAntennaInfoListener(IGnssAntennaInfoListener listener,
+            String packageName, String featureId, String listenerIdentifier) {
+        Objects.requireNonNull(listenerIdentifier);
+
+        return mGnssManagerService != null && mGnssManagerService.addGnssAntennaInfoListener(
+                listener, packageName, featureId, listenerIdentifier);
+    }
+
+    @Override
+    public void removeGnssAntennaInfoListener(IGnssAntennaInfoListener listener) {
+        if (mGnssManagerService != null) {
+            mGnssManagerService.removeGnssAntennaInfoListener(listener);
+        }
+    }
+
+    @Override
     public boolean addGnssNavigationMessageListener(IGnssNavigationMessageListener listener,
             String packageName, String featureId, String listenerIdentifier) {
         Objects.requireNonNull(listenerIdentifier);
@@ -2419,6 +2525,17 @@
     }
 
     @Override
+    public void setLocationEnabledForUser(boolean enabled, int userId) {
+        if (UserHandle.getCallingUserId() != userId) {
+            mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS,
+                    null);
+        }
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS,
+                "Requires WRITE_SECURE_SETTINGS permission");
+        mSettingsHelper.setLocationEnabled(enabled, userId);
+    }
+
+    @Override
     public boolean isLocationEnabledForUser(int userId) {
         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                 userId, false, false, "isLocationEnabledForUser", null);
@@ -2426,18 +2543,15 @@
     }
 
     @Override
-    public boolean isProviderEnabledForUser(String providerName, int userId) {
+    public boolean isProviderEnabledForUser(String provider, int userId) {
         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                 userId, false, false, "isProviderEnabledForUser", null);
 
         // Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
         // so we discourage its use
-        if (FUSED_PROVIDER.equals(providerName)) return false;
+        if (FUSED_PROVIDER.equals(provider)) return false;
 
-        synchronized (mLock) {
-            LocationProviderManager manager = getLocationProviderManager(providerName);
-            return manager != null && manager.isEnabled(userId);
-        }
+        return mLocalService.isProviderEnabledForUser(provider, userId);
     }
 
     @GuardedBy("mLock")
@@ -2450,7 +2564,7 @@
 
         // Check whether sufficient time has passed
         long minTime = record.mRealRequest.getFastestInterval();
-        long deltaMs = TimeUnit.NANOSECONDS.toMillis(
+        long deltaMs = NANOSECONDS.toMillis(
                 loc.getElapsedRealtimeNanos() - lastLoc.getElapsedRealtimeNanos());
         if (deltaMs < minTime - MAX_PROVIDER_SCHEDULING_JITTER_MS) {
             return false;
@@ -2474,62 +2588,23 @@
     }
 
     @GuardedBy("mLock")
-    private void handleLocationChangedLocked(Location location, LocationProviderManager manager) {
+    private void handleLocationChangedLocked(LocationProviderManager manager, Location location,
+            Location coarseLocation) {
         if (!mProviderManagers.contains(manager)) {
             Log.w(TAG, "received location from unknown provider: " + manager.getName());
             return;
         }
-        if (!location.isComplete()) {
-            Log.w(TAG, "dropping incomplete location from " + manager.getName() + " provider: "
-                    + location);
-            return;
-        }
 
         // notify passive provider
         if (manager != mPassiveManager) {
-            mPassiveManager.updateLocation(new Location(location));
+            mPassiveManager.updateLocation(location);
         }
 
-        if (D) Log.d(TAG, "incoming location: " + location);
         long now = SystemClock.elapsedRealtime();
 
-
-        // only update last location for locations that come from enabled providers
-        if (manager.isEnabled()) {
-            updateLastLocationLocked(location, manager.getName());
-        }
-
-        // Update last known coarse interval location if enough time has passed.
-        Location lastLocationCoarseInterval = mLastLocationCoarseInterval.get(
-                manager.getName());
-        if (lastLocationCoarseInterval == null) {
-            lastLocationCoarseInterval = new Location(location);
-
-            if (manager.isEnabled()) {
-                mLastLocationCoarseInterval.put(manager.getName(), lastLocationCoarseInterval);
-            }
-        }
-        long timeDeltaMs = TimeUnit.NANOSECONDS.toMillis(location.getElapsedRealtimeNanos()
-                - lastLocationCoarseInterval.getElapsedRealtimeNanos());
-        if (timeDeltaMs > LocationFudger.FASTEST_INTERVAL_MS) {
-            lastLocationCoarseInterval.set(location);
-        }
-        // Don't ever return a coarse location that is more recent than the allowed update
-        // interval (i.e. don't allow an app to keep registering and unregistering for
-        // location updates to overcome the minimum interval).
-        Location noGPSLocation =
-                lastLocationCoarseInterval.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
-
-        // Skip if there are no UpdateRecords for this provider.
         ArrayList<UpdateRecord> records = mRecordsByProvider.get(manager.getName());
         if (records == null || records.size() == 0) return;
 
-        // Fetch coarse location
-        Location coarseLocation = null;
-        if (noGPSLocation != null) {
-            coarseLocation = mLocationFudger.getOrCreate(noGPSLocation);
-        }
-
         ArrayList<Receiver> deadReceivers = null;
         ArrayList<UpdateRecord> deadUpdateRecords = null;
 
@@ -2537,22 +2612,23 @@
         for (UpdateRecord r : records) {
             Receiver receiver = r.mReceiver;
             boolean receiverDead = false;
+            int userId = UserHandle.getUserId(receiver.mCallerIdentity.mUid);
 
-            if (!manager.isEnabled() && !isSettingsExempt(r)) {
+
+            if (!manager.isEnabled(userId) && !isSettingsExempt(r)) {
                 continue;
             }
 
-            int receiverUserId = UserHandle.getUserId(receiver.mCallerIdentity.mUid);
-            if (!mUserInfoHelper.isCurrentUserOrProfile(receiverUserId)
+            if (!mUserInfoHelper.isCurrentUserId(userId)
                     && !isProviderPackage(receiver.mCallerIdentity.mPackageName)) {
                 if (D) {
-                    Log.d(TAG, "skipping loc update for background user " + receiverUserId +
-                            " (app: " + receiver.mCallerIdentity.mPackageName + ")");
+                    Log.d(TAG, "skipping loc update for background user " + userId
+                            + " (app: " + receiver.mCallerIdentity.mPackageName + ")");
                 }
                 continue;
             }
 
-            if (mSettingsHelper.isLocationPackageBlacklisted(receiverUserId,
+            if (mSettingsHelper.isLocationPackageBlacklisted(userId,
                     receiver.mCallerIdentity.mPackageName)) {
                 if (D) {
                     Log.d(TAG, "skipping loc update for blacklisted app: " +
@@ -2567,39 +2643,30 @@
             } else {
                 notifyLocation = location;  // use fine location
             }
-            if (notifyLocation != null) {
-                Location lastLoc = r.mLastFixBroadcast;
-                if ((lastLoc == null)
-                        || shouldBroadcastSafeLocked(notifyLocation, lastLoc, r, now)) {
-                    if (lastLoc == null) {
-                        lastLoc = new Location(notifyLocation);
-                        r.mLastFixBroadcast = lastLoc;
-                    } else {
-                        lastLoc.set(notifyLocation);
+            if (shouldBroadcastSafeLocked(notifyLocation, r.mLastFixBroadcast, r, now)) {
+                r.mLastFixBroadcast = notifyLocation;
+                // Report location access before delivering location to the client. This will
+                // note location delivery to appOps, so it should be called only when a
+                // location is really being delivered to the client.
+                if (!reportLocationAccessNoThrow(
+                        receiver.mCallerIdentity.mPid,
+                        receiver.mCallerIdentity.mUid,
+                        receiver.mCallerIdentity.mPackageName,
+                        receiver.mCallerIdentity.mFeatureId,
+                        receiver.mAllowedResolutionLevel,
+                        "Location sent to " + receiver.mCallerIdentity.mListenerIdentifier)) {
+                    if (D) {
+                        Log.d(TAG, "skipping loc update for no op app: "
+                                + receiver.mCallerIdentity.mPackageName);
                     }
-                    // Report location access before delivering location to the client. This will
-                    // note location delivery to appOps, so it should be called only when a
-                    // location is really being delivered to the client.
-                    if (!reportLocationAccessNoThrow(
-                            receiver.mCallerIdentity.mPid,
-                            receiver.mCallerIdentity.mUid,
-                            receiver.mCallerIdentity.mPackageName,
-                            receiver.mCallerIdentity.mFeatureId,
-                            receiver.mAllowedResolutionLevel,
-                            "Location sent to " + receiver.mCallerIdentity.mListenerIdentifier)) {
-                        if (D) {
-                            Log.d(TAG, "skipping loc update for no op app: "
-                                    + receiver.mCallerIdentity.mPackageName);
-                        }
-                        continue;
-                    }
-                    if (!receiver.callLocationChangedLocked(notifyLocation)) {
-                        Slog.w(TAG, "RemoteException calling onLocationChanged on "
-                                + receiver);
-                        receiverDead = true;
-                    }
-                    r.mRealRequest.decrementNumUpdates();
+                    continue;
                 }
+                if (!receiver.callLocationChangedLocked(notifyLocation)) {
+                    Log.w(TAG, "RemoteException calling onLocationChanged on "
+                            + receiver);
+                    receiverDead = true;
+                }
+                r.mRealRequest.decrementNumUpdates();
             }
 
             // track expired records
@@ -2636,30 +2703,6 @@
         }
     }
 
-    @GuardedBy("mLock")
-    private void updateLastLocationLocked(Location location, String provider) {
-        Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
-        Location lastNoGPSLocation;
-        Location lastLocation = mLastLocation.get(provider);
-        if (lastLocation == null) {
-            lastLocation = new Location(provider);
-            mLastLocation.put(provider, lastLocation);
-        } else {
-            lastNoGPSLocation = lastLocation.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
-            if (noGPSLocation == null && lastNoGPSLocation != null) {
-                // New location has no no-GPS location: adopt last no-GPS location. This is set
-                // directly into location because we do not want to notify COARSE clients.
-                Bundle extras = location.getExtras();
-                if (extras == null) {
-                    extras = new Bundle();
-                }
-                extras.putParcelable(Location.EXTRA_NO_GPS_LOCATION, lastNoGPSLocation);
-                location.setExtras(extras);
-            }
-        }
-        lastLocation.set(location);
-    }
-
     // Geocoder
 
     @Override
@@ -2729,8 +2772,6 @@
             manager.setMockProvider(null);
             if (!manager.hasProvider()) {
                 mProviderManagers.remove(manager);
-                mLastLocation.remove(manager.getName());
-                mLastLocationCoarseInterval.remove(manager.getName());
             }
         }
     }
@@ -2840,8 +2881,8 @@
 
             ipw.println("Historical Records by Provider:");
             ipw.increaseIndent();
-            TreeMap<PackageProviderKey, PackageStatistics> sorted = new TreeMap<>();
-            sorted.putAll(mRequestStatistics.statistics);
+            TreeMap<PackageProviderKey, PackageStatistics> sorted = new TreeMap<>(
+                    mRequestStatistics.statistics);
             for (Map.Entry<PackageProviderKey, PackageStatistics> entry
                     : sorted.entrySet()) {
                 PackageProviderKey key = entry.getKey();
@@ -2851,20 +2892,6 @@
 
             mRequestStatistics.history.dump(ipw);
 
-            ipw.println("Last Known Locations:");
-            ipw.increaseIndent();
-            for (Map.Entry<String, Location> entry : mLastLocation.entrySet()) {
-                ipw.println(entry.getKey() + ": " + entry.getValue());
-            }
-            ipw.decreaseIndent();
-
-            ipw.println("Last Known Coarse Locations:");
-            ipw.increaseIndent();
-            for (Map.Entry<String, Location> entry : mLastLocationCoarseInterval.entrySet()) {
-                ipw.println(entry.getKey() + ": " + entry.getValue());
-            }
-            ipw.decreaseIndent();
-
             if (mGeofenceManager != null) {
                 ipw.println("Geofences:");
                 ipw.increaseIndent();
@@ -2876,21 +2903,16 @@
                 ipw.println("Location Controller Extra Package: " + mExtraLocationControllerPackage
                         + (mExtraLocationControllerPackageEnabled ? " [enabled]" : "[disabled]"));
             }
+        }
 
-            if (mLocationFudger != null) {
-                ipw.println("Location Fudger:");
-                ipw.increaseIndent();
-                mLocationFudger.dump(fd, ipw, args);
-                ipw.decreaseIndent();
-            }
+        ipw.println("Location Providers:");
+        ipw.increaseIndent();
+        for (LocationProviderManager manager : mProviderManagers) {
+            manager.dump(fd, ipw, args);
+        }
+        ipw.decreaseIndent();
 
-            ipw.println("Location Providers:");
-            ipw.increaseIndent();
-            for (LocationProviderManager manager : mProviderManagers) {
-                manager.dump(fd, ipw, args);
-            }
-            ipw.decreaseIndent();
-
+        synchronized (mLock) {
             if (mGnssManagerService != null) {
                 ipw.println("GNSS:");
                 ipw.increaseIndent();
@@ -2915,6 +2937,18 @@
         }
 
         @Override
+        public boolean isProviderEnabledForUser(@NonNull String provider, int userId) {
+            synchronized (mLock) {
+                LocationProviderManager manager = getLocationProviderManager(provider);
+                if (manager == null) {
+                    return false;
+                }
+
+                return manager.isEnabled(userId);
+            }
+        }
+
+        @Override
         public boolean isProviderPackage(String packageName) {
             for (LocationProviderManager manager : mProviderManagers) {
                 if (manager.getPackages().contains(packageName)) {
diff --git a/services/core/java/com/android/server/LocationManagerServiceUtils.java b/services/core/java/com/android/server/LocationManagerServiceUtils.java
index 372e91e..ba1c81c 100644
--- a/services/core/java/com/android/server/LocationManagerServiceUtils.java
+++ b/services/core/java/com/android/server/LocationManagerServiceUtils.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
@@ -37,22 +38,31 @@
     /**
      * Listener that can be linked to a binder.
      * @param <TListener> listener type
+     * @param <TRequest> request type
      */
-    public static class LinkedListener<TListener> extends
+    public static class LinkedListener<TRequest, TListener> extends
             LinkedListenerBase {
+        @Nullable protected final TRequest mRequest;
         private final TListener mListener;
         private final Consumer<TListener> mBinderDeathCallback;
 
         public LinkedListener(
+                @Nullable TRequest request,
                 @NonNull TListener listener,
                 String listenerName,
                 @NonNull CallerIdentity callerIdentity,
                 @NonNull Consumer<TListener> binderDeathCallback) {
             super(callerIdentity, listenerName);
             mListener = listener;
+            mRequest = request;
             mBinderDeathCallback = binderDeathCallback;
         }
 
+        @Nullable
+        public TRequest getRequest() {
+            return mRequest;
+        }
+
         @Override
         public void binderDied() {
             if (D) Log.d(TAG, "Remote " + mListenerName + " died.");
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index a08bdb2..c9ba988 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -224,6 +224,7 @@
      * disables FuseDaemon. If {@code 0}, uses the default value from the build system.
      */
     private static final String FUSE_ENABLED = "fuse_enabled";
+    private static final boolean DEFAULT_FUSE_ENABLED = true;
 
     public static class Lifecycle extends SystemService {
         private StorageManagerService mStorageManagerService;
@@ -342,9 +343,43 @@
      */
     private final Object mPackagesLock = new Object();
 
+    /**
+     * mLocalUnlockedUsers affects the return value of isUserUnlocked.  If
+     * any value in the array changes, then the binder cache for
+     * isUserUnlocked must be invalidated.  When adding mutating methods to
+     * WatchedLockedUsers, be sure to invalidate the cache in the new
+     * methods.
+     */
+    private class WatchedLockedUsers {
+        private int[] users = EmptyArray.INT;
+        public WatchedLockedUsers() {
+        }
+        public void append(int userId) {
+            users = ArrayUtils.appendInt(users, userId);
+            invalidateIsUserUnlockedCache();
+        }
+        public void remove(int userId) {
+            users = ArrayUtils.removeInt(users, userId);
+            invalidateIsUserUnlockedCache();
+        }
+        public boolean contains(int userId) {
+            return ArrayUtils.contains(users, userId);
+        }
+        public int[] all() {
+            return users;
+        }
+        @Override
+        public String toString() {
+            return Arrays.toString(users);
+        }
+        private void invalidateIsUserUnlockedCache() {
+            UserManager.invalidateIsUserUnlockedCache();
+        }
+    }
+
     /** Set of users that we know are unlocked. */
     @GuardedBy("mLock")
-    private int[] mLocalUnlockedUsers = EmptyArray.INT;
+    private WatchedLockedUsers mLocalUnlockedUsers = new WatchedLockedUsers();
     /** Set of users that system knows are unlocked. */
     @GuardedBy("mLock")
     private int[] mSystemUnlockedUsers = EmptyArray.INT;
@@ -1623,7 +1658,7 @@
         // If there is no value in the property yet (first boot after data wipe), this value may be
         // incorrect until #updateFusePropFromSettings where we set the correct value and reboot if
         // different
-        mIsFuseEnabled = SystemProperties.getBoolean(PROP_FUSE, false);
+        mIsFuseEnabled = SystemProperties.getBoolean(PROP_FUSE, DEFAULT_FUSE_ENABLED);
         mContext = context;
         mResolver = mContext.getContentResolver();
         mCallbacks = new Callbacks(FgThread.get().getLooper());
@@ -1686,23 +1721,17 @@
      *  and updates PROP_FUSE (reboots if changed).
      */
     private void updateFusePropFromSettings() {
-        boolean defaultFuseFlag = false;
-        boolean settingsFuseFlag = SystemProperties.getBoolean(PROP_SETTINGS_FUSE, defaultFuseFlag);
-        Slog.d(TAG, "FUSE flags. Settings: " + settingsFuseFlag + ". Default: " + defaultFuseFlag);
-
-        if (TextUtils.isEmpty(SystemProperties.get(PROP_SETTINGS_FUSE))) {
-            // Set default value of PROP_SETTINGS_FUSE and PROP_FUSE if it
-            // is unset (neither true nor false).
-            // This happens only on the first boot after wiping data partition
-            SystemProperties.set(PROP_SETTINGS_FUSE, Boolean.toString(defaultFuseFlag));
-            SystemProperties.set(PROP_FUSE, Boolean.toString(defaultFuseFlag));
-            return;
-        }
+        boolean settingsFuseFlag = SystemProperties.getBoolean(PROP_SETTINGS_FUSE,
+                DEFAULT_FUSE_ENABLED);
+        Slog.d(TAG, "FUSE flags. Settings: " + settingsFuseFlag
+                + ". Default: " + DEFAULT_FUSE_ENABLED);
 
         if (mIsFuseEnabled != settingsFuseFlag) {
             Slog.i(TAG, "Toggling persist.sys.fuse to " + settingsFuseFlag);
+            // Set prop_fuse to match prop_settings_fuse because it is used by native daemons like
+            // init, zygote, installd and vold
             SystemProperties.set(PROP_FUSE, Boolean.toString(settingsFuseFlag));
-            // Perform hard reboot to kick policy into place
+            // Then perform hard reboot to kick policy into place
             mContext.getSystemService(PowerManager.class).reboot("fuse_prop");
         }
     }
@@ -3042,7 +3071,7 @@
         }
 
         synchronized (mLock) {
-            mLocalUnlockedUsers = ArrayUtils.appendInt(mLocalUnlockedUsers, userId);
+            mLocalUnlockedUsers.append(userId);
         }
     }
 
@@ -3058,14 +3087,14 @@
         }
 
         synchronized (mLock) {
-            mLocalUnlockedUsers = ArrayUtils.removeInt(mLocalUnlockedUsers, userId);
+            mLocalUnlockedUsers.remove(userId);
         }
     }
 
     @Override
     public boolean isUserKeyUnlocked(int userId) {
         synchronized (mLock) {
-            return ArrayUtils.contains(mLocalUnlockedUsers, userId);
+            return mLocalUnlockedUsers.contains(userId);
         }
     }
 
@@ -4177,7 +4206,7 @@
             }
 
             pw.println();
-            pw.println("Local unlocked users: " + Arrays.toString(mLocalUnlockedUsers));
+            pw.println("Local unlocked users: " + mLocalUnlockedUsers);
             pw.println("System unlocked users: " + Arrays.toString(mSystemUnlockedUsers));
 
             final ContentResolver cr = mContext.getContentResolver();
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 0eedf8a..6763c51 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -302,15 +302,19 @@
     private final ContentObserver mDarkThemeObserver = new ContentObserver(mHandler) {
         @Override
         public void onChange(boolean selfChange, Uri uri) {
-            int mode = Secure.getIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE,
-                    mNightMode, 0);
-            if (mode == MODE_NIGHT_AUTO || mode == MODE_NIGHT_CUSTOM) {
-                mode = MODE_NIGHT_YES;
-            }
-            SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME, Integer.toString(mode));
+            updateSystemProperties();
         }
     };
 
+    private void updateSystemProperties() {
+        int mode = Secure.getIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE,
+                mNightMode, 0);
+        if (mode == MODE_NIGHT_AUTO || mode == MODE_NIGHT_CUSTOM) {
+            mode = MODE_NIGHT_YES;
+        }
+        SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME, Integer.toString(mode));
+    }
+
     @Override
     public void onSwitchUser(int userHandle) {
         super.onSwitchUser(userHandle);
@@ -392,6 +396,7 @@
 
         context.getContentResolver().registerContentObserver(Secure.getUriFor(Secure.UI_NIGHT_MODE),
                 false, mDarkThemeObserver, 0);
+        updateSystemProperties();
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index f7d7d6c..cdde67d 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -42,11 +42,13 @@
 import android.os.IBinder;
 import android.os.IExternalVibratorService;
 import android.os.IVibratorService;
+import android.os.IVibratorStateListener;
 import android.os.PowerManager;
 import android.os.PowerManager.ServiceType;
 import android.os.PowerManagerInternal;
 import android.os.PowerSaveState;
 import android.os.Process;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
@@ -166,7 +168,11 @@
     private ExternalVibration mCurrentExternalVibration;
     private boolean mVibratorUnderExternalControl;
     private boolean mLowPowerMode;
+    @GuardedBy("mLock")
     private boolean mIsVibrating;
+    @GuardedBy("mLock")
+    private final RemoteCallbackList<IVibratorStateListener> mVibratorStateListeners =
+                new RemoteCallbackList<>();
     private int mHapticFeedbackIntensity;
     private int mNotificationIntensity;
     private int mRingIntensity;
@@ -522,6 +528,75 @@
     }
 
     @Override // Binder call
+    public boolean isVibrating() {
+        if (!hasPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)) {
+            throw new SecurityException("Requires ACCESS_VIBRATOR_STATE permission");
+        }
+        synchronized (mLock) {
+            return mIsVibrating;
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void notifyStateListenerLocked(IVibratorStateListener listener) {
+        try {
+            listener.onVibrating(mIsVibrating);
+        } catch (RemoteException | RuntimeException e) {
+            Slog.e(TAG, "Vibrator callback failed to call", e);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void notifyStateListenersLocked() {
+        final int length = mVibratorStateListeners.beginBroadcast();
+        try {
+            for (int i = 0; i < length; i++) {
+                final IVibratorStateListener listener =
+                        mVibratorStateListeners.getBroadcastItem(i);
+                notifyStateListenerLocked(listener);
+            }
+        } finally {
+            mVibratorStateListeners.finishBroadcast();
+        }
+    }
+
+    @Override // Binder call
+    public boolean registerVibratorStateListener(IVibratorStateListener listener) {
+        if (!hasPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)) {
+            throw new SecurityException("Requires ACCESS_VIBRATOR_STATE permission");
+        }
+        synchronized (mLock) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if (!mVibratorStateListeners.register(listener)) {
+                    return false;
+                }
+                // Notify its callback after new client registered.
+                notifyStateListenerLocked(listener);
+                return true;
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    }
+
+    @Override // Binder call
+    @GuardedBy("mLock")
+    public boolean unregisterVibratorStateListener(IVibratorStateListener listener) {
+        if (!hasPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)) {
+            throw new SecurityException("Requires ACCESS_VIBRATOR_STATE permission");
+        }
+        synchronized (mLock) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                return mVibratorStateListeners.unregister(listener);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    }
+
+    @Override // Binder call
     public boolean hasAmplitudeControl() {
         synchronized (mInputDeviceVibrators) {
             // Input device vibrators don't support amplitude controls yet, but are still used over
@@ -1373,7 +1448,10 @@
             FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED, uid, null,
                     FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__ON, millis);
             mCurVibUid = uid;
-            mIsVibrating = true;
+            if (!mIsVibrating) {
+                mIsVibrating = true;
+                notifyStateListenersLocked();
+            }
         } catch (RemoteException e) {
         }
     }
@@ -1387,7 +1465,10 @@
             } catch (RemoteException e) { }
             mCurVibUid = -1;
         }
-        mIsVibrating = false;
+        if (mIsVibrating) {
+            mIsVibrating = false;
+            notifyStateListenersLocked();
+        }
     }
 
     private void setVibratorUnderExternalControl(boolean externalControl) {
@@ -1414,6 +1495,8 @@
             pw.print("  mCurrentExternalVibration=" + mCurrentExternalVibration);
             pw.println("  mVibratorUnderExternalControl=" + mVibratorUnderExternalControl);
             pw.println("  mIsVibrating=" + mIsVibrating);
+            pw.println("  mVibratorStateListeners Count=" +
+                            mVibratorStateListeners.getRegisteredCallbackCount());
             pw.println("  mLowPowerMode=" + mLowPowerMode);
             pw.println("  mHapticFeedbackIntensity=" + mHapticFeedbackIntensity);
             pw.println("  mNotificationIntensity=" + mNotificationIntensity);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 46a20c9..0852458 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -466,9 +466,18 @@
     // How long we wait for a launched process to attach to the activity manager
     // before we decide it's never going to come up for real.
     static final int PROC_START_TIMEOUT = 10*1000;
+    // How long we wait for an attached process to publish its content providers
+    // before we decide it must be hung.
+    static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;
+
     // How long we wait to kill an application zygote, after the last process using
     // it has gone away.
     static final int KILL_APP_ZYGOTE_DELAY_MS = 5 * 1000;
+    /**
+     * How long we wait for an provider to be published. Should be longer than
+     * {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT}.
+     */
+    static final int CONTENT_PROVIDER_WAIT_TIMEOUT = 20 * 1000;
 
     // How long we wait for a launched process to attach to the activity manager
     // before we decide it's never going to come up for real, when the process was
@@ -4959,8 +4968,7 @@
         if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
             Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
             msg.obj = app;
-            mHandler.sendMessageDelayed(msg,
-                    ContentResolver.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS);
+            mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
         }
 
         checkTime(startTime, "attachApplicationLocked: before bindApplication");
@@ -7239,8 +7247,7 @@
         }
 
         // Wait for the provider to be published...
-        final long timeout =
-                SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS;
+        final long timeout = SystemClock.uptimeMillis() + CONTENT_PROVIDER_WAIT_TIMEOUT;
         boolean timedOut = false;
         synchronized (cpr) {
             while (cpr.provider == null) {
@@ -7277,14 +7284,12 @@
             }
         }
         if (timedOut) {
-            // Note we do it after releasing the lock.
+            // Note we do it afer releasing the lock.
             String callerName = "unknown";
-            if (caller != null) {
-                synchronized (this) {
-                    final ProcessRecord record = mProcessList.getLRURecordForAppLocked(caller);
-                    if (record != null) {
-                        callerName = record.processName;
-                    }
+            synchronized (this) {
+                final ProcessRecord record = mProcessList.getLRURecordForAppLocked(caller);
+                if (record != null) {
+                    callerName = record.processName;
                 }
             }
 
@@ -19465,6 +19470,13 @@
     }
 
     /**
+     * Force the settings cache to be loaded
+     */
+    void refreshSettingsCache() {
+        mCoreSettingsObserver.onChange(true);
+    }
+
+    /**
      * Kill processes for the user with id userId and that depend on the package named packageName
      */
     @Override
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index c0e98cd..c7f5f63 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -306,6 +306,8 @@
                     return runWaitForBroadcastIdle(pw);
                 case "compat":
                     return runCompat(pw);
+                case "refresh-settings-cache":
+                    return runRefreshSettingsCache();
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -2941,6 +2943,11 @@
         return 0;
     }
 
+    int runRefreshSettingsCache() throws RemoteException {
+        mInternal.refreshSettingsCache();
+        return 0;
+    }
+
     private int runCompat(PrintWriter pw) throws RemoteException {
         final PlatformCompat platformCompat = (PlatformCompat)
                 ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE);
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 145f91b..789f719 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -798,7 +798,6 @@
         boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
                 Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
 
-        final String packageName;
         final int userId;
         synchronized (mService) {
             final ProcessRecord proc = data.proc;
@@ -807,7 +806,6 @@
                 Slog.e(TAG, "handleShowAppErrorUi: proc is null");
                 return;
             }
-            packageName = proc.info.packageName;
             userId = proc.userId;
             if (proc.getDialogController().hasCrashDialogs()) {
                 Slog.e(TAG, "App already has crash dialog: " + proc);
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index dcada89..abe0dd5 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1577,7 +1577,19 @@
             // For DownloadProviders and MTP: To grant access to /sdcard/Android/
             // And a special case for the FUSE daemon since it runs an MTP server and should have
             // access to Android/
+            // Note that we must add in the user id, because sdcardfs synthesizes this permission
+            // based on the user
             gidList.add(UserHandle.getUid(UserHandle.getUserId(uid), Process.SDCARD_RW_GID));
+
+            // For devices without sdcardfs, these GIDs are needed instead; note that we
+            // consciously don't add the user_id in the GID, since these apps are anyway
+            // isolated to only their own user
+            gidList.add(Process.EXT_DATA_RW_GID);
+            gidList.add(Process.EXT_OBB_RW_GID);
+        }
+        if (mountExternal == Zygote.MOUNT_EXTERNAL_INSTALLER) {
+            // For devices without sdcardfs, this GID is needed to allow installers access to OBBs
+            gidList.add(Process.EXT_OBB_RW_GID);
         }
         if (mountExternal == Zygote.MOUNT_EXTERNAL_PASS_THROUGH) {
             // For the FUSE daemon: To grant access to the lower filesystem.
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index f3d8bc8..e63da9b 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1910,8 +1910,7 @@
             mWaitDialog = null;
         }
 
-        void forAllDialogs(List<? extends BaseErrorDialog> dialogs,
-                Consumer<BaseErrorDialog> c) {
+        void forAllDialogs(List<? extends BaseErrorDialog> dialogs, Consumer<BaseErrorDialog> c) {
             for (int i = dialogs.size() - 1; i >= 0; i--) {
                 c.accept(dialogs.get(i));
             }
@@ -1920,42 +1919,72 @@
         void showCrashDialogs(AppErrorDialog.Data data) {
             List<Context> contexts = getDisplayContexts(false /* lastUsedOnly */);
             mCrashDialogs = new ArrayList<>();
-
             for (int i = contexts.size() - 1; i >= 0; i--) {
                 final Context c = contexts.get(i);
                 mCrashDialogs.add(new AppErrorDialog(c, mService, data));
             }
-            mService.mUiHandler.post(() -> mCrashDialogs.forEach(Dialog::show));
+            mService.mUiHandler.post(() -> {
+                List<AppErrorDialog> dialogs;
+                synchronized (mService) {
+                    dialogs = mCrashDialogs;
+                }
+                if (dialogs != null) {
+                    forAllDialogs(dialogs, Dialog::show);
+                }
+            });
         }
 
         void showAnrDialogs(AppNotRespondingDialog.Data data) {
             List<Context> contexts = getDisplayContexts(isSilentAnr() /* lastUsedOnly */);
             mAnrDialogs = new ArrayList<>();
-
             for (int i = contexts.size() - 1; i >= 0; i--) {
                 final Context c = contexts.get(i);
                 mAnrDialogs.add(new AppNotRespondingDialog(mService, c, data));
             }
-            mService.mUiHandler.post(() -> mAnrDialogs.forEach(Dialog::show));
+            mService.mUiHandler.post(() -> {
+                List<AppNotRespondingDialog> dialogs;
+                synchronized (mService) {
+                    dialogs = mAnrDialogs;
+                }
+                if (dialogs != null) {
+                    forAllDialogs(dialogs, Dialog::show);
+                }
+            });
         }
 
         void showViolationDialogs(AppErrorResult res) {
             List<Context> contexts = getDisplayContexts(false /* lastUsedOnly */);
             mViolationDialogs = new ArrayList<>();
-
             for (int i = contexts.size() - 1; i >= 0; i--) {
                 final Context c = contexts.get(i);
                 mViolationDialogs.add(
                         new StrictModeViolationDialog(c, mService, res, ProcessRecord.this));
             }
-            mService.mUiHandler.post(() -> mViolationDialogs.forEach(Dialog::show));
+            mService.mUiHandler.post(() -> {
+                List<StrictModeViolationDialog> dialogs;
+                synchronized (mService) {
+                    dialogs = mViolationDialogs;
+                }
+                if (dialogs != null) {
+                    forAllDialogs(dialogs, Dialog::show);
+                }
+            });
         }
 
         void showDebugWaitingDialogs() {
             List<Context> contexts = getDisplayContexts(true /* lastUsedOnly */);
             final Context c = contexts.get(0);
             mWaitDialog = new AppWaitingForDebuggerDialog(mService, c, ProcessRecord.this);
-            mService.mUiHandler.post(() -> mWaitDialog.show());
+
+            mService.mUiHandler.post(() -> {
+                Dialog dialog;
+                synchronized (mService) {
+                    dialog = mWaitDialog;
+                }
+                if (dialog != null) {
+                    dialog.show();
+                }
+            });
         }
 
         /**
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index e575e10..88eb885 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -414,6 +414,7 @@
                 Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
                 intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
                 intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
+                        | Intent.FLAG_RECEIVER_OFFLOAD
                         | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
                 mInjector.broadcastIntent(intent, null, resultTo, 0, null, null,
                         new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index e17c1f8..566b72d 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -29,6 +29,7 @@
 import android.media.AudioRoutesInfo;
 import android.media.AudioSystem;
 import android.media.IAudioRoutesObserver;
+import android.media.IStrategyPreferredDeviceDispatcher;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -410,6 +411,16 @@
         return mDeviceInventory.removePreferredDeviceForStrategySync(strategy);
     }
 
+    /*package*/ void registerStrategyPreferredDeviceDispatcher(
+            @NonNull IStrategyPreferredDeviceDispatcher dispatcher) {
+        mDeviceInventory.registerStrategyPreferredDeviceDispatcher(dispatcher);
+    }
+
+    /*package*/ void unregisterStrategyPreferredDeviceDispatcher(
+            @NonNull IStrategyPreferredDeviceDispatcher dispatcher) {
+        mDeviceInventory.unregisterStrategyPreferredDeviceDispatcher(dispatcher);
+    }
+
     //---------------------------------------------------------------------
     // Communication with (to) AudioService
     //TODO check whether the AudioService methods are candidates to move here
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 1f998c3..e170db0 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -16,6 +16,7 @@
 package com.android.server.audio;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
@@ -31,6 +32,7 @@
 import android.media.AudioRoutesInfo;
 import android.media.AudioSystem;
 import android.media.IAudioRoutesObserver;
+import android.media.IStrategyPreferredDeviceDispatcher;
 import android.os.Binder;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
@@ -87,6 +89,10 @@
     final RemoteCallbackList<IAudioRoutesObserver> mRoutesObservers =
             new RemoteCallbackList<IAudioRoutesObserver>();
 
+    // Monitoring of strategy-preferred device
+    final RemoteCallbackList<IStrategyPreferredDeviceDispatcher> mPrefDevDispatchers =
+            new RemoteCallbackList<IStrategyPreferredDeviceDispatcher>();
+
     /*package*/ AudioDeviceInventory(@NonNull AudioDeviceBroker broker) {
         mDeviceBroker = broker;
         mAudioSystem = AudioSystemAdapter.getDefaultAdapter();
@@ -470,10 +476,12 @@
 
     /*package*/ void onSaveSetPreferredDevice(int strategy, @NonNull AudioDevice device) {
         mPreferredDevices.put(strategy, device);
+        dispatchPreferredDevice(strategy, device);
     }
 
     /*package*/ void onSaveRemovePreferredDevice(int strategy) {
         mPreferredDevices.remove(strategy);
+        dispatchPreferredDevice(strategy, null);
     }
 
     //------------------------------------------------------------
@@ -502,6 +510,16 @@
         return status;
     }
 
+    /*package*/ void registerStrategyPreferredDeviceDispatcher(
+            @NonNull IStrategyPreferredDeviceDispatcher dispatcher) {
+        mPrefDevDispatchers.register(dispatcher);
+    }
+
+    /*package*/ void unregisterStrategyPreferredDeviceDispatcher(
+            @NonNull IStrategyPreferredDeviceDispatcher dispatcher) {
+        mPrefDevDispatchers.unregister(dispatcher);
+    }
+
     /**
      * Implements the communication with AudioSystem to (dis)connect a device in the native layers
      * @param connect true if connection
@@ -702,6 +720,10 @@
                 delay = 0;
             }
             mDeviceBroker.postSetHearingAidConnectionState(state, device, delay);
+            if (state == BluetoothHearingAid.STATE_CONNECTED) {
+                mDeviceBroker.setForceUse_Async(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE,
+                                "HEARING_AID set to CONNECTED");
+            }
             return delay;
         }
     }
@@ -1090,6 +1112,17 @@
         }
     }
 
+    private void dispatchPreferredDevice(int strategy, @Nullable AudioDevice device) {
+        final int nbDispatchers = mPrefDevDispatchers.beginBroadcast();
+        for (int i = 0; i < nbDispatchers; i++) {
+            try {
+                mPrefDevDispatchers.getBroadcastItem(i).dispatchPrefDeviceChanged(strategy, device);
+            } catch (RemoteException e) {
+            }
+        }
+        mPrefDevDispatchers.finishBroadcast();
+    }
+
     //----------------------------------------------------------
     // For tests only
 
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 342ce22..97a8b87 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -21,7 +21,6 @@
 import static android.media.AudioManager.RINGER_MODE_SILENT;
 import static android.media.AudioManager.RINGER_MODE_VIBRATE;
 import static android.media.AudioManager.STREAM_SYSTEM;
-import static android.media.audiopolicy.AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE;
 import static android.os.Process.FIRST_APPLICATION_UID;
 import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE;
 import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
@@ -83,6 +82,7 @@
 import android.media.IPlaybackConfigDispatcher;
 import android.media.IRecordingConfigDispatcher;
 import android.media.IRingtonePlayer;
+import android.media.IStrategyPreferredDeviceDispatcher;
 import android.media.IVolumeController;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
@@ -90,7 +90,6 @@
 import android.media.VolumePolicy;
 import android.media.audiofx.AudioEffect;
 import android.media.audiopolicy.AudioMix;
-import android.media.audiopolicy.AudioMixingRule.AudioMixMatchCriterion;
 import android.media.audiopolicy.AudioPolicy;
 import android.media.audiopolicy.AudioPolicyConfig;
 import android.media.audiopolicy.AudioProductStrategy;
@@ -1764,6 +1763,26 @@
         }
     }
 
+    /** @see AudioManager#addOnPreferredDeviceForStrategyChangedListener(Executor, AudioManager.OnPreferredDeviceForStrategyChangedListener) */
+    public void registerStrategyPreferredDeviceDispatcher(
+            @Nullable IStrategyPreferredDeviceDispatcher dispatcher) {
+        if (dispatcher == null) {
+            return;
+        }
+        enforceModifyAudioRoutingPermission();
+        mDeviceBroker.registerStrategyPreferredDeviceDispatcher(dispatcher);
+    }
+
+    /** @see AudioManager#removeOnPreferredDeviceForStrategyChangedListener(AudioManager.OnPreferredDeviceForStrategyChangedListener) */
+    public void unregisterStrategyPreferredDeviceDispatcher(
+            @Nullable IStrategyPreferredDeviceDispatcher dispatcher) {
+        if (dispatcher == null) {
+            return;
+        }
+        enforceModifyAudioRoutingPermission();
+        mDeviceBroker.unregisterStrategyPreferredDeviceDispatcher(dispatcher);
+    }
+
     /** @see AudioManager#getDevicesForAttributes(AudioAttributes) */
     public @NonNull ArrayList<AudioDevice> getDevicesForAttributes(
             @NonNull AudioAttributes attributes) {
@@ -6805,8 +6824,9 @@
 
         boolean requireValidProjection = false;
         boolean requireCaptureAudioOrMediaOutputPerm = false;
-        boolean requireVoiceComunicationOutputPerm = false;
         boolean requireModifyRouting = false;
+        ArrayList<AudioMix> voiceCommunicationCaptureMixes = null;
+
 
         if (hasFocusAccess || isVolumeController) {
             requireModifyRouting |= true;
@@ -6815,23 +6835,29 @@
             requireModifyRouting |= true;
         }
         for (AudioMix mix : policyConfig.getMixes()) {
-            // If mix is trying to capture USAGE_VOICE_COMMUNICATION using playback capture
-            if (isVoiceCommunicationPlaybackCaptureMix(mix)) {
-                // then it must have CAPTURE_USAGE_VOICE_COMMUNICATION_OUTPUT permission
-                requireVoiceComunicationOutputPerm |= true;
-            }
-            // If mix is requesting privileged capture and is capturing at
-            // least one usage which is not USAGE_VOICE_COMMUNICATION.
-            if (mix.getRule().allowPrivilegedPlaybackCapture()
-                    && isNonVoiceCommunicationCaptureMix(mix)) {
+            // If mix is requesting privileged capture
+            if (mix.getRule().allowPrivilegedPlaybackCapture()) {
                 // then it must have CAPTURE_MEDIA_OUTPUT or CAPTURE_AUDIO_OUTPUT permission
                 requireCaptureAudioOrMediaOutputPerm |= true;
+
                 // and its format must be low quality enough
                 String error = mix.canBeUsedForPrivilegedCapture(mix.getFormat());
                 if (error != null) {
                     Log.e(TAG, error);
                     return false;
                 }
+
+                // If mix is trying to excplicitly capture USAGE_VOICE_COMMUNICATION
+                if (mix.containsMatchAttributeRuleForUsage(
+                        AudioAttributes.USAGE_VOICE_COMMUNICATION)) {
+                    // then it must have CAPTURE_USAGE_VOICE_COMMUNICATION_OUTPUT permission
+                    // Note that for UID, USERID or EXCLDUE rules, the capture will be silenced
+                    // in AudioPolicyMix
+                    if (voiceCommunicationCaptureMixes == null) {
+                        voiceCommunicationCaptureMixes = new ArrayList<AudioMix>();
+                    }
+                    voiceCommunicationCaptureMixes.add(mix);
+                }
             }
 
             // If mix is RENDER|LOOPBACK, then an audio MediaProjection is enough
@@ -6851,12 +6877,18 @@
             return false;
         }
 
-        if (requireVoiceComunicationOutputPerm
-                && !callerHasPermission(
-                        android.Manifest.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT)) {
-            Log.e(TAG, "Privileged audio capture for voice communication requires "
-                      + "CAPTURE_VOICE_COMMUNICATION_OUTPUT system permission");
-            return false;
+        if (voiceCommunicationCaptureMixes != null && voiceCommunicationCaptureMixes.size() > 0) {
+            if (!callerHasPermission(
+                    android.Manifest.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT)) {
+                Log.e(TAG, "Privileged audio capture for voice communication requires "
+                        + "CAPTURE_VOICE_COMMUNICATION_OUTPUT system permission");
+                return false;
+            }
+
+            // If permission check succeeded, we set the flag in each of the mixing rules
+            for (AudioMix mix : voiceCommunicationCaptureMixes) {
+                mix.getRule().setVoiceCommunicationCaptureAllowed(true);
+            }
         }
 
         if (requireValidProjection && !canProjectAudio(projection)) {
@@ -6872,41 +6904,6 @@
         return true;
     }
 
-    /**
-    * Checks whether a given AudioMix is used for playback capture
-    * (has the ROUTE_FLAG_LOOP_BACK_RENDER flag) and has a matching
-    * criterion for USAGE_VOICE_COMMUNICATION.
-    */
-    private boolean isVoiceCommunicationPlaybackCaptureMix(AudioMix mix) {
-        if (mix.getRouteFlags() != mix.ROUTE_FLAG_LOOP_BACK_RENDER) {
-            return false;
-        }
-
-        for (AudioMixMatchCriterion criterion : mix.getRule().getCriteria()) {
-            if (criterion.getRule() == RULE_MATCH_ATTRIBUTE_USAGE
-                    && criterion.getAudioAttributes().getUsage()
-                    == AudioAttributes.USAGE_VOICE_COMMUNICATION) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-    * Checks whether a given AudioMix has a matching
-    * criterion for a usage which is not USAGE_VOICE_COMMUNICATION.
-    */
-    private boolean isNonVoiceCommunicationCaptureMix(AudioMix mix) {
-        for (AudioMixMatchCriterion criterion : mix.getRule().getCriteria()) {
-            if (criterion.getRule() == RULE_MATCH_ATTRIBUTE_USAGE
-                    && criterion.getAudioAttributes().getUsage()
-                    != AudioAttributes.USAGE_VOICE_COMMUNICATION) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     private boolean callerHasPermission(String permission) {
         return mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED;
     }
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 07fc9b7..26c94c5 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -25,6 +25,7 @@
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
 import static android.hardware.biometrics.BiometricManager.Authenticators;
 
+import android.annotation.IntDef;
 import android.app.ActivityManager;
 import android.app.IActivityManager;
 import android.app.UserSwitchObserver;
@@ -83,6 +84,25 @@
     static final String TAG = "BiometricService";
     private static final boolean DEBUG = true;
 
+    private static final int BIOMETRIC_NO_HARDWARE = 0;
+    private static final int BIOMETRIC_OK = 1;
+    private static final int BIOMETRIC_DISABLED_BY_DEVICE_POLICY = 2;
+    private static final int BIOMETRIC_INSUFFICIENT_STRENGTH = 3;
+    private static final int BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE = 4;
+    private static final int BIOMETRIC_HARDWARE_NOT_DETECTED = 5;
+    private static final int BIOMETRIC_NOT_ENROLLED = 6;
+    private static final int BIOMETRIC_NOT_ENABLED_FOR_APPS = 7;
+
+    @IntDef({BIOMETRIC_NO_HARDWARE,
+            BIOMETRIC_OK,
+            BIOMETRIC_DISABLED_BY_DEVICE_POLICY,
+            BIOMETRIC_INSUFFICIENT_STRENGTH,
+            BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE,
+            BIOMETRIC_HARDWARE_NOT_DETECTED,
+            BIOMETRIC_NOT_ENROLLED,
+            BIOMETRIC_NOT_ENABLED_FOR_APPS})
+    @interface BiometricStatus {}
+
     private static final int MSG_ON_AUTHENTICATION_SUCCEEDED = 2;
     private static final int MSG_ON_AUTHENTICATION_REJECTED = 3;
     private static final int MSG_ON_ERROR = 4;
@@ -206,7 +226,7 @@
         }
 
         boolean isAllowDeviceCredential() {
-            return Utils.isDeviceCredentialAllowed(mBundle);
+            return Utils.isCredentialRequested(mBundle);
         }
     }
 
@@ -372,16 +392,20 @@
          * strength.
          * @return a bitfield, see {@link Authenticators}
          */
-        public int getActualStrength() {
+        int getActualStrength() {
             return OEMStrength | updatedStrength;
         }
 
+        boolean isDowngraded() {
+            return OEMStrength != updatedStrength;
+        }
+
         /**
          * Stores the updated strength, which takes effect whenever {@link #getActualStrength()}
          * is checked.
          * @param newStrength
          */
-        public void updateStrength(int newStrength) {
+        void updateStrength(int newStrength) {
             String log = "updateStrength: Before(" + toString() + ")";
             updatedStrength = newStrength;
             log += " After(" + toString() + ")";
@@ -1007,6 +1031,79 @@
         return isBiometricDisabled;
     }
 
+    private static int biometricStatusToBiometricConstant(@BiometricStatus int status) {
+        switch (status) {
+            case BIOMETRIC_NO_HARDWARE:
+                return BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT;
+            case BIOMETRIC_OK:
+                return BiometricConstants.BIOMETRIC_SUCCESS;
+            case BIOMETRIC_DISABLED_BY_DEVICE_POLICY:
+                return BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
+            case BIOMETRIC_INSUFFICIENT_STRENGTH:
+                return BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT;
+            case BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE:
+                return BiometricConstants.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED;
+            case BIOMETRIC_HARDWARE_NOT_DETECTED:
+                return BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
+            case BIOMETRIC_NOT_ENROLLED:
+                return BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS;
+            case BIOMETRIC_NOT_ENABLED_FOR_APPS:
+                return BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
+            default:
+                return BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
+        }
+    }
+
+    /**
+     * Returns the status of the authenticator, with errors returned in a specific priority order.
+     * For example, {@link #BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE} is only returned
+     * if it has enrollments, and is enabled for apps.
+     *
+     * We should only return the modality if the authenticator should be exposed. e.g.
+     * BIOMETRIC_NOT_ENROLLED_FOR_APPS should not expose the authenticator's type.
+     *
+     * @return A Pair with `first` being modality, and `second` being @BiometricStatus
+     */
+    private Pair<Integer, Integer> getStatusForBiometricAuthenticator(
+            AuthenticatorWrapper authenticator, int userId, String opPackageName,
+            boolean checkDevicePolicyManager, int requestedStrength) {
+        if (checkDevicePolicyManager) {
+            if (isBiometricDisabledByDevicePolicy(authenticator.modality, userId)) {
+                return new Pair<>(TYPE_NONE, BIOMETRIC_DISABLED_BY_DEVICE_POLICY);
+            }
+        }
+
+        final boolean wasStrongEnough =
+                Utils.isAtLeastStrength(authenticator.OEMStrength, requestedStrength);
+        final boolean isStrongEnough =
+                Utils.isAtLeastStrength(authenticator.getActualStrength(), requestedStrength);
+
+        if (wasStrongEnough && !isStrongEnough) {
+            return new Pair<>(authenticator.modality,
+                    BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE);
+        } else if (!wasStrongEnough) {
+            return new Pair<>(TYPE_NONE, BIOMETRIC_INSUFFICIENT_STRENGTH);
+        }
+
+        try {
+            if (!authenticator.impl.isHardwareDetected(opPackageName)) {
+                return new Pair<>(authenticator.modality, BIOMETRIC_HARDWARE_NOT_DETECTED);
+            }
+
+            if (!authenticator.impl.hasEnrolledTemplates(userId, opPackageName)) {
+                return new Pair<>(authenticator.modality, BIOMETRIC_NOT_ENROLLED);
+            }
+        } catch (RemoteException e) {
+            return new Pair<>(authenticator.modality, BIOMETRIC_HARDWARE_NOT_DETECTED);
+        }
+
+        if (!isEnabledForApp(authenticator.modality, userId)) {
+            return new Pair<>(TYPE_NONE, BIOMETRIC_NOT_ENABLED_FOR_APPS);
+        }
+
+        return new Pair<>(authenticator.modality, BIOMETRIC_OK);
+    }
+
     /**
      * Depending on the requested authentication (credential/biometric combination), checks their
      * availability.
@@ -1029,10 +1126,9 @@
     private Pair<Integer, Integer> checkAndGetAuthenticators(int userId, Bundle bundle,
             String opPackageName, boolean checkDevicePolicyManager) throws RemoteException {
 
-        final boolean biometricRequested = Utils.isBiometricAllowed(bundle);
-        final boolean credentialRequested = Utils.isDeviceCredentialAllowed(bundle);
+        final boolean biometricRequested = Utils.isBiometricRequested(bundle);
+        final boolean credentialRequested = Utils.isCredentialRequested(bundle);
 
-        final boolean biometricOk;
         final boolean credentialOk = mTrustManager.isDeviceSecure(userId);
 
         // Assuming that biometric authenticators are listed in priority-order, the rest of this
@@ -1041,96 +1137,56 @@
         // the correct error. Error strings that are modality-specific should also respect the
         // priority-order.
 
-        // Find first biometric authenticator that's strong enough, detected, enrolled, and enabled.
-        boolean disabledByDevicePolicy = false;
-        boolean hasSufficientStrength = false;
-        boolean isHardwareDetected = false;
-        boolean hasTemplatesEnrolled = false;
-        boolean enabledForApps = false;
+        int firstBiometricModality = TYPE_NONE;
+        @BiometricStatus int firstBiometricStatus = BIOMETRIC_NO_HARDWARE;
 
-        int modality = TYPE_NONE;
-        int firstHwAvailable = TYPE_NONE;
+        int biometricModality = TYPE_NONE;
+        @BiometricStatus int biometricStatus = BIOMETRIC_NO_HARDWARE;
+
         for (AuthenticatorWrapper authenticator : mAuthenticators) {
-            final int actualStrength = authenticator.getActualStrength();
             final int requestedStrength = Utils.getPublicBiometricStrength(bundle);
+            Pair<Integer, Integer> result = getStatusForBiometricAuthenticator(
+                    authenticator, userId, opPackageName, checkDevicePolicyManager,
+                    requestedStrength);
 
-            if (isBiometricDisabledByDevicePolicy(authenticator.modality, userId)) {
-                disabledByDevicePolicy = true;
-                continue;
-            }
-            disabledByDevicePolicy = false;
+            biometricStatus = result.second;
 
-            if (!Utils.isAtLeastStrength(actualStrength, requestedStrength)) {
-                continue;
-            }
-            hasSufficientStrength = true;
+            Slog.d(TAG, "Authenticator ID: " + authenticator.id
+                    + " Modality: " + authenticator.modality
+                    + " ReportedModality: " + result.first
+                    + " Status: " + biometricStatus);
 
-            if (!authenticator.impl.isHardwareDetected(opPackageName)) {
-                continue;
-            }
-            isHardwareDetected = true;
-
-            if (firstHwAvailable == TYPE_NONE) {
-                // Store the first one since we want to return the error in correct
-                // priority order.
-                firstHwAvailable = authenticator.modality;
+            if (firstBiometricModality == TYPE_NONE) {
+                firstBiometricModality = result.first;
+                firstBiometricStatus = biometricStatus;
             }
 
-            if (!authenticator.impl.hasEnrolledTemplates(userId, opPackageName)) {
-                continue;
+            if (biometricStatus == BIOMETRIC_OK) {
+                biometricModality = result.first;
+                break;
             }
-            hasTemplatesEnrolled = true;
-
-            if (!isEnabledForApp(authenticator.modality, userId)) {
-                continue;
-            }
-            enabledForApps = true;
-            modality = authenticator.modality;
-            break;
         }
 
-        biometricOk = !disabledByDevicePolicy
-                && hasSufficientStrength && isHardwareDetected
-                && hasTemplatesEnrolled && enabledForApps;
-
-        Slog.d(TAG, "checkAndGetAuthenticators: user=" + userId
-                + " checkDevicePolicyManager=" + checkDevicePolicyManager
-                + " isHardwareDetected=" + isHardwareDetected
-                + " hasTemplatesEnrolled=" + hasTemplatesEnrolled
-                + " enabledForApps=" + enabledForApps
-                + " disabledByDevicePolicy=" + disabledByDevicePolicy);
-
         if (biometricRequested && credentialRequested) {
-            if (credentialOk || biometricOk) {
-                if (!biometricOk) {
+            if (credentialOk || biometricStatus == BIOMETRIC_OK) {
+                if (biometricStatus != BIOMETRIC_OK) {
                     // If there's a problem with biometrics but device credential is
                     // allowed, only show credential UI.
                     bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED,
                             Authenticators.DEVICE_CREDENTIAL);
                 }
-                return new Pair<>(modality, BiometricConstants.BIOMETRIC_SUCCESS);
+                return new Pair<>(biometricModality, BiometricConstants.BIOMETRIC_SUCCESS);
             } else {
-                return new Pair<>(firstHwAvailable,
+                return new Pair<>(firstBiometricModality,
                         BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS);
             }
         } else if (biometricRequested) {
-            if (biometricOk) {
-                return new Pair<>(modality, BiometricConstants.BIOMETRIC_SUCCESS);
-            } else if (disabledByDevicePolicy) {
-                return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
-            } else if (!hasSufficientStrength) {
-                return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
-            } else if (!isHardwareDetected) {
-                return new Pair<>(firstHwAvailable,
-                        BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
-            } else if (!hasTemplatesEnrolled) {
-                return new Pair<>(firstHwAvailable,
-                        BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS);
-            } else if (!enabledForApps) {
-                return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
+            if (biometricStatus == BIOMETRIC_OK) {
+                return new Pair<>(biometricModality,
+                        biometricStatusToBiometricConstant(biometricStatus));
             } else {
-                Slog.e(TAG, "Unexpected case");
-                return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
+                return new Pair<>(firstBiometricModality,
+                        biometricStatusToBiometricConstant(firstBiometricStatus));
             }
         } else if (credentialRequested) {
             if (credentialOk) {
@@ -1403,16 +1459,14 @@
             return;
         }
 
-        if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
-            if (message == null) {
-                Slog.w(TAG, "Ignoring null message: " + acquiredInfo);
-                return;
-            }
-            try {
-                mStatusBarService.onBiometricHelp(message);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Remote exception", e);
-            }
+        if (message == null) {
+            Slog.w(TAG, "Ignoring null message: " + acquiredInfo);
+            return;
+        }
+        try {
+            mStatusBarService.onBiometricHelp(message);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception", e);
         }
     }
 
diff --git a/services/core/java/com/android/server/biometrics/LoggableMonitor.java b/services/core/java/com/android/server/biometrics/LoggableMonitor.java
index c03c77f..c50ab17 100644
--- a/services/core/java/com/android/server/biometrics/LoggableMonitor.java
+++ b/services/core/java/com/android/server/biometrics/LoggableMonitor.java
@@ -20,6 +20,7 @@
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
 import android.util.Slog;
 
 import com.android.internal.util.FrameworkStatsLog;
@@ -69,8 +70,12 @@
 
     protected final void logOnAcquired(Context context, int acquiredInfo, int vendorCode,
             int targetUserId) {
-        if (statsModality() == BiometricsProtoEnums.MODALITY_FACE) {
-            if (acquiredInfo == FaceManager.FACE_ACQUIRED_START) {
+
+        final boolean isFace = statsModality() == BiometricsProtoEnums.MODALITY_FACE;
+        final boolean isFingerprint = statsModality() == BiometricsProtoEnums.MODALITY_FINGERPRINT;
+        if (isFace || isFingerprint) {
+            if ((isFingerprint && acquiredInfo == FingerprintManager.FINGERPRINT_ACQUIRED_START)
+                    || (isFace && acquiredInfo == FaceManager.FACE_ACQUIRED_START)) {
                 mFirstAcquireTimeMs = System.currentTimeMillis();
             }
         } else if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
@@ -124,7 +129,7 @@
                 error,
                 vendorCode,
                 Utils.isDebugEnabled(context, targetUserId),
-                latency);
+                sanitizeLatency(latency));
     }
 
     protected final void logOnAuthenticated(Context context, boolean authenticated,
@@ -165,7 +170,7 @@
                 statsClient(),
                 requireConfirmation,
                 authState,
-                latency,
+                sanitizeLatency(latency),
                 Utils.isDebugEnabled(context, targetUserId));
     }
 
@@ -183,8 +188,16 @@
         FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ENROLLED,
                 statsModality(),
                 targetUserId,
-                latency,
+                sanitizeLatency(latency),
                 enrollSuccessful);
     }
 
+    private long sanitizeLatency(long latency) {
+        if (latency < 0) {
+            Slog.w(TAG, "found a negative latency : " + latency);
+            return -1;
+        }
+        return latency;
+    }
+
 }
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 2d4ab63..8f3fd36 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -80,7 +80,7 @@
      * @param authenticators composed of one or more values from {@link Authenticators}
      * @return true if device credential is allowed.
      */
-    public static boolean isDeviceCredentialAllowed(@Authenticators.Types int authenticators) {
+    public static boolean isCredentialRequested(@Authenticators.Types int authenticators) {
         return (authenticators & Authenticators.DEVICE_CREDENTIAL) != 0;
     }
 
@@ -88,8 +88,8 @@
      * @param bundle should be first processed by {@link #combineAuthenticatorBundles(Bundle)}
      * @return true if device credential is allowed.
      */
-    public static boolean isDeviceCredentialAllowed(Bundle bundle) {
-        return isDeviceCredentialAllowed(bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
+    public static boolean isCredentialRequested(Bundle bundle) {
+        return isCredentialRequested(bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
     }
 
     /**
@@ -120,7 +120,7 @@
      * @param bundle should be first processed by {@link #combineAuthenticatorBundles(Bundle)}
      * @return true if biometric authentication is allowed.
      */
-    public static boolean isBiometricAllowed(Bundle bundle) {
+    public static boolean isBiometricRequested(Bundle bundle) {
         return getPublicBiometricStrength(bundle) != 0;
     }
 
@@ -169,7 +169,7 @@
         // should be set.
         final int biometricBits = authenticators & Authenticators.BIOMETRIC_MIN_STRENGTH;
         if (biometricBits == Authenticators.EMPTY_SET
-                && isDeviceCredentialAllowed(authenticators)) {
+                && isCredentialRequested(authenticators)) {
             return true;
         } else if (biometricBits == Authenticators.BIOMETRIC_STRONG) {
             return true;
@@ -209,6 +209,9 @@
             case BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT:
                 biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE;
                 break;
+            case BiometricConstants.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED:
+                biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED;
+                break;
             default:
                 Slog.e(BiometricService.TAG, "Unhandled result code: " + biometricConstantsCode);
                 biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index 57d1867..0a61988 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -41,7 +41,7 @@
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
 import android.hardware.biometrics.IBiometricServiceReceiverInternal;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprintClientCallback;
+import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback;
 import android.hardware.fingerprint.Fingerprint;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.IFingerprintClientActiveCallback;
@@ -601,6 +601,11 @@
 
         @Override
         public void onAcquired(final long deviceId, final int acquiredInfo, final int vendorCode) {
+            onAcquired_2_2(deviceId, acquiredInfo, vendorCode);
+        }
+
+        @Override
+        public void onAcquired_2_2(long deviceId, int acquiredInfo, int vendorCode) {
             mHandler.post(() -> {
                 FingerprintService.super.handleAcquired(deviceId, acquiredInfo, vendorCode);
             });
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
index 1a1845a..cbfa87f 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
@@ -274,8 +274,18 @@
         return hwSel;
     }
 
-    static @NonNull ProgramSelector programSelectorFromHal(
+    private static boolean isEmpty(
             @NonNull android.hardware.broadcastradio.V2_0.ProgramSelector sel) {
+        if (sel.primaryId.type != 0) return false;
+        if (sel.primaryId.value != 0) return false;
+        if (sel.secondaryIds.size() != 0) return false;
+        return true;
+    }
+
+    static @Nullable ProgramSelector programSelectorFromHal(
+            @NonNull android.hardware.broadcastradio.V2_0.ProgramSelector sel) {
+        if (isEmpty(sel)) return null;
+
         ProgramSelector.Identifier[] secondaryIds = sel.secondaryIds.stream().
                 map(Convert::programIdentifierFromHal).map(Objects::requireNonNull).
                 toArray(ProgramSelector.Identifier[]::new);
@@ -363,7 +373,7 @@
                 collect(Collectors.toList());
 
         return new RadioManager.ProgramInfo(
-                programSelectorFromHal(info.selector),
+                Objects.requireNonNull(programSelectorFromHal(info.selector)),
                 programIdentifierFromHal(info.logicallyTunedTo),
                 programIdentifierFromHal(info.physicallyTunedTo),
                 relatedContent,
@@ -401,7 +411,7 @@
     public static @NonNull android.hardware.radio.Announcement announcementFromHal(
             @NonNull Announcement hwAnnouncement) {
         return new android.hardware.radio.Announcement(
-            programSelectorFromHal(hwAnnouncement.selector),
+            Objects.requireNonNull(programSelectorFromHal(hwAnnouncement.selector)),
             hwAnnouncement.type,
             vendorInfoFromHal(hwAnnouncement.vendorInfo)
         );
diff --git a/services/core/java/com/android/server/compat/TEST_MAPPING b/services/core/java/com/android/server/compat/TEST_MAPPING
new file mode 100644
index 0000000..0c30c79
--- /dev/null
+++ b/services/core/java/com/android/server/compat/TEST_MAPPING
@@ -0,0 +1,21 @@
+{
+    "presubmit": [
+        // Unit tests
+        {
+            "name": "FrameworksServicesTests",
+            "options": [
+                {
+                    "include-filter": "com.android.server.compat"
+                }
+            ]
+        },
+        // Tests for the TestRule
+        {
+            "name": "PlatformCompatGating"
+        },
+        // CTS tests
+        {
+            "name": "CtsAppCompatHostTestCases#"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index af8a366..5059a48 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -325,7 +325,7 @@
             mSlot = slot;
             int error = isValid();
             if (error == SUCCESS) {
-                Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.name());
+                Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.toShortString());
                 switch (mType) {
                     case TYPE_NATT:
                         mNai.asyncChannel.sendMessage(
@@ -365,7 +365,8 @@
                     Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network);
                 }
             }
-            Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.name() + ": " + reason);
+            Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.toShortString()
+                    + ": " + reason);
             switch (mStartedState) {
                 case NOT_STARTED:
                     // Remove the reference of the keepalive that meet error before starting,
@@ -476,7 +477,7 @@
     }
 
     public void handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason) {
-        String networkName = (nai == null) ? "(null)" : nai.name();
+        final String networkName = NetworkAgentInfo.toShortString(nai);
         HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
         if (networkKeepalives == null) {
             Log.e(TAG, "Attempt to stop keepalive on nonexistent network " + networkName);
@@ -493,7 +494,7 @@
     }
 
     private void cleanupStoppedKeepalive(NetworkAgentInfo nai, int slot) {
-        String networkName = (nai == null) ? "(null)" : nai.name();
+        final String networkName = NetworkAgentInfo.toShortString(nai);
         HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
         if (networkKeepalives == null) {
             Log.e(TAG, "Attempt to remove keepalive on nonexistent network " + networkName);
@@ -540,7 +541,7 @@
         } catch(NullPointerException e) {}
         if (ki == null) {
             Log.e(TAG, "Event " + message.what + "," + slot + "," + reason
-                    + " for unknown keepalive " + slot + " on " + nai.name());
+                    + " for unknown keepalive " + slot + " on " + nai.toShortString());
             return;
         }
 
@@ -562,7 +563,7 @@
         if (KeepaliveInfo.STARTING == ki.mStartedState) {
             if (SUCCESS == reason) {
                 // Keepalive successfully started.
-                Log.d(TAG, "Started keepalive " + slot + " on " + nai.name());
+                Log.d(TAG, "Started keepalive " + slot + " on " + nai.toShortString());
                 ki.mStartedState = KeepaliveInfo.STARTED;
                 try {
                     ki.mCallback.onStarted(slot);
@@ -570,14 +571,14 @@
                     Log.w(TAG, "Discarded onStarted(" + slot + ") callback");
                 }
             } else {
-                Log.d(TAG, "Failed to start keepalive " + slot + " on " + nai.name()
+                Log.d(TAG, "Failed to start keepalive " + slot + " on " + nai.toShortString()
                         + ": " + reason);
                 // The message indicated some error trying to start: do call handleStopKeepalive.
                 handleStopKeepalive(nai, slot, reason);
             }
         } else if (KeepaliveInfo.STOPPING == ki.mStartedState) {
             // The message indicated result of stopping : clean up keepalive slots.
-            Log.d(TAG, "Stopped keepalive " + slot + " on " + nai.name()
+            Log.d(TAG, "Stopped keepalive " + slot + " on " + nai.toShortString()
                     + " stopped: " + reason);
             ki.mStartedState = KeepaliveInfo.NOT_STARTED;
             cleanupStoppedKeepalive(nai, slot);
@@ -733,7 +734,7 @@
         pw.println("Socket keepalives:");
         pw.increaseIndent();
         for (NetworkAgentInfo nai : mKeepalives.keySet()) {
-            pw.println(nai.name());
+            pw.println(nai.toShortString());
             pw.increaseIndent();
             for (int slot : mKeepalives.get(nai).keySet()) {
                 KeepaliveInfo ki = mKeepalives.get(nai).get(slot);
diff --git a/services/core/java/com/android/server/connectivity/LingerMonitor.java b/services/core/java/com/android/server/connectivity/LingerMonitor.java
index 7071510..04c000f 100644
--- a/services/core/java/com/android/server/connectivity/LingerMonitor.java
+++ b/services/core/java/com/android/server/connectivity/LingerMonitor.java
@@ -200,8 +200,9 @@
         }
 
         if (DBG) {
-            Log.d(TAG, "Notifying switch from=" + fromNai.name() + " to=" + toNai.name() +
-                    " type=" + sNotifyTypeNames.get(notifyType, "unknown(" + notifyType + ")"));
+            Log.d(TAG, "Notifying switch from=" + fromNai.toShortString()
+                    + " to=" + toNai.toShortString()
+                    + " type=" + sNotifyTypeNames.get(notifyType, "unknown(" + notifyType + ")"));
         }
 
         mNotifications.put(fromNai.network.netId, toNai.network.netId);
@@ -222,10 +223,10 @@
     public void noteLingerDefaultNetwork(@NonNull final NetworkAgentInfo fromNai,
             @Nullable final NetworkAgentInfo toNai) {
         if (VDBG) {
-            Log.d(TAG, "noteLingerDefaultNetwork from=" + fromNai.name() +
-                    " everValidated=" + fromNai.everValidated +
-                    " lastValidated=" + fromNai.lastValidated +
-                    " to=" + toNai.name());
+            Log.d(TAG, "noteLingerDefaultNetwork from=" + fromNai.toShortString()
+                    + " everValidated=" + fromNai.everValidated
+                    + " lastValidated=" + fromNai.lastValidated
+                    + " to=" + toNai.toShortString());
         }
 
         // If we are currently notifying the user because the device switched to fromNai, now that
@@ -270,7 +271,8 @@
         // TODO: should we do this?
         if (everNotified(fromNai)) {
             if (VDBG) {
-                Log.d(TAG, "Not notifying handover from " + fromNai.name() + ", already notified");
+                Log.d(TAG, "Not notifying handover from " + fromNai.toShortString()
+                        + ", already notified");
             }
             return;
         }
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index f636d67..82465f8 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -174,7 +174,7 @@
         try {
             mNMService.registerObserver(this);
         } catch (RemoteException e) {
-            Slog.e(TAG, "Can't register interface observer for clat on " + mNetwork.name());
+            Slog.e(TAG, "Can't register iface observer for clat on " + mNetwork.toShortString());
             return;
         }
 
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 3cfe916..592be2f 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -16,7 +16,10 @@
 
 package com.android.server.connectivity;
 
+import static android.net.NetworkCapabilities.transportNamesOf;
+
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.net.IDnsResolver;
 import android.net.INetd;
@@ -372,7 +375,7 @@
             // Should only happen if the requestId wraps. If that happens lots of other things will
             // be broken as well.
             Log.wtf(TAG, String.format("Duplicate requestId for %s and %s on %s",
-                    networkRequest, existing, name()));
+                    networkRequest, existing, toShortString()));
             updateRequestCounts(REMOVE, existing);
         }
         mNetworkRequests.put(networkRequest.requestId, networkRequest);
@@ -542,11 +545,11 @@
             // Cannot happen. Once a request is lingering on a particular network, we cannot
             // re-linger it unless that network becomes the best for that request again, in which
             // case we should have unlingered it.
-            Log.wtf(TAG, this.name() + ": request " + request.requestId + " already lingered");
+            Log.wtf(TAG, toShortString() + ": request " + request.requestId + " already lingered");
         }
         final long expiryMs = now + duration;
         LingerTimer timer = new LingerTimer(request, expiryMs);
-        if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + this.name());
+        if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + toShortString());
         mLingerTimers.add(timer);
         mLingerTimerForRequest.put(request.requestId, timer);
     }
@@ -558,7 +561,7 @@
     public boolean unlingerRequest(NetworkRequest request) {
         LingerTimer timer = mLingerTimerForRequest.get(request.requestId);
         if (timer != null) {
-            if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + this.name());
+            if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + toShortString());
             mLingerTimers.remove(timer);
             mLingerTimerForRequest.remove(request.requestId);
             return true;
@@ -645,9 +648,16 @@
                 + "}";
     }
 
-    public String name() {
-        return "NetworkAgentInfo [" + networkInfo.getTypeName() + " (" +
-                networkInfo.getSubtypeName() + ") - " + Objects.toString(network) + "]";
+    /**
+     * Show a short string representing a Network.
+     *
+     * This is often not enough for debugging purposes for anything complex, but the full form
+     * is very long and hard to read, so this is useful when there isn't a lot of ambiguity.
+     * This represents the network with something like "[100 WIFI|VPN]" or "[108 MOBILE]".
+     */
+    public String toShortString() {
+        return "[" + network.netId + " "
+                + transportNamesOf(networkCapabilities.getTransportTypes()) + "]";
     }
 
     // Enables sorting in descending order of score.
@@ -655,4 +665,12 @@
     public int compareTo(NetworkAgentInfo other) {
         return other.getCurrentScore() - getCurrentScore();
     }
+
+    /**
+     * Null-guarding version of NetworkAgentInfo#toShortString()
+     */
+    @NonNull
+    public static String toShortString(@Nullable final NetworkAgentInfo nai) {
+        return null != nai ? nai.toShortString() : "[null]";
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/NetworkRanker.java b/services/core/java/com/android/server/connectivity/NetworkRanker.java
new file mode 100644
index 0000000..d0aabf95
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/NetworkRanker.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.NetworkRequest;
+
+import java.util.Collection;
+
+/**
+ * A class that knows how to find the best network matching a request out of a list of networks.
+ */
+public class NetworkRanker {
+    public NetworkRanker() { }
+
+    /**
+     * Find the best network satisfying this request among the list of passed networks.
+     */
+    // Almost equivalent to Collections.max(nais), but allows returning null if no network
+    // satisfies the request.
+    @Nullable
+    public NetworkAgentInfo getBestNetwork(@NonNull final NetworkRequest request,
+            @NonNull final Collection<NetworkAgentInfo> nais) {
+        NetworkAgentInfo bestNetwork = null;
+        int bestScore = Integer.MIN_VALUE;
+        for (final NetworkAgentInfo nai : nais) {
+            if (!nai.satisfies(request)) continue;
+            if (nai.getCurrentScore() > bestScore) {
+                bestNetwork = nai;
+                bestScore = nai.getCurrentScore();
+            }
+        }
+        return bestNetwork;
+    }
+}
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index c99774a..6178e6c 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -21,6 +21,7 @@
 import android.app.ActivityTaskManager;
 import android.app.IActivityTaskManager;
 import android.app.TaskStackListener;
+import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.hardware.Sensor;
@@ -41,6 +42,7 @@
 import android.util.Slog;
 import android.util.TimeUtils;
 
+import com.android.internal.BrightnessSynchronizer;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
 import com.android.server.EventLogTags;
@@ -89,6 +91,8 @@
     // The minimum and maximum screen brightnesses.
     private final int mScreenBrightnessRangeMinimum;
     private final int mScreenBrightnessRangeMaximum;
+    private final float mScreenBrightnessRangeMinimumFloat;
+    private final float mScreenBrightnessRangeMaximumFloat;
 
     // How much to scale doze brightness by (should be (0, 1.0]).
     private final float mDozeScaleFactor;
@@ -174,7 +178,7 @@
     // that we can quickly revert to the previous auto-brightness level
     // while the light sensor warms up.
     // Use -1 if there is no current auto-brightness value available.
-    private int mScreenAutoBrightness = -1;
+    private int mScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID;
 
     // The current display policy. This is useful, for example,  for knowing when we're dozing,
     // where the light sensor may not be available.
@@ -204,39 +208,44 @@
     private TaskStackListenerImpl mTaskStackListener;
     private IActivityTaskManager mActivityTaskManager;
     private PackageManager mPackageManager;
+    private Context mContext;
 
     private final Injector mInjector;
 
     AutomaticBrightnessController(Callbacks callbacks, Looper looper,
             SensorManager sensorManager, Sensor lightSensor, BrightnessMappingStrategy mapper,
-            int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor,
-            int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig,
-            long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig,
-            HysteresisLevels ambientBrightnessThresholds,
-            HysteresisLevels screenBrightnessThresholds,
-            PackageManager packageManager) {
+            int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
+            float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
+            long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
+            boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
+            HysteresisLevels screenBrightnessThresholds, Context context) {
         this(new Injector(), callbacks, looper, sensorManager, lightSensor, mapper,
                 lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor,
                 lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
                 darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
-                ambientBrightnessThresholds, screenBrightnessThresholds, packageManager);
+                ambientBrightnessThresholds, screenBrightnessThresholds, context);
     }
 
     @VisibleForTesting
     AutomaticBrightnessController(Injector injector, Callbacks callbacks, Looper looper,
             SensorManager sensorManager, Sensor lightSensor, BrightnessMappingStrategy mapper,
-            int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor,
-            int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig,
-            long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig,
-            HysteresisLevels ambientBrightnessThresholds,
-            HysteresisLevels screenBrightnessThresholds,
-            PackageManager packageManager) {
+            int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
+            float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
+            long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
+            boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
+            HysteresisLevels screenBrightnessThresholds, Context context) {
         mInjector = injector;
+        mContext = context;
         mCallbacks = callbacks;
         mSensorManager = sensorManager;
         mBrightnessMapper = mapper;
-        mScreenBrightnessRangeMinimum = brightnessMin;
-        mScreenBrightnessRangeMaximum = brightnessMax;
+        mScreenBrightnessRangeMinimum =
+                BrightnessSynchronizer.brightnessFloatToInt(mContext, brightnessMin);
+        mScreenBrightnessRangeMaximum =
+                com.android.internal.BrightnessSynchronizer.brightnessFloatToInt(
+                        mContext, brightnessMax);
+        mScreenBrightnessRangeMinimumFloat = brightnessMin;
+        mScreenBrightnessRangeMaximumFloat = brightnessMax;
         mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime;
         mDozeScaleFactor = dozeScaleFactor;
         mNormalLightSensorRate = lightSensorRate;
@@ -261,7 +270,7 @@
         }
 
         mActivityTaskManager = ActivityTaskManager.getService();
-        mPackageManager = packageManager;
+        mPackageManager = mContext.getPackageManager();
         mTaskStackListener = new TaskStackListenerImpl();
         mForegroundAppPackageName = null;
         mPendingForegroundAppPackageName = null;
@@ -291,7 +300,7 @@
             return -1;
         }
         if (mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE) {
-            return (int) (mScreenAutoBrightness * mDozeScaleFactor);
+            return Math.round(mScreenAutoBrightness * mDozeScaleFactor);
         }
         return mScreenAutoBrightness;
     }
@@ -473,7 +482,7 @@
         } else if (mLightSensorEnabled) {
             mLightSensorEnabled = false;
             mAmbientLuxValid = !mResetAmbientLuxAfterWarmUpConfig;
-            mScreenAutoBrightness = -1;
+            mScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID;
             mRecentLightSamples = 0;
             mAmbientLightRingBuffer.clear();
             mCurrentLightSensorRate = -1;
@@ -722,10 +731,8 @@
 
         float value = mBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName,
                 mForegroundAppCategory);
-
-        int newScreenAutoBrightness = Math.round(clampScreenBrightness(
-                value * PowerManager.BRIGHTNESS_ON));
-
+        int newScreenAutoBrightness = BrightnessSynchronizer.brightnessFloatToInt(
+                mContext, clampScreenBrightnessFloat(value));
         // If screenAutoBrightness is set, we should have screen{Brightening,Darkening}Threshold,
         // in which case we ignore the new screen brightness if it doesn't differ enough from the
         // previous one.
@@ -759,11 +766,19 @@
         }
     }
 
+    // Clamps values with float range [1.0-255.0]
+    // TODO(brightnessfloat): convert everything that uses this to float system
     private float clampScreenBrightness(float value) {
         return MathUtils.constrain(value,
                 mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
     }
 
+    // Clamps values with float range [0.0-1.0]
+    private float clampScreenBrightnessFloat(float value) {
+        return MathUtils.constrain(value,
+                mScreenBrightnessRangeMinimumFloat, mScreenBrightnessRangeMaximumFloat);
+    }
+
     private void prepareBrightnessAdjustmentSample() {
         if (!mBrightnessAdjustmentSamplePending) {
             mBrightnessAdjustmentSamplePending = true;
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 6ff2767..a099606 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -28,6 +28,7 @@
 import android.util.Slog;
 import android.util.Spline;
 
+import com.android.internal.BrightnessSynchronizer;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 import com.android.server.display.utils.Plog;
@@ -342,9 +343,9 @@
     }
 
     protected static float normalizeAbsoluteBrightness(int brightness) {
-        brightness = MathUtils.constrain(brightness,
-                PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
-        return (float) brightness / PowerManager.BRIGHTNESS_ON;
+        return BrightnessSynchronizer.brightnessIntToFloat(brightness,
+                PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON,
+                PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX);
     }
 
     private Pair<float[], float[]> insertControlPoint(
diff --git a/services/core/java/com/android/server/display/DisplayBlanker.java b/services/core/java/com/android/server/display/DisplayBlanker.java
index 816dc13..d294898 100644
--- a/services/core/java/com/android/server/display/DisplayBlanker.java
+++ b/services/core/java/com/android/server/display/DisplayBlanker.java
@@ -20,5 +20,5 @@
  * Interface used to update the actual display state.
  */
 public interface DisplayBlanker {
-    void requestDisplayState(int state, int brightness);
+    void requestDisplayState(int state, float brightness);
 }
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index e69a3b8b..63a8d7c 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -129,11 +129,11 @@
      * Sets the display state, if supported.
      *
      * @param state The new display state.
-     * @param brightness The new display brightness.
+     * @param brightnessState The new display brightnessState.
      * @return A runnable containing work to be deferred until after we have
      * exited the critical section, or null if none.
      */
-    public Runnable requestDisplayStateLocked(int state, int brightness) {
+    public Runnable requestDisplayStateLocked(int state, float brightnessState) {
         return null;
     }
 
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 71ade62..9140589 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -232,7 +232,7 @@
 
     // The overall display brightness.
     // For now, this only applies to the built-in display but we may split it up eventually.
-    private int mGlobalDisplayBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+    private float mGlobalDisplayBrightness;
 
     // Set to true when there are pending display changes that have yet to be applied
     // to the surface flinger state.
@@ -340,7 +340,8 @@
         mMinimumBrightnessSpline = Spline.createSpline(lux, nits);
 
         PowerManager pm = mContext.getSystemService(PowerManager.class);
-        mGlobalDisplayBrightness = pm.getDefaultScreenBrightnessSetting();
+        mGlobalDisplayBrightness = pm.getBrightnessConstraint(
+                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT);
         mCurrentUserId = UserHandle.USER_SYSTEM;
         ColorSpace[] colorSpaces = SurfaceControl.getCompositionColorSpaces();
         mWideColorSpace = colorSpaces[1];
@@ -539,16 +540,16 @@
         }
     }
 
-    private void requestGlobalDisplayStateInternal(int state, int brightness) {
+    private void requestGlobalDisplayStateInternal(int state, float brightnessState) {
         if (state == Display.STATE_UNKNOWN) {
             state = Display.STATE_ON;
         }
         if (state == Display.STATE_OFF) {
-            brightness = PowerManager.BRIGHTNESS_OFF;
-        } else if (brightness < 0) {
-            brightness = PowerManager.BRIGHTNESS_DEFAULT;
-        } else if (brightness > PowerManager.BRIGHTNESS_ON) {
-            brightness = PowerManager.BRIGHTNESS_ON;
+            brightnessState = PowerManager.BRIGHTNESS_OFF_FLOAT;
+        } else if (brightnessState < PowerManager.BRIGHTNESS_MIN || Float.isNaN(brightnessState)) {
+            brightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+        } else if (brightnessState > PowerManager.BRIGHTNESS_MAX) {
+            brightnessState = PowerManager.BRIGHTNESS_MAX;
         }
 
         synchronized (mTempDisplayStateWorkQueue) {
@@ -558,15 +559,15 @@
                 // may happen as a side-effect of displays changing state.
                 synchronized (mSyncRoot) {
                     if (mGlobalDisplayState == state
-                            && mGlobalDisplayBrightness == brightness) {
+                            && mGlobalDisplayBrightness == brightnessState) {
                         return; // no change
                     }
 
                     Trace.traceBegin(Trace.TRACE_TAG_POWER, "requestGlobalDisplayState("
                             + Display.stateToString(state)
-                            + ", brightness=" + brightness + ")");
+                            + ", brightness=" + brightnessState + ")");
                     mGlobalDisplayState = state;
-                    mGlobalDisplayBrightness = brightness;
+                    mGlobalDisplayBrightness = brightnessState;
                     applyGlobalDisplayStateLocked(mTempDisplayStateWorkQueue);
                 }
 
@@ -1023,7 +1024,8 @@
         // by the display power controller (if known).
         DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
         if ((info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
-            return device.requestDisplayStateLocked(mGlobalDisplayState, mGlobalDisplayBrightness);
+            return device.requestDisplayStateLocked(
+                    mGlobalDisplayState, mGlobalDisplayBrightness);
         }
         return null;
     }
@@ -2300,7 +2302,7 @@
         }
 
         @Override // Binder call
-        public void setTemporaryBrightness(int brightness) {
+        public void setTemporaryBrightness(float brightness) {
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,
                     "Permission required to set the display's brightness");
@@ -2419,7 +2421,7 @@
             synchronized (mSyncRoot) {
                 DisplayBlanker blanker = new DisplayBlanker() {
                     @Override
-                    public void requestDisplayState(int state, int brightness) {
+                    public void requestDisplayState(int state, float brightness) {
                         // The order of operations is important for legacy reasons.
                         if (state == Display.STATE_OFF) {
                             requestGlobalDisplayStateInternal(state, brightness);
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index e87ad41..0c6c797 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -108,8 +108,8 @@
                 "Permission required to set the display's brightness");
         final long token = Binder.clearCallingIdentity();
         try {
-            Settings.System.putIntForUser(context.getContentResolver(),
-                    Settings.System.SCREEN_BRIGHTNESS, (int) (brightness * 255),
+            Settings.System.putFloatForUser(context.getContentResolver(),
+                    Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightness,
                     UserHandle.USER_CURRENT);
         } finally {
             Binder.restoreCallingIdentity(token);
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 96532f4..8bbeabf 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -946,9 +946,9 @@
      * {@link R.array#config_ambientThresholdsOfPeakRefreshRate}.
      */
     private class BrightnessObserver extends ContentObserver {
+        // TODO: brightnessfloat: change this to the float setting
         private final Uri mDisplayBrightnessSetting =
                 Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
-
         private final static int LIGHT_SENSOR_RATE_MS = 250;
         private int[] mDisplayBrightnessThresholds;
         private int[] mAmbientBrightnessThresholds;
@@ -1174,7 +1174,7 @@
 
             return false;
         }
-
+        // TODO: brightnessfloat: make it use float not int
         private void onBrightnessChangedLocked() {
             int brightness = Settings.System.getInt(mContext.getContentResolver(),
                     Settings.System.SCREEN_BRIGHTNESS, -1);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index f1655f0..197842e 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -51,6 +51,7 @@
 import android.util.TimeUtils;
 import android.view.Display;
 
+import com.android.internal.BrightnessSynchronizer;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -102,7 +103,8 @@
     private static final boolean USE_COLOR_FADE_ON_ANIMATION = false;
 
     // The minimum reduction in brightness when dimmed.
-    private static final int SCREEN_DIM_MINIMUM_REDUCTION = 10;
+    private static final float SCREEN_DIM_MINIMUM_REDUCTION_FLOAT = 0.04f;
+    private static final float SCREEN_ANIMATION_RATE_MINIMUM = 0.0f;
 
     private static final int COLOR_FADE_ON_ANIMATION_DURATION_MILLIS = 250;
     private static final int COLOR_FADE_OFF_ANIMATION_DURATION_MILLIS = 400;
@@ -169,28 +171,27 @@
     private Sensor mProximitySensor;
 
     // The doze screen brightness.
-    private final int mScreenBrightnessDozeConfig;
+    private final float mScreenBrightnessDozeConfig;
 
     // The dim screen brightness.
-    private final int mScreenBrightnessDimConfig;
+    private final float mScreenBrightnessDimConfig;
 
     // The minimum allowed brightness.
-    private final int mScreenBrightnessRangeMinimum;
+    private final float mScreenBrightnessRangeMinimum;
 
     // The maximum allowed brightness.
-    private final int mScreenBrightnessRangeMaximum;
+    private final float mScreenBrightnessRangeMaximum;
 
-    // The default screen brightness.
-    private final int mScreenBrightnessDefault;
+    private final float mScreenBrightnessDefault;
 
     // The minimum allowed brightness while in VR.
-    private final int mScreenBrightnessForVrRangeMinimum;
+    private final float mScreenBrightnessForVrRangeMinimum;
 
     // The maximum allowed brightness while in VR.
-    private final int mScreenBrightnessForVrRangeMaximum;
+    private final float mScreenBrightnessForVrRangeMaximum;
 
     // The default screen brightness for VR.
-    private final int mScreenBrightnessForVrDefault;
+    private final float mScreenBrightnessForVrDefault;
 
     // True if auto-brightness should be used.
     private boolean mUseSoftwareAutoBrightnessConfig;
@@ -317,8 +318,9 @@
     private BrightnessReason mBrightnessReasonTemp = new BrightnessReason();
 
     // Brightness animation ramp rates in brightness units per second
-    private final int mBrightnessRampRateFast;
-    private final int mBrightnessRampRateSlow;
+    private final float mBrightnessRampRateSlow = 0.2352941f;
+    private final float mBrightnessRampRateFast = 0.7058823f;
+
 
     // Whether or not to skip the initial brightness ramps into STATE_ON.
     private final boolean mSkipScreenOnBrightnessRamp;
@@ -333,7 +335,7 @@
     private int mSkipRampState = RAMP_STATE_SKIP_NONE;
 
     // The first autobrightness value set when entering RAMP_STATE_SKIP_INITIAL.
-    private int mInitialAutoBrightness;
+    private float mInitialAutoBrightness;
 
     // The controller for the automatic brightness level.
     private AutomaticBrightnessController mAutomaticBrightnessController;
@@ -348,24 +350,24 @@
 
     // The last brightness that was set by the user and not temporary. Set to -1 when a brightness
     // has yet to be recorded.
-    private int mLastUserSetScreenBrightness;
+    private float mLastUserSetScreenBrightness;
 
     // The screen brightenss setting has changed but not taken effect yet. If this is different
     // from the current screen brightness setting then this is coming from something other than us
     // and should be considered a user interaction.
-    private int mPendingScreenBrightnessSetting;
+    private float mPendingScreenBrightnessSetting;
 
     // The last observed screen brightness setting, either set by us or by the settings app on
     // behalf of the user.
-    private int mCurrentScreenBrightnessSetting;
+    private float mCurrentScreenBrightnessSetting;
 
     // The temporary screen brightness. Typically set when a user is interacting with the
-    // brightness slider but hasn't settled on a choice yet. Set to -1 when there's no temporary
-    // brightness set.
-    private int mTemporaryScreenBrightness;
+    // brightness slider but hasn't settled on a choice yet. Set to
+    // PowerManager.BRIGHNTESS_INVALID_FLOAT when there's no temporary brightness set.
+    private float mTemporaryScreenBrightness;
 
     // The current screen brightness while in VR mode.
-    private int mScreenBrightnessForVr;
+    private float mScreenBrightnessForVr;
 
     // The last auto brightness adjustment that was set by the user and not temporary. Set to
     // Float.NaN when an auto-brightness adjustment hasn't been recorded yet.
@@ -384,6 +386,8 @@
     private ObjectAnimator mColorFadeOffAnimator;
     private RampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
 
+    private BrightnessSynchronizer mBrightnessSynchronizer;
+
     /**
      * Creates the display power controller.
      */
@@ -394,37 +398,39 @@
         mBrightnessTracker = new BrightnessTracker(context, null);
         mSettingsObserver = new SettingsObserver(mHandler);
         mCallbacks = callbacks;
-
+        mBrightnessSynchronizer = new BrightnessSynchronizer(context);
         mBatteryStats = BatteryStatsService.getService();
         mSensorManager = sensorManager;
         mWindowManagerPolicy = LocalServices.getService(WindowManagerPolicy.class);
         mBlanker = blanker;
         mContext = context;
 
+        PowerManager pm =  context.getSystemService(PowerManager.class);
         final Resources resources = context.getResources();
-        final int screenBrightnessSettingMinimum = clampAbsoluteBrightness(resources.getInteger(
-                com.android.internal.R.integer.config_screenBrightnessSettingMinimum));
+        final float screenBrightnessSettingMinimumFloat = clampAbsoluteBrightness(
+                pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM));
 
-        mScreenBrightnessDozeConfig = clampAbsoluteBrightness(resources.getInteger(
-                com.android.internal.R.integer.config_screenBrightnessDoze));
+        // DOZE AND DIM SETTINGS
+        mScreenBrightnessDozeConfig = clampAbsoluteBrightness(
+                pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DOZE));
+        mScreenBrightnessDimConfig = clampAbsoluteBrightness(
+                pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM));
 
-        mScreenBrightnessDimConfig = clampAbsoluteBrightness(resources.getInteger(
-                com.android.internal.R.integer.config_screenBrightnessDim));
-
+        // NORMAL SCREEN SETTINGS
         mScreenBrightnessRangeMinimum =
-                Math.min(screenBrightnessSettingMinimum, mScreenBrightnessDimConfig);
+                Math.min(screenBrightnessSettingMinimumFloat, mScreenBrightnessDimConfig);
+        mScreenBrightnessRangeMaximum = clampAbsoluteBrightness(
+                pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM));
+        mScreenBrightnessDefault = clampAbsoluteBrightness(
+                pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT));
 
-        mScreenBrightnessRangeMaximum = clampAbsoluteBrightness(resources.getInteger(
-                    com.android.internal.R.integer.config_screenBrightnessSettingMaximum));
-        mScreenBrightnessDefault = clampAbsoluteBrightness(resources.getInteger(
-                    com.android.internal.R.integer.config_screenBrightnessSettingDefault));
-
-        mScreenBrightnessForVrRangeMinimum = clampAbsoluteBrightness(resources.getInteger(
-                    com.android.internal.R.integer.config_screenBrightnessForVrSettingMinimum));
-        mScreenBrightnessForVrRangeMaximum = clampAbsoluteBrightness(resources.getInteger(
-                    com.android.internal.R.integer.config_screenBrightnessForVrSettingMaximum));
-        mScreenBrightnessForVrDefault = clampAbsoluteBrightness(resources.getInteger(
-                    com.android.internal.R.integer.config_screenBrightnessForVrSettingDefault));
+        // VR SETTINGS
+        mScreenBrightnessForVrDefault = clampAbsoluteBrightness(
+                pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_VR));
+        mScreenBrightnessForVrRangeMaximum = clampAbsoluteBrightness(
+                pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM_VR));
+        mScreenBrightnessForVrRangeMinimum = clampAbsoluteBrightness(
+                pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR));
 
         mUseSoftwareAutoBrightnessConfig = resources.getBoolean(
                 com.android.internal.R.bool.config_automatic_brightness_available);
@@ -432,10 +438,6 @@
         mAllowAutoBrightnessWhileDozingConfig = resources.getBoolean(
                 com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing);
 
-        mBrightnessRampRateFast = resources.getInteger(
-                com.android.internal.R.integer.config_brightness_ramp_rate_fast);
-        mBrightnessRampRateSlow = resources.getInteger(
-                com.android.internal.R.integer.config_brightness_ramp_rate_slow);
         mSkipScreenOnBrightnessRamp = resources.getBoolean(
                 com.android.internal.R.bool.config_skipScreenOnBrightnessRamp);
 
@@ -496,7 +498,7 @@
                         mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate,
                         initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
                         autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds,
-                        screenBrightnessThresholds, context.getPackageManager());
+                        screenBrightnessThresholds, context);
             } else {
                 mUseSoftwareAutoBrightnessConfig = false;
             }
@@ -519,14 +521,13 @@
                         TYPICAL_PROXIMITY_THRESHOLD);
             }
         }
-
         mCurrentScreenBrightnessSetting = getScreenBrightnessSetting();
         mScreenBrightnessForVr = getScreenBrightnessForVrSetting();
         mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
-        mTemporaryScreenBrightness = -1;
-        mPendingScreenBrightnessSetting = -1;
-        mTemporaryAutoBrightnessAdjustment = Float.NaN;
-        mPendingAutoBrightnessAdjustment = Float.NaN;
+        mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+        mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+        mTemporaryAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+        mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
 
         DisplayWhiteBalanceSettings displayWhiteBalanceSettings = null;
         DisplayWhiteBalanceController displayWhiteBalanceController = null;
@@ -681,28 +682,28 @@
         }
 
         mScreenBrightnessRampAnimator = new RampAnimator<DisplayPowerState>(
-                mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS);
+                mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT);
         mScreenBrightnessRampAnimator.setListener(mRampAnimatorListener);
 
         // Initialize screen state for battery stats.
         try {
             mBatteryStats.noteScreenState(mPowerState.getScreenState());
-            mBatteryStats.noteScreenBrightness(mPowerState.getScreenBrightness());
+            mBatteryStats.noteScreenBrightness(BrightnessSynchronizer.brightnessFloatToInt(mContext,
+                    mPowerState.getScreenBrightness()));
         } catch (RemoteException ex) {
             // same process
         }
-
         // Initialize all of the brightness tracking state
-        final float brightness = convertToNits(mPowerState.getScreenBrightness());
+        final float brightness = convertToNits(BrightnessSynchronizer.brightnessFloatToInt(mContext,
+                mPowerState.getScreenBrightness()));
         if (brightness >= 0.0f) {
             mBrightnessTracker.start(brightness);
         }
-
         mContext.getContentResolver().registerContentObserver(
-                Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS),
+                Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FLOAT),
                 false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL);
         mContext.getContentResolver().registerContentObserver(
-                Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_VR),
+                Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT),
                 false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL);
         mContext.getContentResolver().registerContentObserver(
                 Settings.System.getUriFor(Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ),
@@ -776,8 +777,9 @@
 
         // Compute the basic display state using the policy.
         // We might override this below based on other factors.
+        // Initialise brightness as invalid.
         int state;
-        int brightness = PowerManager.BRIGHTNESS_DEFAULT;
+        float brightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
         boolean performScreenOffTransition = false;
         switch (mPowerRequest.policy) {
             case DisplayPowerRequest.POLICY_OFF:
@@ -791,7 +793,7 @@
                     state = Display.STATE_DOZE;
                 }
                 if (!mAllowAutoBrightnessWhileDozingConfig) {
-                    brightness = mPowerRequest.dozeScreenBrightness;
+                    brightnessState = mPowerRequest.dozeScreenBrightness;
                     mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE);
                 }
                 break;
@@ -843,20 +845,20 @@
         animateScreenStateChange(state, performScreenOffTransition);
         state = mPowerState.getScreenState();
 
-        // Use zero brightness when screen is off.
         if (state == Display.STATE_OFF) {
-            brightness = PowerManager.BRIGHTNESS_OFF;
+            brightnessState = PowerManager.BRIGHTNESS_OFF_FLOAT;
             mBrightnessReasonTemp.setReason(BrightnessReason.REASON_SCREEN_OFF);
         }
 
         // Always use the VR brightness when in the VR state.
         if (state == Display.STATE_VR) {
-            brightness = mScreenBrightnessForVr;
+            brightnessState = mScreenBrightnessForVr;
             mBrightnessReasonTemp.setReason(BrightnessReason.REASON_VR);
         }
 
-        if (brightness < 0 && mPowerRequest.screenBrightnessOverride > 0) {
-            brightness = mPowerRequest.screenBrightnessOverride;
+        if ((Float.isNaN(brightnessState))
+                && isValidBrightnessValue(mPowerRequest.screenBrightnessOverride)) {
+            brightnessState = mPowerRequest.screenBrightnessOverride;
             mBrightnessReasonTemp.setReason(BrightnessReason.REASON_OVERRIDE);
             mAppliedScreenBrightnessOverride = true;
         } else {
@@ -867,15 +869,15 @@
                 mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state);
         final boolean autoBrightnessEnabled = mPowerRequest.useAutoBrightness
                     && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
-                    && brightness < 0
+                    && Float.isNaN(brightnessState)
                     && mAutomaticBrightnessController != null;
 
         final boolean userSetBrightnessChanged = updateUserSetScreenBrightness();
 
         // Use the temporary screen brightness if there isn't an override, either from
         // WindowManager or based on the display state.
-        if (mTemporaryScreenBrightness > 0) {
-            brightness = mTemporaryScreenBrightness;
+        if (isValidBrightnessValue(mTemporaryScreenBrightness)) {
+            brightnessState = mTemporaryScreenBrightness;
             mAppliedTemporaryBrightness = true;
             mBrightnessReasonTemp.setReason(BrightnessReason.REASON_TEMPORARY);
         } else {
@@ -898,14 +900,13 @@
             brightnessAdjustmentFlags = BrightnessReason.ADJUSTMENT_AUTO;
             mAppliedTemporaryAutoBrightnessAdjustment = false;
         }
-
         // Apply brightness boost.
         // We do this here after deciding whether auto-brightness is enabled so that we don't
         // disable the light sensor during this temporary state.  That way when boost ends we will
         // be able to resume normal auto-brightness behavior without any delay.
         if (mPowerRequest.boostScreenBrightness
-                && brightness != PowerManager.BRIGHTNESS_OFF) {
-            brightness = PowerManager.BRIGHTNESS_ON;
+                && brightnessState != PowerManager.BRIGHTNESS_OFF_FLOAT) {
+            brightnessState = PowerManager.BRIGHTNESS_MAX;
             mBrightnessReasonTemp.setReason(BrightnessReason.REASON_BOOST);
             mAppliedBrightnessBoost = true;
         } else {
@@ -914,16 +915,15 @@
 
         // If the brightness is already set then it's been overridden by something other than the
         // user, or is a temporary adjustment.
-        boolean userInitiatedChange = brightness < 0
+        boolean userInitiatedChange = (Float.isNaN(brightnessState))
                 && (autoBrightnessAdjustmentChanged || userSetBrightnessChanged);
-
         boolean hadUserBrightnessPoint = false;
         // Configure auto-brightness.
         if (mAutomaticBrightnessController != null) {
             hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints();
             mAutomaticBrightnessController.configure(autoBrightnessEnabled,
                     mBrightnessConfiguration,
-                    mLastUserSetScreenBrightness / (float) PowerManager.BRIGHTNESS_ON,
+                    mLastUserSetScreenBrightness,
                     userSetBrightnessChanged, autoBrightnessAdjustment,
                     autoBrightnessAdjustmentChanged, mPowerRequest.policy);
         }
@@ -934,17 +934,18 @@
 
         // Apply auto-brightness.
         boolean slowChange = false;
-        if (brightness < 0) {
+        if (Float.isNaN(brightnessState)) {
             float newAutoBrightnessAdjustment = autoBrightnessAdjustment;
             if (autoBrightnessEnabled) {
-                brightness = mAutomaticBrightnessController.getAutomaticScreenBrightness();
+                brightnessState = BrightnessSynchronizer.brightnessIntToFloat(
+                mContext, mAutomaticBrightnessController.getAutomaticScreenBrightness());
                 newAutoBrightnessAdjustment =
                         mAutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment();
             }
-
-            if (brightness >= 0) {
+            if (isValidBrightnessValue(brightnessState)
+                    || brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT) {
                 // Use current auto-brightness value and slowly adjust to changes.
-                brightness = clampScreenBrightness(brightness);
+                brightnessState = clampScreenBrightness(brightnessState);
                 if (mAppliedAutoBrightness && !autoBrightnessAdjustmentChanged) {
                     slowChange = true; // slowly adapt to auto-brightness
                 }
@@ -952,7 +953,7 @@
                 // before applying the low power or dim transformations so that the slider
                 // accurately represents the full possible range, even if they range changes what
                 // it means in absolute terms.
-                putScreenBrightnessSetting(brightness);
+                putScreenBrightnessSetting(brightnessState);
                 mAppliedAutoBrightness = true;
                 mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC);
             } else {
@@ -970,24 +971,25 @@
             mAppliedAutoBrightness = false;
             brightnessAdjustmentFlags = 0;
         }
-
         // Use default brightness when dozing unless overridden.
-        if (brightness < 0 && Display.isDozeState(state)) {
-            brightness = mScreenBrightnessDozeConfig;
+        if ((Float.isNaN(brightnessState))
+                && Display.isDozeState(state)) {
+            brightnessState = mScreenBrightnessDozeConfig;
             mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT);
         }
 
         // Apply manual brightness.
-        if (brightness < 0) {
-            brightness = clampScreenBrightness(mCurrentScreenBrightnessSetting);
+        if (Float.isNaN(brightnessState)) {
+            brightnessState = clampScreenBrightness(mCurrentScreenBrightnessSetting);
             mBrightnessReasonTemp.setReason(BrightnessReason.REASON_MANUAL);
         }
 
         // Apply dimming by at least some minimum amount when user activity
         // timeout is about to expire.
         if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
-            if (brightness > mScreenBrightnessRangeMinimum) {
-                brightness = Math.max(Math.min(brightness - SCREEN_DIM_MINIMUM_REDUCTION,
+            if (brightnessState > mScreenBrightnessRangeMinimum) {
+                brightnessState = Math.max(Math.min(brightnessState
+                                - SCREEN_DIM_MINIMUM_REDUCTION_FLOAT,
                         mScreenBrightnessDimConfig), mScreenBrightnessRangeMinimum);
                 mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_DIMMED);
             }
@@ -999,15 +1001,15 @@
             slowChange = false;
             mAppliedDimming = false;
         }
-
         // If low power mode is enabled, scale brightness by screenLowPowerBrightnessFactor
         // as long as it is above the minimum threshold.
         if (mPowerRequest.lowPowerMode) {
-            if (brightness > mScreenBrightnessRangeMinimum) {
+            if (brightnessState > mScreenBrightnessRangeMinimum) {
                 final float brightnessFactor =
                         Math.min(mPowerRequest.screenLowPowerBrightnessFactor, 1);
-                final int lowPowerBrightness = (int) (brightness * brightnessFactor);
-                brightness = Math.max(lowPowerBrightness, mScreenBrightnessRangeMinimum);
+                final float lowPowerBrightnessFloat = (brightnessState * brightnessFactor);
+                brightnessState = Math.max(lowPowerBrightnessFloat,
+                        mScreenBrightnessRangeMinimum);
                 mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_LOW_POWER);
             }
             if (!mAppliedLowPower) {
@@ -1025,11 +1027,12 @@
             if (mSkipScreenOnBrightnessRamp) {
                 if (state == Display.STATE_ON) {
                     if (mSkipRampState == RAMP_STATE_SKIP_NONE && mDozing) {
-                        mInitialAutoBrightness = brightness;
+                        mInitialAutoBrightness = brightnessState;
                         mSkipRampState = RAMP_STATE_SKIP_INITIAL;
                     } else if (mSkipRampState == RAMP_STATE_SKIP_INITIAL
                             && mUseSoftwareAutoBrightnessConfig
-                            && brightness != mInitialAutoBrightness) {
+                            && !BrightnessSynchronizer.floatEquals(brightnessState,
+                            mInitialAutoBrightness)) {
                         mSkipRampState = RAMP_STATE_SKIP_AUTOBRIGHT;
                     } else if (mSkipRampState == RAMP_STATE_SKIP_AUTOBRIGHT) {
                         mSkipRampState = RAMP_STATE_SKIP_NONE;
@@ -1056,9 +1059,9 @@
                     mAppliedTemporaryBrightness || mAppliedTemporaryAutoBrightnessAdjustment;
             if (initialRampSkip || hasBrightnessBuckets
                     || wasOrWillBeInVr || !isDisplayContentVisible || brightnessIsTemporary) {
-                animateScreenBrightness(brightness, 0);
+                animateScreenBrightness(brightnessState, SCREEN_ANIMATION_RATE_MINIMUM);
             } else {
-                animateScreenBrightness(brightness,
+                animateScreenBrightness(brightnessState,
                         slowChange ? mBrightnessRampRateSlow : mBrightnessRampRateFast);
             }
 
@@ -1069,14 +1072,16 @@
                     // slider event so notify as if the system changed the brightness.
                     userInitiatedChange = false;
                 }
-                notifyBrightnessChanged(brightness, userInitiatedChange, hadUserBrightnessPoint);
+                notifyBrightnessChanged(
+                        BrightnessSynchronizer.brightnessFloatToInt(mContext, brightnessState),
+                        userInitiatedChange, hadUserBrightnessPoint);
             }
 
         }
 
         // Log any changes to what is currently driving the brightness setting.
         if (!mBrightnessReasonTemp.equals(mBrightnessReason) || brightnessAdjustmentFlags != 0) {
-            Slog.v(TAG, "Brightness [" + brightness + "] reason changing to: '"
+            Slog.v(TAG, "Brightness [" + brightnessState + "] reason changing to: '"
                     + mBrightnessReasonTemp.toString(brightnessAdjustmentFlags)
                     + "', previous reason: '" + mBrightnessReason + "'.");
             mBrightnessReason.set(mBrightnessReasonTemp);
@@ -1161,9 +1166,9 @@
         msg.sendToTarget();
     }
 
-    public void setTemporaryBrightness(int brightness) {
+    public void setTemporaryBrightness(float brightness) {
         Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_BRIGHTNESS,
-                brightness, 0 /*unused*/);
+                Float.floatToIntBits(brightness), 0 /*unused*/);
         msg.sendToTarget();
     }
 
@@ -1282,24 +1287,38 @@
         mReportedScreenStateToPolicy = state;
     }
 
-    private int clampScreenBrightnessForVr(int value) {
+    private float clampScreenBrightnessForVr(float value) {
         return MathUtils.constrain(
-                value, mScreenBrightnessForVrRangeMinimum, mScreenBrightnessForVrRangeMaximum);
+                value, mScreenBrightnessForVrRangeMinimum,
+                mScreenBrightnessForVrRangeMaximum);
     }
 
-    private int clampScreenBrightness(int value) {
+    private float clampScreenBrightness(float value) {
+        if (Float.isNaN(value)) {
+            return mScreenBrightnessRangeMinimum;
+        }
         return MathUtils.constrain(
                 value, mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
     }
 
-    private void animateScreenBrightness(int target, int rate) {
+    // Checks whether the brightness is within the valid brightness range, not including the off or
+    // invalid states.
+    private boolean isValidBrightnessValue(float brightnessState) {
+        return brightnessState >= mScreenBrightnessRangeMinimum
+                && brightnessState <= mScreenBrightnessRangeMaximum;
+    }
+
+    private void animateScreenBrightness(float target, float rate) {
         if (DEBUG) {
             Slog.d(TAG, "Animating brightness: target=" + target +", rate=" + rate);
         }
         if (mScreenBrightnessRampAnimator.animateTo(target, rate)) {
-            Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", target);
+            Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", (int) target);
             try {
-                mBatteryStats.noteScreenBrightness(target);
+                // TODO(brightnessfloat): change BatteryStats to use float
+                mBatteryStats.noteScreenBrightness(
+                        BrightnessSynchronizer.brightnessFloatToInt(
+                        mContext, target));
             } catch (RemoteException ex) {
                 // same process
             }
@@ -1578,6 +1597,7 @@
 
     private void handleSettingsChange(boolean userSwitch) {
         mPendingScreenBrightnessSetting = getScreenBrightnessSetting();
+
         if (userSwitch) {
             // Don't treat user switches as user initiated change.
             mCurrentScreenBrightnessSetting = mPendingScreenBrightnessSetting;
@@ -1598,24 +1618,24 @@
         return Float.isNaN(adj) ? 0.0f : clampAutoBrightnessAdjustment(adj);
     }
 
-    private int getScreenBrightnessSetting() {
-        final int brightness = Settings.System.getIntForUser(mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS, mScreenBrightnessDefault,
+    private float getScreenBrightnessSetting() {
+        final float brightness = Settings.System.getFloatForUser(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_FLOAT, mScreenBrightnessDefault,
                 UserHandle.USER_CURRENT);
         return clampAbsoluteBrightness(brightness);
     }
 
-    private int getScreenBrightnessForVrSetting() {
-        final int brightness = Settings.System.getIntForUser(mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_FOR_VR, mScreenBrightnessForVrDefault,
+    private float getScreenBrightnessForVrSetting() {
+        final float brightnessFloat = Settings.System.getFloatForUser(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT, mScreenBrightnessForVrDefault,
                 UserHandle.USER_CURRENT);
-        return clampScreenBrightnessForVr(brightness);
+        return clampScreenBrightnessForVr(brightnessFloat);
     }
 
-    private void putScreenBrightnessSetting(int brightness) {
-        mCurrentScreenBrightnessSetting = brightness;
-        Settings.System.putIntForUser(mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS, brightness, UserHandle.USER_CURRENT);
+    private void putScreenBrightnessSetting(float brightnessValue) {
+        mCurrentScreenBrightnessSetting = brightnessValue;
+        Settings.System.putFloatForUser(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightnessValue, UserHandle.USER_CURRENT);
     }
 
     private void putAutoBrightnessAdjustmentSetting(float adjustment) {
@@ -1638,18 +1658,19 @@
     }
 
     private boolean updateUserSetScreenBrightness() {
-        if (mPendingScreenBrightnessSetting < 0) {
+        if ((Float.isNaN(mPendingScreenBrightnessSetting)
+                || mPendingScreenBrightnessSetting < 0.0f)) {
             return false;
         }
         if (mCurrentScreenBrightnessSetting == mPendingScreenBrightnessSetting) {
-            mPendingScreenBrightnessSetting = -1;
-            mTemporaryScreenBrightness = -1;
+            mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+            mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
             return false;
         }
         mCurrentScreenBrightnessSetting = mPendingScreenBrightnessSetting;
         mLastUserSetScreenBrightness = mPendingScreenBrightnessSetting;
-        mPendingScreenBrightnessSetting = -1;
-        mTemporaryScreenBrightness = -1;
+        mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+        mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
         return true;
     }
 
@@ -1728,8 +1749,6 @@
         pw.println("Display Power Controller Configuration:");
         pw.println("  mScreenBrightnessDozeConfig=" + mScreenBrightnessDozeConfig);
         pw.println("  mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig);
-        pw.println("  mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
-        pw.println("  mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
         pw.println("  mScreenBrightnessDefault=" + mScreenBrightnessDefault);
         pw.println("  mScreenBrightnessForVrRangeMinimum=" + mScreenBrightnessForVrRangeMinimum);
         pw.println("  mScreenBrightnessForVrRangeMaximum=" + mScreenBrightnessForVrRangeMaximum);
@@ -1737,8 +1756,6 @@
         pw.println("  mUseSoftwareAutoBrightnessConfig=" + mUseSoftwareAutoBrightnessConfig);
         pw.println("  mAllowAutoBrightnessWhileDozingConfig=" +
                 mAllowAutoBrightnessWhileDozingConfig);
-        pw.println("  mBrightnessRampRateFast=" + mBrightnessRampRateFast);
-        pw.println("  mBrightnessRampRateSlow=" + mBrightnessRampRateSlow);
         pw.println("  mSkipScreenOnBrightnessRamp=" + mSkipScreenOnBrightnessRamp);
         pw.println("  mColorFadeFadesConfig=" + mColorFadeFadesConfig);
         pw.println("  mColorFadeEnabled=" + mColorFadeEnabled);
@@ -1767,15 +1784,15 @@
         pw.println("  mPendingProximityDebounceTime="
                 + TimeUtils.formatUptime(mPendingProximityDebounceTime));
         pw.println("  mScreenOffBecauseOfProximity=" + mScreenOffBecauseOfProximity);
-        pw.println("  mLastUserSetScreenBrightness=" + mLastUserSetScreenBrightness);
-        pw.println("  mCurrentScreenBrightnessSetting=" + mCurrentScreenBrightnessSetting);
-        pw.println("  mPendingScreenBrightnessSetting=" + mPendingScreenBrightnessSetting);
-        pw.println("  mTemporaryScreenBrightness=" + mTemporaryScreenBrightness);
+        pw.println("  mLastUserSetScreenBrightnessFloat=" + mLastUserSetScreenBrightness);
+        pw.println("  mPendingScreenBrightnessSettingFloat="
+                + mPendingScreenBrightnessSetting);
+        pw.println("  mTemporaryScreenBrightnessFloat=" + mTemporaryScreenBrightness);
         pw.println("  mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment);
         pw.println("  mBrightnessReason=" + mBrightnessReason);
         pw.println("  mTemporaryAutoBrightnessAdjustment=" + mTemporaryAutoBrightnessAdjustment);
         pw.println("  mPendingAutoBrightnessAdjustment=" + mPendingAutoBrightnessAdjustment);
-        pw.println("  mScreenBrightnessForVr=" + mScreenBrightnessForVr);
+        pw.println("  mScreenBrightnessForVrFloat=" + mScreenBrightnessForVr);
         pw.println("  mAppliedAutoBrightness=" + mAppliedAutoBrightness);
         pw.println("  mAppliedDimming=" + mAppliedDimming);
         pw.println("  mAppliedLowPower=" + mAppliedLowPower);
@@ -1783,7 +1800,6 @@
         pw.println("  mAppliedTemporaryBrightness=" + mAppliedTemporaryBrightness);
         pw.println("  mDozing=" + mDozing);
         pw.println("  mSkipRampState=" + skipRampStateToString(mSkipRampState));
-        pw.println("  mInitialAutoBrightness=" + mInitialAutoBrightness);
         pw.println("  mScreenOnBlockStartRealTime=" + mScreenOnBlockStartRealTime);
         pw.println("  mScreenOffBlockStartRealTime=" + mScreenOffBlockStartRealTime);
         pw.println("  mPendingScreenOnUnblocker=" + mPendingScreenOnUnblocker);
@@ -1869,6 +1885,11 @@
         return MathUtils.constrain(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
     }
 
+    private static float clampAbsoluteBrightness(float value) {
+        return MathUtils.constrain(value, PowerManager.BRIGHTNESS_MIN,
+                PowerManager.BRIGHTNESS_MAX);
+    }
+
     private static float clampAutoBrightnessAdjustment(float value) {
         return MathUtils.constrain(value, -1.0f, 1.0f);
     }
@@ -1908,7 +1929,7 @@
 
                 case MSG_SET_TEMPORARY_BRIGHTNESS:
                     // TODO: Should we have a a timeout for the temporary brightness?
-                    mTemporaryScreenBrightness = msg.arg1;
+                    mTemporaryScreenBrightness = Float.intBitsToFloat(msg.arg1);
                     updatePowerState();
                     break;
 
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index 763f56f..24e1b4e 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -22,11 +22,12 @@
 import android.os.PowerManager;
 import android.os.Trace;
 import android.util.FloatProperty;
-import android.util.IntProperty;
 import android.util.Slog;
 import android.view.Choreographer;
 import android.view.Display;
 
+import com.android.internal.BrightnessSynchronizer;
+
 import java.io.PrintWriter;
 
 /**
@@ -59,7 +60,7 @@
     private final PhotonicModulator mPhotonicModulator;
 
     private int mScreenState;
-    private int mScreenBrightness;
+    private float mScreenBrightness;
     private boolean mScreenReady;
     private boolean mScreenUpdatePending;
 
@@ -85,7 +86,7 @@
         // will reset the brightness to a new level immediately before the changes
         // actually have a chance to be applied.
         mScreenState = Display.STATE_ON;
-        mScreenBrightness = PowerManager.BRIGHTNESS_ON;
+        mScreenBrightness = PowerManager.BRIGHTNESS_MAX;
         scheduleScreenUpdate();
 
         mColorFadePrepared = false;
@@ -106,18 +107,19 @@
         }
     };
 
-    public static final IntProperty<DisplayPowerState> SCREEN_BRIGHTNESS =
-            new IntProperty<DisplayPowerState>("screenBrightness") {
-        @Override
-        public void setValue(DisplayPowerState object, int value) {
-            object.setScreenBrightness(value);
-        }
 
-        @Override
-        public Integer get(DisplayPowerState object) {
-            return object.getScreenBrightness();
-        }
-    };
+    public static final FloatProperty<DisplayPowerState> SCREEN_BRIGHTNESS_FLOAT =
+            new FloatProperty<DisplayPowerState>("screenBrightnessFloat") {
+                @Override
+                public void setValue(DisplayPowerState object, float value) {
+                    object.setScreenBrightness(value);
+                }
+
+                @Override
+                public Float get(DisplayPowerState object) {
+                    return object.getScreenBrightness();
+                }
+            };
 
     /**
      * Sets whether the screen is on, off, or dozing.
@@ -146,7 +148,7 @@
      *
      * @param brightness The brightness, ranges from 0 (minimum / off) to 255 (brightest).
      */
-    public void setScreenBrightness(int brightness) {
+    public void setScreenBrightness(float brightness) {
         if (mScreenBrightness != brightness) {
             if (DEBUG) {
                 Slog.d(TAG, "setScreenBrightness: brightness=" + brightness);
@@ -163,7 +165,7 @@
     /**
      * Gets the screen brightness.
      */
-    public int getScreenBrightness() {
+    public float getScreenBrightness() {
         return mScreenBrightness;
     }
 
@@ -308,9 +310,9 @@
         public void run() {
             mScreenUpdatePending = false;
 
-            int brightness = mScreenState != Display.STATE_OFF
-                    && mColorFadeLevel > 0f ? mScreenBrightness : 0;
-            if (mPhotonicModulator.setState(mScreenState, brightness)) {
+            float brightnessState = mScreenState != Display.STATE_OFF
+                    && mColorFadeLevel > 0f ? mScreenBrightness : PowerManager.BRIGHTNESS_OFF_FLOAT;
+            if (mPhotonicModulator.setState(mScreenState, brightnessState)) {
                 if (DEBUG) {
                     Slog.d(TAG, "Screen ready");
                 }
@@ -345,14 +347,14 @@
      */
     private final class PhotonicModulator extends Thread {
         private static final int INITIAL_SCREEN_STATE = Display.STATE_OFF; // unknown, assume off
-        private static final int INITIAL_BACKLIGHT = -1; // unknown
+        private static final float INITIAL_BACKLIGHT_FLOAT = PowerManager.BRIGHTNESS_INVALID_FLOAT;
 
         private final Object mLock = new Object();
 
         private int mPendingState = INITIAL_SCREEN_STATE;
-        private int mPendingBacklight = INITIAL_BACKLIGHT;
+        private float mPendingBacklight = INITIAL_BACKLIGHT_FLOAT;
         private int mActualState = INITIAL_SCREEN_STATE;
-        private int mActualBacklight = INITIAL_BACKLIGHT;
+        private float mActualBacklight = INITIAL_BACKLIGHT_FLOAT;
         private boolean mStateChangeInProgress;
         private boolean mBacklightChangeInProgress;
 
@@ -360,19 +362,19 @@
             super("PhotonicModulator");
         }
 
-        public boolean setState(int state, int backlight) {
+        public boolean setState(int state, float brightnessState) {
             synchronized (mLock) {
                 boolean stateChanged = state != mPendingState;
-                boolean backlightChanged = backlight != mPendingBacklight;
+                boolean backlightChanged = !BrightnessSynchronizer.floatEquals(
+                        brightnessState, mPendingBacklight);
                 if (stateChanged || backlightChanged) {
                     if (DEBUG) {
                         Slog.d(TAG, "Requesting new screen state: state="
-                                + Display.stateToString(state) + ", backlight=" + backlight);
+                                + Display.stateToString(state) + ", backlight=" + brightnessState);
                     }
 
                     mPendingState = state;
-                    mPendingBacklight = backlight;
-
+                    mPendingBacklight = brightnessState;
                     boolean changeInProgress = mStateChangeInProgress || mBacklightChangeInProgress;
                     mStateChangeInProgress = stateChanged || mStateChangeInProgress;
                     mBacklightChangeInProgress = backlightChanged || mBacklightChangeInProgress;
@@ -404,13 +406,14 @@
                 // Get pending change.
                 final int state;
                 final boolean stateChanged;
-                final int backlight;
+                final float brightnessState;
                 final boolean backlightChanged;
                 synchronized (mLock) {
                     state = mPendingState;
                     stateChanged = (state != mActualState);
-                    backlight = mPendingBacklight;
-                    backlightChanged = (backlight != mActualBacklight);
+                    brightnessState = mPendingBacklight;
+                    backlightChanged = !BrightnessSynchronizer.floatEquals(
+                            brightnessState, mActualBacklight);
                     if (!stateChanged) {
                         // State changed applied, notify outer class.
                         postScreenUpdateThreadSafe();
@@ -426,15 +429,15 @@
                         continue;
                     }
                     mActualState = state;
-                    mActualBacklight = backlight;
+                    mActualBacklight = brightnessState;
                 }
 
                 // Apply pending change.
                 if (DEBUG) {
                     Slog.d(TAG, "Updating screen state: state="
-                            + Display.stateToString(state) + ", backlight=" + backlight);
+                            + Display.stateToString(state) + ", backlight=" + brightnessState);
                 }
-                mBlanker.requestDisplayState(state, backlight);
+                mBlanker.requestDisplayState(state, brightnessState);
             }
         }
     }
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index fc9542a..2b225e5 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -38,6 +38,7 @@
 import android.view.Surface;
 import android.view.SurfaceControl;
 
+import com.android.internal.BrightnessSynchronizer;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
@@ -179,7 +180,7 @@
         private DisplayDeviceInfo mInfo;
         private boolean mHavePendingChanges;
         private int mState = Display.STATE_UNKNOWN;
-        private int mBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+        private float mBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
         private int mDefaultModeId;
         private int mActiveModeId;
         private boolean mActiveModeInvalid;
@@ -574,12 +575,14 @@
         }
 
         @Override
-        public Runnable requestDisplayStateLocked(final int state, final int brightness) {
+        public Runnable requestDisplayStateLocked(final int state, final float brightnessState) {
             // Assume that the brightness is off if the display is being turned off.
-            assert state != Display.STATE_OFF || brightness == PowerManager.BRIGHTNESS_OFF;
-
+            assert state != Display.STATE_OFF || BrightnessSynchronizer.floatEquals(
+                    brightnessState, PowerManager.BRIGHTNESS_OFF_FLOAT);
             final boolean stateChanged = (mState != state);
-            final boolean brightnessChanged = (mBrightness != brightness) && mBacklight != null;
+            final boolean brightnessChanged = (!BrightnessSynchronizer.floatEquals(
+                    mBrightnessState, brightnessState))
+                    && mBacklight != null;
             if (stateChanged || brightnessChanged) {
                 final long physicalDisplayId = mPhysicalDisplayId;
                 final IBinder token = getDisplayTokenLocked();
@@ -591,7 +594,7 @@
                 }
 
                 if (brightnessChanged) {
-                    mBrightness = brightness;
+                    mBrightnessState = brightnessState;
                 }
 
                 // Defer actually setting the display state until after we have exited
@@ -630,10 +633,9 @@
                             vrModeChange = true;
                         }
 
-
                         // Apply brightness changes given that we are in a non-suspended state.
                         if (brightnessChanged || vrModeChange) {
-                            setDisplayBrightness(brightness);
+                            setDisplayBrightness(brightnessState);
                         }
 
                         // Enter the final desired state, possibly suspended.
@@ -694,7 +696,7 @@
                         }
                     }
 
-                    private void setDisplayBrightness(int brightness) {
+                    private void setDisplayBrightness(float brightness) {
                         if (DEBUG) {
                             Slog.d(TAG, "setDisplayBrightness("
                                     + "id=" + physicalDisplayId
@@ -704,26 +706,33 @@
                         Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayBrightness("
                                 + "id=" + physicalDisplayId + ", brightness=" + brightness + ")");
                         try {
-                            if (mHalBrightnessSupport) {
-                                mBacklight.setBrightnessFloat(
-                                        displayBrightnessToHalBrightness(brightness));
-                            } else {
-                                mBacklight.setBrightness(brightness);
+                            // TODO: make it float
+                            if (isHalBrightnessRangeSpecified()) {
+                                brightness = displayBrightnessToHalBrightness(
+                                        BrightnessSynchronizer.brightnessFloatToInt(getContext(),
+                                                brightness));
                             }
+                            mBacklight.setBrightness(brightness);
                             Trace.traceCounter(Trace.TRACE_TAG_POWER,
-                                    "ScreenBrightness", brightness);
+                                    "ScreenBrightness",
+                                    BrightnessSynchronizer.brightnessFloatToInt(
+                                            getContext(), brightness));
                         } finally {
                             Trace.traceEnd(Trace.TRACE_TAG_POWER);
                         }
                     }
 
+                    private boolean isHalBrightnessRangeSpecified() {
+                        return !(mSystemBrightnessToNits == null || mNitsToHalBrightness == null);
+                    }
+
                     /**
                      * Converts brightness range from the framework's brightness space to the
                      * Hal brightness space if the HAL brightness space has been provided via
                      * a display device configuration file.
                      */
                     private float displayBrightnessToHalBrightness(int brightness) {
-                        if (mSystemBrightnessToNits == null || mNitsToHalBrightness == null) {
+                        if (!isHalBrightnessRangeSpecified()) {
                             return PowerManager.BRIGHTNESS_INVALID_FLOAT;
                         }
 
@@ -887,7 +896,7 @@
             pw.println("mActiveColorMode=" + mActiveColorMode);
             pw.println("mDefaultModeId=" + mDefaultModeId);
             pw.println("mState=" + Display.stateToString(mState));
-            pw.println("mBrightness=" + mBrightness);
+            pw.println("mBrightnessState=" + mBrightnessState);
             pw.println("mBacklight=" + mBacklight);
             pw.println("mAllmSupported=" + mAllmSupported);
             pw.println("mAllmRequested=" + mAllmRequested);
diff --git a/services/core/java/com/android/server/display/RampAnimator.java b/services/core/java/com/android/server/display/RampAnimator.java
index d71269f..7916d81 100644
--- a/services/core/java/com/android/server/display/RampAnimator.java
+++ b/services/core/java/com/android/server/display/RampAnimator.java
@@ -17,21 +17,23 @@
 package com.android.server.display;
 
 import android.animation.ValueAnimator;
-import android.util.IntProperty;
+import android.util.FloatProperty;
 import android.view.Choreographer;
 
+import com.android.internal.BrightnessSynchronizer;
+
 /**
  * A custom animator that progressively updates a property value at
  * a given variable rate until it reaches a particular target value.
  */
 final class RampAnimator<T> {
     private final T mObject;
-    private final IntProperty<T> mProperty;
+    private final FloatProperty<T> mProperty;
     private final Choreographer mChoreographer;
 
-    private int mCurrentValue;
-    private int mTargetValue;
-    private int mRate;
+    private float mCurrentValue;
+    private float mTargetValue;
+    private float mRate;
 
     private boolean mAnimating;
     private float mAnimatedValue; // higher precision copy of mCurrentValue
@@ -41,7 +43,7 @@
 
     private Listener mListener;
 
-    public RampAnimator(T object, IntProperty<T> property) {
+    public RampAnimator(T object, FloatProperty<T> property) {
         mObject = object;
         mProperty = property;
         mChoreographer = Choreographer.getInstance();
@@ -57,7 +59,8 @@
      * @param rate The convergence rate in units per second, or 0 to set the value immediately.
      * @return True if the target differs from the previous target.
      */
-    public boolean animateTo(int target, int rate) {
+    public boolean animateTo(float target, float rate) {
+
         // Immediately jump to the target the first time.
         if (mFirstTime || rate <= 0) {
             if (mFirstTime || target != mCurrentValue) {
@@ -152,14 +155,12 @@
                     mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetValue);
                 }
             }
-            final int oldCurrentValue = mCurrentValue;
-            mCurrentValue = Math.round(mAnimatedValue);
-
-            if (oldCurrentValue != mCurrentValue) {
+            final float oldCurrentValue = mCurrentValue;
+            mCurrentValue = mAnimatedValue;
+            if (!BrightnessSynchronizer.floatEquals(oldCurrentValue, mCurrentValue)) {
                 mProperty.setValue(mObject, mCurrentValue);
             }
-
-            if (mTargetValue != mCurrentValue) {
+            if (!BrightnessSynchronizer.floatEquals(mTargetValue, mCurrentValue)) {
                 postAnimationCallback();
             } else {
                 mAnimating = false;
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 1ca8dd3..f4f2ead 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -265,7 +265,7 @@
         }
 
         @Override
-        public Runnable requestDisplayStateLocked(int state, int brightness) {
+        public Runnable requestDisplayStateLocked(int state, float brightnessState) {
             if (state != mDisplayState) {
                 mDisplayState = state;
                 if (state == Display.STATE_OFF) {
diff --git a/services/core/java/com/android/server/incremental/IncrementalManagerService.java b/services/core/java/com/android/server/incremental/IncrementalManagerService.java
index 64f25dd..a8121cc 100644
--- a/services/core/java/com/android/server/incremental/IncrementalManagerService.java
+++ b/services/core/java/com/android/server/incremental/IncrementalManagerService.java
@@ -142,16 +142,6 @@
         }
     }
 
-    // TODO: remove this
-    @Override
-    public void newFileForDataLoader(int mountId, byte[] fileId, byte[] metadata) {
-        IDataLoader dataLoader = mDataLoaderManager.getDataLoader(mountId);
-        if (dataLoader == null) {
-            Slog.e(TAG, "Failed to retrieve data loader for ID=" + mountId);
-            return;
-        }
-    }
-
     @Override
     public void showHealthBlockedUI(int mountId) {
         // TODO(b/136132412): implement this
diff --git a/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java b/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java
index b0e2e64..6a5e963 100644
--- a/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java
+++ b/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java
@@ -16,9 +16,7 @@
 
 package com.android.server.incremental;
 
-import static android.content.pm.InstallationFile.FILE_TYPE_OBB;
 import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
-import static android.content.pm.PackageInstaller.LOCATION_MEDIA_OBB;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -183,10 +181,8 @@
             session = packageInstaller.openSession(sessionId);
             for (int i = 0; i < numFiles; i++) {
                 InstallationFile file = installationFiles.get(i);
-                final int location = file.getFileType() == FILE_TYPE_OBB ? LOCATION_MEDIA_OBB
-                        : LOCATION_DATA_APP;
-                session.addFile(location, file.getName(), file.getSize(), file.getMetadata(),
-                        null);
+                session.addFile(file.getLocation(), file.getName(), file.getLengthBytes(),
+                        file.getMetadata(), file.getSignature());
             }
             session.commit(localReceiver.getIntentSender());
             final Intent result = localReceiver.getResult();
@@ -304,7 +300,8 @@
                     }
                     final byte[] metadata = String.valueOf(index).getBytes(
                             StandardCharsets.UTF_8);
-                    fileList.add(new InstallationFile(name, size, metadata));
+                    fileList.add(
+                            new InstallationFile(LOCATION_DATA_APP, name, size, metadata, null));
                     break;
                 }
                 default:
diff --git a/services/core/java/com/android/server/integrity/model/ComponentBitSize.java b/services/core/java/com/android/server/integrity/model/ComponentBitSize.java
index c389963..94e6708 100644
--- a/services/core/java/com/android/server/integrity/model/ComponentBitSize.java
+++ b/services/core/java/com/android/server/integrity/model/ComponentBitSize.java
@@ -29,13 +29,14 @@
     public static final int KEY_BITS = 4;
     public static final int OPERATOR_BITS = 3;
     public static final int CONNECTOR_BITS = 2;
-    public static final int SEPARATOR_BITS = 2;
+    public static final int SEPARATOR_BITS = 3;
     public static final int VALUE_SIZE_BITS = 8;
     public static final int IS_HASHED_BITS = 1;
 
     public static final int ATOMIC_FORMULA_START = 0;
     public static final int COMPOUND_FORMULA_START = 1;
     public static final int COMPOUND_FORMULA_END = 2;
+    public static final int INSTALLER_ALLOWED_BY_MANIFEST_START = 3;
 
     public static final int DEFAULT_FORMAT_VERSION = 1;
     public static final int SIGNAL_BIT = 1;
diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
index 4b8efaf..11e8d91 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
@@ -23,6 +23,7 @@
 import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS;
 import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS;
 import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.INSTALLER_ALLOWED_BY_MANIFEST_START;
 import static com.android.server.integrity.model.ComponentBitSize.IS_HASHED_BITS;
 import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS;
 import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS;
@@ -35,6 +36,7 @@
 
 import android.content.integrity.AtomicFormula;
 import android.content.integrity.CompoundFormula;
+import android.content.integrity.InstallerAllowedByManifestFormula;
 import android.content.integrity.IntegrityFormula;
 import android.content.integrity.Rule;
 
@@ -140,6 +142,8 @@
                 return parseCompoundFormula(bitInputStream);
             case COMPOUND_FORMULA_END:
                 return null;
+            case INSTALLER_ALLOWED_BY_MANIFEST_START:
+                return new InstallerAllowedByManifestFormula();
             default:
                 throw new IllegalArgumentException(
                         String.format("Unknown formula separator: %s", separator));
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
index 00e0545..8ba5870 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
@@ -23,6 +23,7 @@
 import static com.android.server.integrity.model.ComponentBitSize.DEFAULT_FORMAT_VERSION;
 import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS;
 import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.INSTALLER_ALLOWED_BY_MANIFEST_START;
 import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS;
 import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS;
 import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS;
@@ -36,6 +37,7 @@
 
 import android.content.integrity.AtomicFormula;
 import android.content.integrity.CompoundFormula;
+import android.content.integrity.InstallerAllowedByManifestFormula;
 import android.content.integrity.IntegrityFormula;
 import android.content.integrity.IntegrityUtils;
 import android.content.integrity.Rule;
@@ -202,6 +204,8 @@
             serializeAtomicFormula((AtomicFormula) formula, bitOutputStream);
         } else if (formula instanceof CompoundFormula) {
             serializeCompoundFormula((CompoundFormula) formula, bitOutputStream);
+        } else if (formula instanceof InstallerAllowedByManifestFormula) {
+            bitOutputStream.setNext(SEPARATOR_BITS, INSTALLER_ALLOWED_BY_MANIFEST_START);
         } else {
             throw new IllegalArgumentException(
                     String.format("Invalid formula type: %s", formula.getClass()));
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java
index 6f7d172..e723559 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java
@@ -84,6 +84,7 @@
                 return getIndexingDetailsForStringAtomicFormula(
                         (AtomicFormula.StringAtomicFormula) formula);
             case IntegrityFormula.LONG_ATOMIC_FORMULA_TAG:
+            case IntegrityFormula.INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG:
             case IntegrityFormula.BOOLEAN_ATOMIC_FORMULA_TAG:
                 // Package name and app certificate related formulas are string atomic formulas.
                 return new RuleIndexingDetails(NOT_INDEXED);
diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java
index 5683e69..a42dec8 100644
--- a/services/core/java/com/android/server/lights/LightsService.java
+++ b/services/core/java/com/android/server/lights/LightsService.java
@@ -40,6 +40,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
+import com.android.internal.BrightnessSynchronizer;
 import com.android.server.SystemService;
 
 import java.util.ArrayList;
@@ -249,28 +250,21 @@
         }
 
         @Override
-        public void setBrightnessFloat(float brightness) {
-            if (!Float.isNaN(brightness)) {
-                setBrightness(brightness, 0, BRIGHTNESS_MODE_USER);
-            }
-        }
-
-        @Override
-        public void setBrightness(int brightness) {
+        public void setBrightness(float brightness) {
             setBrightness(brightness, BRIGHTNESS_MODE_USER);
         }
 
         @Override
-        public void setBrightness(int brightness, int brightnessMode) {
-            setBrightness(Float.NaN, brightness, brightnessMode);
-        }
-
-        private void setBrightness(float brightnessFloat, int brightness, int brightnessMode) {
+        public void setBrightness(float brightness, int brightnessMode) {
+            if (Float.isNaN(brightness)) {
+                Slog.w(TAG, "Brightness is not valid: " + brightness);
+                return;
+            }
             synchronized (this) {
                 // LOW_PERSISTENCE cannot be manually set
                 if (brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) {
                     Slog.w(TAG, "setBrightness with LOW_PERSISTENCE unexpected #" + mHwLight.id
-                            + ": brightness=0x" + Integer.toHexString(brightness));
+                            + ": brightness=" + brightness);
                     return;
                 }
                 // Ideally, we'd like to set the brightness mode through the SF/HWC as well, but
@@ -278,6 +272,7 @@
                 // anything but USER or the device shouldBeInLowPersistenceMode().
                 if (brightnessMode == BRIGHTNESS_MODE_USER && !shouldBeInLowPersistenceMode()
                         && mSurfaceControlMaximumBrightness == 255) {
+                    // New system
                     // TODO: the last check should be mSurfaceControlMaximumBrightness != 0; the
                     // reason we enforce 255 right now is to stay consistent with the old path. In
                     // the future, the framework should be refactored so that brightness is a float
@@ -286,17 +281,12 @@
                     if (DEBUG) {
                         Slog.d(TAG, "Using new setBrightness path!");
                     }
-
-                    if (!Float.isNaN(brightnessFloat)) {
-                        SurfaceControl.setDisplayBrightness(mDisplayToken, brightnessFloat);
-                    } else if (brightness == 0) {
-                        SurfaceControl.setDisplayBrightness(mDisplayToken, -1.0f);
-                    } else {
-                        SurfaceControl.setDisplayBrightness(mDisplayToken,
-                                (float) (brightness - 1) / (mSurfaceControlMaximumBrightness - 1));
-                    }
+                    SurfaceControl.setDisplayBrightness(mDisplayToken, brightness);
                 } else {
-                    int color = brightness & 0x000000ff;
+                    // Old system
+                    int brightnessInt = BrightnessSynchronizer.brightnessFloatToInt(
+                            getContext(), brightness);
+                    int color = brightnessInt & 0x000000ff;
                     color = 0xff000000 | (color << 16) | (color << 8) | color;
                     setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode);
                 }
diff --git a/services/core/java/com/android/server/lights/LogicalLight.java b/services/core/java/com/android/server/lights/LogicalLight.java
index 33dfbb4..7491cec 100644
--- a/services/core/java/com/android/server/lights/LogicalLight.java
+++ b/services/core/java/com/android/server/lights/LogicalLight.java
@@ -57,18 +57,12 @@
     /**
      * Set the brightness of a display.
      */
-    public abstract void setBrightness(int brightness);
+    public abstract void setBrightness(float brightness);
 
     /**
      * Set the brightness and mode of a display.
      */
-    public abstract void setBrightness(int brightness, int brightnessMode);
-
-    /**
-     * Set the brightness of a display using the brightness range defines in a
-     * display-device-configuration file.
-     */
-    public abstract void setBrightnessFloat(float brightness);
+    public abstract void setBrightness(float brightness, int brightnessMode);
 
     /**
      * Set the color of a light.
diff --git a/services/core/java/com/android/server/location/GnssAntennaInfoProvider.java b/services/core/java/com/android/server/location/GnssAntennaInfoProvider.java
new file mode 100644
index 0000000..09af655
--- /dev/null
+++ b/services/core/java/com/android/server/location/GnssAntennaInfoProvider.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.content.Context;
+import android.location.GnssAntennaInfo;
+import android.location.IGnssAntennaInfoListener;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.List;
+
+/**
+ * An base implementation for GNSS antenna info provider. It abstracts out the responsibility of
+ * handling listeners, while still allowing technology specific implementations to be built.
+ *
+ * @hide
+ */
+public abstract class GnssAntennaInfoProvider
+        extends RemoteListenerHelper<Void, IGnssAntennaInfoListener> {
+    private static final String TAG = "GnssAntennaInfoProvider";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private final GnssAntennaInfoProviderNative mNative;
+
+    private boolean mIsListeningStarted;
+
+    protected GnssAntennaInfoProvider(Context context, Handler handler) {
+        this(context, handler, new GnssAntennaInfoProviderNative());
+    }
+
+    @VisibleForTesting
+    public GnssAntennaInfoProvider(
+            Context context, Handler handler, GnssAntennaInfoProviderNative aNative) {
+        super(context, handler, TAG);
+        mNative = aNative;
+    }
+
+    void resumeIfStarted() {
+        if (DEBUG) {
+            Log.d(TAG, "resumeIfStarted");
+        }
+        if (mIsListeningStarted) {
+            mNative.startAntennaInfoListening();
+        }
+    }
+
+
+    @Override
+    public boolean isAvailableInPlatform() {
+        return mNative.isAntennaInfoSupported();
+    }
+
+    @Override
+    protected int registerWithService() {
+        boolean started = mNative.startAntennaInfoListening();
+        if (started) {
+            mIsListeningStarted = true;
+            return RemoteListenerHelper.RESULT_SUCCESS;
+        }
+        return RemoteListenerHelper.RESULT_INTERNAL_ERROR;
+    }
+
+    @Override
+    protected void unregisterFromService() {
+        boolean stopped = mNative.stopAntennaInfoListening();
+        if (stopped) {
+            mIsListeningStarted = false;
+        }
+    }
+
+    /** Handle GNSS capabilities update from the GNSS HAL implementation. */
+    public void onCapabilitiesUpdated(boolean isAntennaInfoSupported) {
+        setSupported(isAntennaInfoSupported);
+        updateResult();
+    }
+
+    /** Handle GNSS enabled changes.*/
+    public void onGpsEnabledChanged() {
+        tryUpdateRegistrationWithService();
+        updateResult();
+    }
+
+    @Override
+    protected ListenerOperation<IGnssAntennaInfoListener> getHandlerOperation(int result) {
+        int status;
+        switch (result) {
+            case RESULT_SUCCESS:
+                status = GnssAntennaInfo.Callback.STATUS_READY;
+                break;
+            case RESULT_NOT_AVAILABLE:
+            case RESULT_NOT_SUPPORTED:
+            case RESULT_INTERNAL_ERROR:
+                status = GnssAntennaInfo.Callback.STATUS_NOT_SUPPORTED;
+                break;
+            case RESULT_GPS_LOCATION_DISABLED:
+                status = GnssAntennaInfo.Callback.STATUS_LOCATION_DISABLED;
+                break;
+            case RESULT_UNKNOWN:
+                return null;
+            default:
+                Log.v(TAG, "Unhandled addListener result: " + result);
+                return null;
+        }
+        return new StatusChangedOperation(status);
+    }
+
+    private static class StatusChangedOperation
+            implements ListenerOperation<IGnssAntennaInfoListener> {
+        private final int mStatus;
+
+        StatusChangedOperation(int status) {
+            mStatus = status;
+        }
+
+        @Override
+        public void execute(IGnssAntennaInfoListener listener,
+                CallerIdentity callerIdentity) throws RemoteException {
+            listener.onStatusChanged(mStatus);
+        }
+    }
+
+    /** Handle Gnss Antenna Info report. */
+    public void onGnssAntennaInfoAvailable(final List<GnssAntennaInfo> gnssAntennaInfos) {
+        foreach((IGnssAntennaInfoListener listener, CallerIdentity callerIdentity) -> {
+            if (!hasPermission(mContext, callerIdentity)) {
+                logPermissionDisabledEventNotReported(
+                        TAG, callerIdentity.mPackageName, "GNSS antenna info");
+                return;
+            }
+            listener.onGnssAntennaInfoReceived(gnssAntennaInfos);
+        });
+    }
+
+    /**
+     * Wrapper class for native methods. This is mocked for testing.
+     */
+    @VisibleForTesting
+    public static class GnssAntennaInfoProviderNative {
+
+        public boolean isAntennaInfoSupported() {
+            return native_is_antenna_info_supported();
+        }
+
+        /** Start antenna info listening. */
+        public boolean startAntennaInfoListening() {
+            return native_start_antenna_info_listening();
+        }
+
+        /** Stop antenna info listening. */
+        public boolean stopAntennaInfoListening() {
+            return native_stop_antenna_info_listening();
+        }
+    }
+
+    private static native boolean native_is_antenna_info_supported();
+
+    private static native boolean native_start_antenna_info_listening();
+
+    private static native boolean native_stop_antenna_info_listening();
+}
diff --git a/services/core/java/com/android/server/location/GnssCapabilitiesProvider.java b/services/core/java/com/android/server/location/GnssCapabilitiesProvider.java
index 88ff6e7..5c8507f 100644
--- a/services/core/java/com/android/server/location/GnssCapabilitiesProvider.java
+++ b/services/core/java/com/android/server/location/GnssCapabilitiesProvider.java
@@ -77,6 +77,9 @@
         if (hasCapability(topHalCapabilities, GnssLocationProvider.GPS_CAPABILITY_NAV_MESSAGES)) {
             gnssCapabilities |= GnssCapabilities.NAV_MESSAGES;
         }
+        if (hasCapability(topHalCapabilities, GnssLocationProvider.GPS_CAPABILITY_ANTENNA_INFO)) {
+            gnssCapabilities |= GnssCapabilities.ANTENNA_INFO;
+        }
 
         synchronized (this) {
             mGnssCapabilities &= ~GNSS_CAPABILITIES_TOP_HAL;
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index bcac473..36136f4 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -29,6 +29,7 @@
 import android.hardware.location.GeofenceHardwareImpl;
 import android.location.Criteria;
 import android.location.FusedBatchOptions;
+import android.location.GnssAntennaInfo;
 import android.location.GnssMeasurementsEvent;
 import android.location.GnssNavigationMessage;
 import android.location.GnssStatus;
@@ -182,6 +183,7 @@
     public static final int GPS_CAPABILITY_LOW_POWER_MODE = 0x0000100;
     public static final int GPS_CAPABILITY_SATELLITE_BLACKLIST = 0x0000200;
     public static final int GPS_CAPABILITY_MEASUREMENT_CORRECTIONS = 0x0000400;
+    public static final int GPS_CAPABILITY_ANTENNA_INFO = 0x0000800;
 
     // The AGPS SUPL mode
     private static final int AGPS_SUPL_MODE_MSA = 0x02;
@@ -397,6 +399,7 @@
     private final GnssStatusListenerHelper mGnssStatusListenerHelper;
     private final GnssMeasurementsProvider mGnssMeasurementsProvider;
     private final GnssMeasurementCorrectionsProvider mGnssMeasurementCorrectionsProvider;
+    private final GnssAntennaInfoProvider mGnssAntennaInfoProvider;
     private final GnssNavigationMessageProvider mGnssNavigationMessageProvider;
     private final LocationChangeListener mNetworkLocationListener = new NetworkLocationListener();
     private final LocationChangeListener mFusedLocationListener = new FusedLocationListener();
@@ -469,6 +472,10 @@
         return mGnssMeasurementCorrectionsProvider;
     }
 
+    public GnssAntennaInfoProvider getGnssAntennaInfoProvider() {
+        return mGnssAntennaInfoProvider;
+    }
+
     public GnssNavigationMessageProvider getGnssNavigationMessageProvider() {
         return mGnssNavigationMessageProvider;
     }
@@ -693,6 +700,13 @@
 
         mGnssMeasurementCorrectionsProvider = new GnssMeasurementCorrectionsProvider(mHandler);
 
+        mGnssAntennaInfoProvider = new GnssAntennaInfoProvider(mContext, mHandler) {
+            @Override
+            protected boolean isGpsEnabled() {
+                return GnssLocationProvider.this.isGpsEnabled();
+            }
+        };
+
         mGnssNavigationMessageProvider = new GnssNavigationMessageProvider(mContext, mHandler) {
             @Override
             protected boolean isGpsEnabled() {
@@ -992,6 +1006,7 @@
 
             mGnssMeasurementsProvider.onGpsEnabledChanged();
             mGnssNavigationMessageProvider.onGpsEnabledChanged();
+            mGnssAntennaInfoProvider.onGpsEnabledChanged();
             mGnssBatchingProvider.enable();
             if (mGnssVisibilityControl != null) {
                 mGnssVisibilityControl.onGpsEnabledChanged(/* isEnabled= */ true);
@@ -1018,6 +1033,7 @@
         // do this before releasing wakelock
         native_cleanup();
 
+        mGnssAntennaInfoProvider.onGpsEnabledChanged();
         mGnssMeasurementsProvider.onGpsEnabledChanged();
         mGnssNavigationMessageProvider.onGpsEnabledChanged();
     }
@@ -1563,6 +1579,11 @@
     }
 
     @NativeEntryPoint
+    private void reportAntennaInfo(List<GnssAntennaInfo> antennaInfos) {
+        mHandler.post(() -> mGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(antennaInfos));
+    }
+
+    @NativeEntryPoint
     private void reportNavigationMessage(GnssNavigationMessage event) {
         if (!mItarSpeedLimitExceeded) {
             // send to handler to allow native to return quickly
@@ -1585,6 +1606,8 @@
             mGnssNavigationMessageProvider.onCapabilitiesUpdated(
                     hasCapability(GPS_CAPABILITY_NAV_MESSAGES));
             restartRequests();
+            mGnssAntennaInfoProvider.onCapabilitiesUpdated(
+                    hasCapability(GPS_CAPABILITY_ANTENNA_INFO));
 
             mGnssCapabilitiesProvider.setTopHalCapabilities(mTopHalCapabilities);
         });
@@ -1606,6 +1629,7 @@
         Log.i(TAG, "restartRequests");
 
         restartLocationRequest();
+        mGnssAntennaInfoProvider.resumeIfStarted();
         mGnssMeasurementsProvider.resumeIfStarted();
         mGnssNavigationMessageProvider.resumeIfStarted();
         mGnssBatchingProvider.resumeIfStarted();
@@ -2198,6 +2222,8 @@
         s.append(" ago)").append('\n');
         s.append("mFixInterval=").append(mFixInterval).append('\n');
         s.append("mLowPowerMode=").append(mLowPowerMode).append('\n');
+        s.append("mGnssAntennaInfoProvider.isRegistered()=")
+                .append(mGnssAntennaInfoProvider.isRegistered()).append('\n');
         s.append("mGnssMeasurementsProvider.isRegistered()=")
                 .append(mGnssMeasurementsProvider.isRegistered()).append('\n');
         s.append("mGnssNavigationMessageProvider.isRegistered()=")
diff --git a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
index 55e427f..6ba5f07 100644
--- a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.location.GnssMeasurementsEvent;
+import android.location.GnssRequest;
 import android.location.IGnssMeasurementsListener;
 import android.os.Handler;
 import android.os.RemoteException;
@@ -33,14 +34,14 @@
  * @hide
  */
 public abstract class GnssMeasurementsProvider
-        extends RemoteListenerHelper<IGnssMeasurementsListener> {
-    private static final String TAG = "GnssMeasurementsProvider";
+        extends RemoteListenerHelper<GnssRequest, IGnssMeasurementsListener> {
+    private static final String TAG = "GnssMeasProvider";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final GnssMeasurementProviderNative mNative;
 
-    private boolean mIsCollectionStarted;
-    private boolean mEnableFullTracking;
+    private boolean mStartedCollection;
+    private boolean mStartedFullTracking;
 
     protected GnssMeasurementsProvider(Context context, Handler handler) {
         this(context, handler, new GnssMeasurementProviderNative());
@@ -57,8 +58,8 @@
         if (DEBUG) {
             Log.d(TAG, "resumeIfStarted");
         }
-        if (mIsCollectionStarted) {
-            mNative.startMeasurementCollection(mEnableFullTracking);
+        if (mStartedCollection) {
+            mNative.startMeasurementCollection(mStartedFullTracking);
         }
     }
 
@@ -67,18 +68,35 @@
         return mNative.isMeasurementSupported();
     }
 
-    @Override
-    protected int registerWithService() {
+    private boolean getMergedFullTracking() {
         int devOptions = Settings.Secure.getInt(mContext.getContentResolver(),
                 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0);
-        int fullTrackingToggled = Settings.Global.getInt(mContext.getContentResolver(),
+        int enableFullTracking = Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, 0);
-        boolean enableFullTracking = (devOptions == 1 /* Developer Mode enabled */)
-                && (fullTrackingToggled == 1 /* Raw Measurements Full Tracking enabled */);
+        boolean enableFullTrackingBySetting = (devOptions == 1 /* Developer Mode enabled */)
+                && (enableFullTracking == 1 /* Raw Measurements Full Tracking enabled */);
+        if (enableFullTrackingBySetting) {
+            return true;
+        }
+
+        synchronized (mListenerMap) {
+            for (IdentifiedListener identifiedListener : mListenerMap.values()) {
+                GnssRequest request = identifiedListener.getRequest();
+                if (request != null && request.isFullTracking()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    protected int registerWithService() {
+        boolean enableFullTracking = getMergedFullTracking();
         boolean result = mNative.startMeasurementCollection(enableFullTracking);
         if (result) {
-            mIsCollectionStarted = true;
-            mEnableFullTracking = enableFullTracking;
+            mStartedCollection = true;
+            mStartedFullTracking = enableFullTracking;
             return RemoteListenerHelper.RESULT_SUCCESS;
         } else {
             return RemoteListenerHelper.RESULT_INTERNAL_ERROR;
@@ -89,7 +107,7 @@
     protected void unregisterFromService() {
         boolean stopped = mNative.stopMeasurementCollection();
         if (stopped) {
-            mIsCollectionStarted = false;
+            mStartedCollection = false;
         }
     }
 
diff --git a/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java b/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
index 983d1da..fb901e8 100644
--- a/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
+++ b/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
@@ -33,7 +33,7 @@
  * @hide
  */
 public abstract class GnssNavigationMessageProvider
-        extends RemoteListenerHelper<IGnssNavigationMessageListener> {
+        extends RemoteListenerHelper<Void, IGnssNavigationMessageListener> {
     private static final String TAG = "GnssNavigationMessageProvider";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
diff --git a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
index eaf63c8..1d16c03 100644
--- a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
+++ b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
@@ -24,7 +24,8 @@
 /**
  * Implementation of a handler for {@link IGnssStatusListener}.
  */
-public abstract class GnssStatusListenerHelper extends RemoteListenerHelper<IGnssStatusListener> {
+public abstract class GnssStatusListenerHelper extends
+        RemoteListenerHelper<Void, IGnssStatusListener> {
     private static final String TAG = "GnssStatusListenerHelper";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
diff --git a/services/core/java/com/android/server/location/LocationFudger.java b/services/core/java/com/android/server/location/LocationFudger.java
index 04c7714..a069e7a 100644
--- a/services/core/java/com/android/server/location/LocationFudger.java
+++ b/services/core/java/com/android/server/location/LocationFudger.java
@@ -16,297 +16,165 @@
 
 package com.android.server.location;
 
-import android.content.Context;
-import android.database.ContentObserver;
+import android.annotation.Nullable;
 import android.location.Location;
-import android.os.Bundle;
-import android.os.Handler;
 import android.os.SystemClock;
-import android.provider.Settings;
-import android.util.Log;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.security.SecureRandom;
-
+import java.time.Clock;
+import java.util.Random;
 
 /**
- * Contains the logic to obfuscate (fudge) locations for coarse applications.
- *
- * <p>The goal is just to prevent applications with only
- * the coarse location permission from receiving a fine location.
+ * Contains the logic to obfuscate (fudge) locations for coarse applications. The goal is just to
+ * prevent applications with only the coarse location permission from receiving a fine location.
  */
 public class LocationFudger {
-    private static final boolean D = false;
-    private static final String TAG = "LocationFudge";
 
-    /**
-     * Default coarse accuracy in meters.
-     */
-    private static final float DEFAULT_ACCURACY_IN_METERS = 2000.0f;
+    // minimum accuracy a coarsened location can have
+    private static final float MIN_ACCURACY_M = 200.0f;
 
-    /**
-     * Minimum coarse accuracy in meters.
-     */
-    private static final float MINIMUM_ACCURACY_IN_METERS = 200.0f;
+    // how often random offsets are updated
+    @VisibleForTesting
+    static final long OFFSET_UPDATE_INTERVAL_MS = 60 * 60 * 1000;
 
-    /**
-     * Secure settings key for coarse accuracy.
-     */
-    private static final String COARSE_ACCURACY_CONFIG_NAME = "locationCoarseAccuracy";
-
-    /**
-     * This is the fastest interval that applications can receive coarse
-     * locations.
-     */
-    public static final long FASTEST_INTERVAL_MS = 10 * 60 * 1000;  // 10 minutes
-
-    /**
-     * The duration until we change the random offset.
-     */
-    private static final long CHANGE_INTERVAL_MS = 60 * 60 * 1000;  // 1 hour
-
-    /**
-     * The percentage that we change the random offset at every interval.
-     *
-     * <p>0.0 indicates the random offset doesn't change. 1.0
-     * indicates the random offset is completely replaced every interval.
-     */
+    // the percentage that we change the random offset at every interval. 0.0 indicates the random
+    // offset doesn't change. 1.0 indicates the random offset is completely replaced every interval
     private static final double CHANGE_PER_INTERVAL = 0.03;  // 3% change
 
-    // Pre-calculated weights used to move the random offset.
-    //
-    // The goal is to iterate on the previous offset, but keep
-    // the resulting standard deviation the same. The variance of
-    // two gaussian distributions summed together is equal to the
-    // sum of the variance of each distribution. So some quick
-    // algebra results in the following sqrt calculation to
-    // weigh in a new offset while keeping the final standard
-    // deviation unchanged.
+    // weights used to move the random offset. the goal is to iterate on the previous offset, but
+    // keep the resulting standard deviation the same. the variance of two gaussian distributions
+    // summed together is equal to the sum of the variance of each distribution. so some quick
+    // algebra results in the following sqrt calculation to weight in a new offset while keeping the
+    // final standard deviation unchanged.
     private static final double NEW_WEIGHT = CHANGE_PER_INTERVAL;
-    private static final double PREVIOUS_WEIGHT = Math.sqrt(1 - NEW_WEIGHT * NEW_WEIGHT);
+    private static final double OLD_WEIGHT = Math.sqrt(1 - NEW_WEIGHT * NEW_WEIGHT);
 
-    /**
-     * This number actually varies because the earth is not round, but
-     * 111,000 meters is considered generally acceptable.
-     */
-    private static final int APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR = 111000;
+    // this number actually varies because the earth is not round, but 111,000 meters is considered
+    // generally acceptable
+    private static final int APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR = 111_000;
 
-    /**
-     * Maximum latitude.
-     *
-     * <p>We pick a value 1 meter away from 90.0 degrees in order
-     * to keep cosine(MAX_LATITUDE) to a non-zero value, so that we avoid
-     * divide by zero fails.
-     */
-    private static final double MAX_LATITUDE = 90.0 -
-            (1.0 / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR);
+    // we pick a value 1 meter away from 90.0 degrees in order to keep cosine(MAX_LATITUDE) to a
+    // non-zero value, so that we avoid divide by zero errors
+    private static final double MAX_LATITUDE =
+            90.0 - (1.0 / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR);
 
-    private final Object mLock = new Object();
-    private final SecureRandom mRandom = new SecureRandom();
+    private final float mAccuracyM;
+    private final Clock mClock;
+    private final Random mRandom;
 
-    /**
-     * Used to monitor coarse accuracy secure setting for changes.
-     */
-    private final ContentObserver mSettingsObserver;
+    @GuardedBy("this")
+    private double mLatitudeOffsetM;
+    @GuardedBy("this")
+    private double mLongitudeOffsetM;
+    @GuardedBy("this")
+    private long mNextUpdateRealtimeMs;
 
-    /**
-     * Used to resolve coarse accuracy setting.
-     */
-    private final Context mContext;
+    @GuardedBy("this")
+    @Nullable private Location mCachedFineLocation;
+    @GuardedBy("this")
+    @Nullable private Location mCachedCoarseLocation;
 
-    // all fields below protected by mLock
-    private double mOffsetLatitudeMeters;
-    private double mOffsetLongitudeMeters;
-    private long mNextInterval;
+    public LocationFudger(float accuracyM) {
+        this(accuracyM, SystemClock.elapsedRealtimeClock(), new SecureRandom());
+    }
 
-    /**
-     * Best location accuracy allowed for coarse applications.
-     * This value should only be set by {@link #setAccuracyInMetersLocked(float)}.
-     */
-    private float mAccuracyInMeters;
+    @VisibleForTesting
+    LocationFudger(float accuracyM, Clock clock, Random random) {
+        mClock = clock;
+        mRandom = random;
+        mAccuracyM = Math.max(accuracyM, MIN_ACCURACY_M);
 
-    /**
-     * The distance between grids for snap-to-grid. See {@link #createCoarse}.
-     * This value should only be set by {@link #setAccuracyInMetersLocked(float)}.
-     */
-    private double mGridSizeInMeters;
-
-    /**
-     * Standard deviation of the (normally distributed) random offset applied
-     * to coarse locations. It does not need to be as large as
-     * {@link #COARSE_ACCURACY_METERS} because snap-to-grid is the primary obfuscation
-     * method. See further details in the implementation.
-     * This value should only be set by {@link #setAccuracyInMetersLocked(float)}.
-     */
-    private double mStandardDeviationInMeters;
-
-    public LocationFudger(Context context, Handler handler) {
-        mContext = context;
-        mSettingsObserver = new ContentObserver(handler) {
-            @Override
-            public void onChange(boolean selfChange) {
-                setAccuracyInMeters(loadCoarseAccuracy());
-            }
-        };
-        mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
-                COARSE_ACCURACY_CONFIG_NAME), false, mSettingsObserver);
-
-        float accuracy = loadCoarseAccuracy();
-        synchronized (mLock) {
-            setAccuracyInMetersLocked(accuracy);
-            mOffsetLatitudeMeters = nextOffsetLocked();
-            mOffsetLongitudeMeters = nextOffsetLocked();
-            mNextInterval = SystemClock.elapsedRealtime() + CHANGE_INTERVAL_MS;
-        }
+        mLatitudeOffsetM = nextRandomOffset();
+        mLongitudeOffsetM = nextRandomOffset();
+        mNextUpdateRealtimeMs = mClock.millis() + OFFSET_UPDATE_INTERVAL_MS;
     }
 
     /**
-     * Get the cached coarse location, or generate a new one and cache it.
+     * Create a coarse location using two technique, random offsets and snap-to-grid.
+     *
+     * First we add a random offset to mitigate against detecting grid transitions. Without a random
+     * offset it is possible to detect a user's position quite accurately when they cross a grid
+     * boundary. The random offset changes very slowly over time, to mitigate against taking many
+     * location samples and averaging them out. Second we snap-to-grid (quantize). This has the nice
+     * property of producing stable results, and mitigating against taking many samples to average
+     * out a random offset.
      */
-    public Location getOrCreate(Location location) {
-        synchronized (mLock) {
-            Location coarse = location.getExtraLocation(Location.EXTRA_COARSE_LOCATION);
-            if (coarse == null) {
-                return addCoarseLocationExtraLocked(location);
+    public Location createCoarse(Location fine) {
+        synchronized (this) {
+            if (fine == mCachedFineLocation) {
+                return new Location(mCachedCoarseLocation);
             }
-            if (coarse.getAccuracy() < mAccuracyInMeters) {
-                return addCoarseLocationExtraLocked(location);
-            }
-            return coarse;
         }
-    }
 
-    private Location addCoarseLocationExtraLocked(Location location) {
-        Location coarse = createCoarseLocked(location);
-        Bundle extras = location.getExtras();
-        if (extras == null) {
-            extras = new Bundle();
-        }
-        extras.putParcelable(Location.EXTRA_COARSE_LOCATION, coarse);
-        location.setExtras(extras);
-        return coarse;
-    }
+        // update the offsets in use
+        updateOffsets();
 
-    /**
-     * Create a coarse location.
-     *
-     * <p>Two techniques are used: random offsets and snap-to-grid.
-     *
-     * <p>First we add a random offset. This mitigates against detecting
-     * grid transitions. Without a random offset it is possible to detect
-     * a users position very accurately when they cross a grid boundary.
-     * The random offset changes very slowly over time, to mitigate against
-     * taking many location samples and averaging them out.
-     *
-     * <p>Second we snap-to-grid (quantize). This has the nice property of
-     * producing stable results, and mitigating against taking many samples
-     * to average out a random offset.
-     */
-    private Location createCoarseLocked(Location fine) {
         Location coarse = new Location(fine);
 
-        // clean all the optional information off the location, because
-        // this can leak detailed location information
+        // clear any fields that could leak more detailed location information
         coarse.removeBearing();
         coarse.removeSpeed();
         coarse.removeAltitude();
         coarse.setExtras(null);
 
-        double lat = coarse.getLatitude();
-        double lon = coarse.getLongitude();
+        double latitude = wrapLatitude(coarse.getLatitude());
+        double longitude = wrapLongitude(coarse.getLongitude());
 
-        // wrap
-        lat = wrapLatitude(lat);
-        lon = wrapLongitude(lon);
+        // add offsets - update longitude first using the non-offset latitude
+        longitude += wrapLongitude(metersToDegreesLongitude(mLongitudeOffsetM, latitude));
+        latitude += wrapLatitude(metersToDegreesLatitude(mLatitudeOffsetM));
 
-        // Step 1) apply a random offset
+        // quantize location by snapping to a grid. this is the primary means of obfuscation. it
+        // gives nice consistent results and is very effective at hiding the true location (as long
+        // as you are not sitting on a grid boundary, which the random offsets mitigate).
         //
-        // The goal of the random offset is to prevent the application
-        // from determining that the device is on a grid boundary
-        // when it crosses from one grid to the next.
-        //
-        // We apply the offset even if the location already claims to be
-        // inaccurate, because it may be more accurate than claimed.
-        updateRandomOffsetLocked();
-        // perform lon first whilst lat is still within bounds
-        lon += metersToDegreesLongitude(mOffsetLongitudeMeters, lat);
-        lat += metersToDegreesLatitude(mOffsetLatitudeMeters);
-        if (D) Log.d(TAG, String.format("applied offset of %.0f, %.0f (meters)",
-                mOffsetLongitudeMeters, mOffsetLatitudeMeters));
+        // note that we quantize the latitude first, since the longitude quantization depends on the
+        // latitude value and so leaks information about the latitude
+        double latGranularity = metersToDegreesLatitude(mAccuracyM);
+        latitude = wrapLatitude(Math.round(latitude / latGranularity) * latGranularity);
+        double lonGranularity = metersToDegreesLongitude(mAccuracyM, latitude);
+        longitude = wrapLongitude(Math.round(longitude / lonGranularity) * lonGranularity);
 
-        // wrap
-        lat = wrapLatitude(lat);
-        lon = wrapLongitude(lon);
+        coarse.setLatitude(latitude);
+        coarse.setLongitude(longitude);
+        coarse.setAccuracy(Math.max(mAccuracyM, coarse.getAccuracy()));
 
-        // Step 2) Snap-to-grid (quantize)
-        //
-        // This is the primary means of obfuscation. It gives nice consistent
-        // results and is very effective at hiding the true location
-        // (as long as you are not sitting on a grid boundary, which
-        // step 1 mitigates).
-        //
-        // Note we quantize the latitude first, since the longitude
-        // quantization depends on the latitude value and so leaks information
-        // about the latitude
-        double latGranularity = metersToDegreesLatitude(mGridSizeInMeters);
-        lat = Math.round(lat / latGranularity) * latGranularity;
-        double lonGranularity = metersToDegreesLongitude(mGridSizeInMeters, lat);
-        lon = Math.round(lon / lonGranularity) * lonGranularity;
+        synchronized (this) {
+            mCachedFineLocation = fine;
+            mCachedCoarseLocation = coarse;
+        }
 
-        // wrap again
-        lat = wrapLatitude(lat);
-        lon = wrapLongitude(lon);
-
-        // apply
-        coarse.setLatitude(lat);
-        coarse.setLongitude(lon);
-        coarse.setAccuracy(Math.max(mAccuracyInMeters, coarse.getAccuracy()));
-
-        if (D) Log.d(TAG, "fudged " + fine + " to " + coarse);
-        return coarse;
+        return new Location(mCachedCoarseLocation);
     }
 
     /**
-     * Update the random offset over time.
+     * Update the random offsets over time.
      *
-     * <p>If the random offset was new for every location
-     * fix then an application can more easily average location results
-     * over time,
-     * especially when the location is near a grid boundary. On the
-     * other hand if the random offset is constant then if an application
-     * found a way to reverse engineer the offset they would be able
-     * to detect location at grid boundaries very accurately. So
-     * we choose a random offset and then very slowly move it, to
-     * make both approaches very hard.
-     *
-     * <p>The random offset does not need to be large, because snap-to-grid
-     * is the primary obfuscation mechanism. It just needs to be large
-     * enough to stop information leakage as we cross grid boundaries.
+     * If the random offset was reset for every location fix then an application could more easily
+     * average location results over time, especially when the location is near a grid boundary. On
+     * the other hand if the random offset is constant then if an application finds a way to reverse
+     * engineer the offset they would be able to detect location at grid boundaries very accurately.
+     * So we choose a random offset and then very slowly move it, to make both approaches very hard.
+     * The random offset does not need to be large, because snap-to-grid is the primary obfuscation
+     * mechanism. It just needs to be large enough to stop information leakage as we cross grid
+     * boundaries.
      */
-    private void updateRandomOffsetLocked() {
-        long now = SystemClock.elapsedRealtime();
-        if (now < mNextInterval) {
+    private synchronized void updateOffsets() {
+        long now = mClock.millis();
+        if (now < mNextUpdateRealtimeMs) {
             return;
         }
 
-        if (D) Log.d(TAG, String.format("old offset: %.0f, %.0f (meters)",
-                mOffsetLongitudeMeters, mOffsetLatitudeMeters));
-
-        // ok, need to update the random offset
-        mNextInterval = now + CHANGE_INTERVAL_MS;
-
-        mOffsetLatitudeMeters *= PREVIOUS_WEIGHT;
-        mOffsetLatitudeMeters += NEW_WEIGHT * nextOffsetLocked();
-        mOffsetLongitudeMeters *= PREVIOUS_WEIGHT;
-        mOffsetLongitudeMeters += NEW_WEIGHT * nextOffsetLocked();
-
-        if (D) Log.d(TAG, String.format("new offset: %.0f, %.0f (meters)",
-                mOffsetLongitudeMeters, mOffsetLatitudeMeters));
+        mLatitudeOffsetM = (OLD_WEIGHT * mLatitudeOffsetM) + (NEW_WEIGHT * nextRandomOffset());
+        mLongitudeOffsetM = (OLD_WEIGHT * mLongitudeOffsetM) + (NEW_WEIGHT * nextRandomOffset());
+        mNextUpdateRealtimeMs = now + OFFSET_UPDATE_INTERVAL_MS;
     }
 
-    private double nextOffsetLocked() {
-        return mRandom.nextGaussian() * mStandardDeviationInMeters;
+    private double nextRandomOffset() {
+        return mRandom.nextGaussian() * (mAccuracyM / 4.0);
     }
 
     private static double wrapLatitude(double lat) {
@@ -334,56 +202,8 @@
         return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR;
     }
 
-    /**
-     * Requires latitude since longitudinal distances change with distance from equator.
-     */
+    // requires latitude since longitudinal distances change with distance from equator.
     private static double metersToDegreesLongitude(double distance, double lat) {
         return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR / Math.cos(Math.toRadians(lat));
     }
-
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println(String.format("offset: %.0f, %.0f (meters)", mOffsetLongitudeMeters,
-                mOffsetLatitudeMeters));
-    }
-
-    /**
-     * This is the main control: call this to set the best location accuracy
-     * allowed for coarse applications and all derived values.
-     */
-    private void setAccuracyInMetersLocked(float accuracyInMeters) {
-        mAccuracyInMeters = Math.max(accuracyInMeters, MINIMUM_ACCURACY_IN_METERS);
-        if (D) {
-            Log.d(TAG, "setAccuracyInMetersLocked: new accuracy = " + mAccuracyInMeters);
-        }
-        mGridSizeInMeters = mAccuracyInMeters;
-        mStandardDeviationInMeters = mGridSizeInMeters / 4.0;
-    }
-
-    /**
-     * Same as setAccuracyInMetersLocked without the pre-lock requirement.
-     */
-    private void setAccuracyInMeters(float accuracyInMeters) {
-        synchronized (mLock) {
-            setAccuracyInMetersLocked(accuracyInMeters);
-        }
-    }
-
-    /**
-     * Loads the coarse accuracy value from secure settings.
-     */
-    private float loadCoarseAccuracy() {
-        String newSetting = Settings.Secure.getString(mContext.getContentResolver(),
-                COARSE_ACCURACY_CONFIG_NAME);
-        if (D) {
-            Log.d(TAG, "loadCoarseAccuracy: newSetting = \"" + newSetting + "\"");
-        }
-        if (newSetting == null) {
-            return DEFAULT_ACCURACY_IN_METERS;
-        }
-        try {
-            return Float.parseFloat(newSetting);
-        } catch (NumberFormatException e) {
-            return DEFAULT_ACCURACY_IN_METERS;
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/location/MockableLocationProvider.java b/services/core/java/com/android/server/location/MockableLocationProvider.java
index b0e1330..f43669e 100644
--- a/services/core/java/com/android/server/location/MockableLocationProvider.java
+++ b/services/core/java/com/android/server/location/MockableLocationProvider.java
@@ -237,6 +237,10 @@
      * Dumps the current provider implementation.
      */
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        // holding the owner lock outside this could lead to deadlock since we don't run dump on the
+        // executor specified by the provider, we run it directly
+        Preconditions.checkState(!Thread.holdsLock(mOwnerLock));
+
         AbstractLocationProvider provider;
         synchronized (mOwnerLock) {
             provider = mProvider;
diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java
index 01522739..11f0685 100644
--- a/services/core/java/com/android/server/location/RemoteListenerHelper.java
+++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java
@@ -17,6 +17,7 @@
 package com.android.server.location;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.os.Handler;
@@ -32,9 +33,10 @@
 /**
  * A helper class that handles operations in remote listeners.
  *
+ * @param <TRequest> the type of request.
  * @param <TListener> the type of GNSS data listener.
  */
-public abstract class RemoteListenerHelper<TListener extends IInterface> {
+public abstract class RemoteListenerHelper<TRequest, TListener extends IInterface> {
 
     protected static final int RESULT_SUCCESS = 0;
     protected static final int RESULT_NOT_AVAILABLE = 1;
@@ -47,7 +49,7 @@
     protected final Handler mHandler;
     private final String mTag;
 
-    private final Map<IBinder, IdentifiedListener> mListenerMap = new HashMap<>();
+    protected final Map<IBinder, IdentifiedListener> mListenerMap = new HashMap<>();
 
     protected final Context mContext;
     protected final AppOpsManager mAppOps;
@@ -75,7 +77,8 @@
     /**
      * Adds GNSS data listener {@code listener} with caller identify {@code callerIdentify}.
      */
-    public void addListener(@NonNull TListener listener, CallerIdentity callerIdentity) {
+    public void addListener(@Nullable TRequest request, @NonNull TListener listener,
+            CallerIdentity callerIdentity) {
         Objects.requireNonNull(listener, "Attempted to register a 'null' listener.");
         IBinder binder = listener.asBinder();
         synchronized (mListenerMap) {
@@ -84,7 +87,7 @@
                 return;
             }
 
-            IdentifiedListener identifiedListener = new IdentifiedListener(listener,
+            IdentifiedListener identifiedListener = new IdentifiedListener(request, listener,
                     callerIdentity);
             mListenerMap.put(binder, identifiedListener);
 
@@ -257,14 +260,22 @@
         return RESULT_SUCCESS;
     }
 
-    private class IdentifiedListener {
+    protected class IdentifiedListener {
+        @Nullable private final TRequest mRequest;
         private final TListener mListener;
         private final CallerIdentity mCallerIdentity;
 
-        private IdentifiedListener(@NonNull TListener listener, CallerIdentity callerIdentity) {
+        private IdentifiedListener(@Nullable TRequest request, @NonNull TListener listener,
+                CallerIdentity callerIdentity) {
             mListener = listener;
+            mRequest = request;
             mCallerIdentity = callerIdentity;
         }
+
+        @Nullable
+        protected TRequest getRequest() {
+            return mRequest;
+        }
     }
 
     private class HandlerRunnable implements Runnable {
diff --git a/services/core/java/com/android/server/location/SettingsHelper.java b/services/core/java/com/android/server/location/SettingsHelper.java
index 9163490..370a3f7 100644
--- a/services/core/java/com/android/server/location/SettingsHelper.java
+++ b/services/core/java/com/android/server/location/SettingsHelper.java
@@ -23,6 +23,7 @@
 import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS;
 import static android.provider.Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST;
 import static android.provider.Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS;
+import static android.provider.Settings.Secure.LOCATION_COARSE_ACCURACY_M;
 import static android.provider.Settings.Secure.LOCATION_MODE;
 import static android.provider.Settings.Secure.LOCATION_MODE_OFF;
 
@@ -88,6 +89,7 @@
     private static final long DEFAULT_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS =
             30 * 60 * 1000;
     private static final long DEFAULT_MAX_LAST_LOCATION_AGE_MS = 20 * 60 * 1000;
+    private static final float DEFAULT_COARSE_LOCATION_ACCURACY_M = 2000.0f;
 
     private final Context mContext;
 
@@ -135,6 +137,24 @@
     }
 
     /**
+     * Set location enabled for a user.
+     */
+    public void setLocationEnabled(boolean enabled, int userId) {
+        long identity = Binder.clearCallingIdentity();
+        try {
+            Settings.Secure.putIntForUser(
+                    mContext.getContentResolver(),
+                    Settings.Secure.LOCATION_MODE,
+                    enabled
+                        ? Settings.Secure.LOCATION_MODE_ON
+                        : Settings.Secure.LOCATION_MODE_OFF,
+                    userId);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
      * Add a listener for changes to the location enabled setting. Callbacks occur on an unspecified
      * thread.
      */
@@ -250,19 +270,45 @@
      * Retrieve the background throttling proximity alert interval.
      */
     public long getBackgroundThrottleProximityAlertIntervalMs() {
-        return Settings.Global.getLong(mContext.getContentResolver(),
-                LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
-                DEFAULT_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS);
+        long identity = Binder.clearCallingIdentity();
+        try {
+            return Settings.Global.getLong(mContext.getContentResolver(),
+                    LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
+                    DEFAULT_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
      * Retrieve maximum age of the last location.
      */
     public long getMaxLastLocationAgeMs() {
-        return Settings.Global.getLong(
-                mContext.getContentResolver(),
-                LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
-                DEFAULT_MAX_LAST_LOCATION_AGE_MS);
+        long identity = Binder.clearCallingIdentity();
+        try {
+            return Settings.Global.getLong(
+                    mContext.getContentResolver(),
+                    LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
+                    DEFAULT_MAX_LAST_LOCATION_AGE_MS);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Retrieve the accuracy for coarsening location, ie, the grid size used for snap-to-grid
+     * coarsening.
+     */
+    public float getCoarseLocationAccuracyM() {
+        long identity = Binder.clearCallingIdentity();
+        try {
+            return Settings.Secure.getFloat(
+                    mContext.getContentResolver(),
+                    LOCATION_COARSE_ACCURACY_M,
+                    DEFAULT_COARSE_LOCATION_ACCURACY_M);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/location/TEST_MAPPING b/services/core/java/com/android/server/location/TEST_MAPPING
index 2e21fa6..214d2f3 100644
--- a/services/core/java/com/android/server/location/TEST_MAPPING
+++ b/services/core/java/com/android/server/location/TEST_MAPPING
@@ -1,10 +1,19 @@
 {
   "presubmit": [
     {
+      "name": "CtsLocationFineTestCases"
+    },
+    {
       "name": "CtsLocationCoarseTestCases"
     },
     {
       "name": "CtsLocationNoneTestCases"
+    },
+    {
+      "name": "FrameworksMockingServicesTests",
+      "options": [{
+        "include-filter": "com.android.server.location"
+      }]
     }
   ]
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/location/UserInfoHelper.java b/services/core/java/com/android/server/location/UserInfoHelper.java
index 94f3a88..a33e2da 100644
--- a/services/core/java/com/android/server/location/UserInfoHelper.java
+++ b/services/core/java/com/android/server/location/UserInfoHelper.java
@@ -16,6 +16,9 @@
 
 package com.android.server.location;
 
+import static android.os.UserManager.DISALLOW_SHARE_LOCATION;
+
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
@@ -23,7 +26,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.UserInfo;
 import android.os.Binder;
 import android.os.Build;
 import android.os.UserHandle;
@@ -36,6 +38,8 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.concurrent.CopyOnWriteArrayList;
 
@@ -47,15 +51,24 @@
     /**
      * Listener for current user changes.
      */
-    public interface UserChangedListener {
+    public interface UserListener {
+
+        int USER_SWITCHED = 1;
+        int USER_STARTED = 2;
+        int USER_STOPPED = 3;
+
+        @IntDef({USER_SWITCHED, USER_STARTED, USER_STOPPED})
+        @Retention(RetentionPolicy.SOURCE)
+        @interface UserChange {}
+
         /**
-         * Called when the current user changes.
+         * Called when something has changed about the given user.
          */
-        void onUserChanged(@UserIdInt int oldUserId, @UserIdInt int newUserId);
+        void onUserChanged(@UserIdInt int userId, @UserChange int change);
     }
 
     private final Context mContext;
-    private final CopyOnWriteArrayList<UserChangedListener> mListeners;
+    private final CopyOnWriteArrayList<UserListener> mListeners;
 
     @GuardedBy("this")
     @Nullable private UserManager mUserManager;
@@ -86,6 +99,8 @@
 
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
+        intentFilter.addAction(Intent.ACTION_USER_STARTED);
+        intentFilter.addAction(Intent.ACTION_USER_STOPPED);
         intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
         intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
 
@@ -96,12 +111,24 @@
                 if (action == null) {
                     return;
                 }
+                int userId;
                 switch (action) {
                     case Intent.ACTION_USER_SWITCHED:
-                        int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
-                                UserHandle.USER_NULL);
+                        userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
                         if (userId != UserHandle.USER_NULL) {
-                            onUserChanged(userId);
+                            onCurrentUserChanged(userId);
+                        }
+                        break;
+                    case Intent.ACTION_USER_STARTED:
+                        userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+                        if (userId != UserHandle.USER_NULL) {
+                            onUserChanged(userId, UserListener.USER_STARTED);
+                        }
+                        break;
+                    case Intent.ACTION_USER_STOPPED:
+                        userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+                        if (userId != UserHandle.USER_NULL) {
+                            onUserChanged(userId, UserListener.USER_STOPPED);
                         }
                         break;
                     case Intent.ACTION_MANAGED_PROFILE_ADDED:
@@ -118,18 +145,18 @@
     /**
      * Adds a listener for user changed events. Callbacks occur on an unspecified thread.
      */
-    public void addListener(UserChangedListener listener) {
+    public void addListener(UserListener listener) {
         mListeners.add(listener);
     }
 
     /**
      * Removes a listener for user changed events.
      */
-    public void removeListener(UserChangedListener listener) {
+    public void removeListener(UserListener listener) {
         mListeners.remove(listener);
     }
 
-    private void onUserChanged(@UserIdInt int newUserId) {
+    private void onCurrentUserChanged(@UserIdInt int newUserId) {
         if (newUserId == mCurrentUserId) {
             return;
         }
@@ -137,8 +164,13 @@
         int oldUserId = mCurrentUserId;
         mCurrentUserId = newUserId;
 
-        for (UserChangedListener listener : mListeners) {
-            listener.onUserChanged(oldUserId, newUserId);
+        onUserChanged(oldUserId, UserListener.USER_SWITCHED);
+        onUserChanged(newUserId, UserListener.USER_SWITCHED);
+    }
+
+    private void onUserChanged(@UserIdInt int userId, @UserListener.UserChange int change) {
+        for (UserListener listener : mListeners) {
+            listener.onUserChanged(userId, change);
         }
     }
 
@@ -151,53 +183,23 @@
     }
 
     /**
-     * Returns the user id of the current user.
+     * Returns an array of current user ids. This will always include the current user, and will
+     * also include any profiles of the current user.
      */
-    @UserIdInt
-    public int getCurrentUserId() {
-        return mCurrentUserId;
+    public int[] getCurrentUserIds() {
+        return getProfileUserIdsForParentUser(mCurrentUserId);
     }
 
     /**
      * Returns true if the given user id is either the current user or a profile of the current
      * user.
      */
-    public boolean isCurrentUserOrProfile(@UserIdInt int userId) {
+    public boolean isCurrentUserId(@UserIdInt int userId) {
         int currentUserId = mCurrentUserId;
         return userId == currentUserId || ArrayUtils.contains(
                 getProfileUserIdsForParentUser(currentUserId), userId);
     }
 
-    /**
-     * Returns the parent user id of the given user id, or the user id itself if the user id either
-     * is a parent or has no profiles.
-     */
-    @UserIdInt
-    public int getParentUserId(@UserIdInt int userId) {
-        synchronized (this) {
-            if (userId == mCachedParentUserId || ArrayUtils.contains(mCachedProfileUserIds,
-                    userId)) {
-                return mCachedParentUserId;
-            }
-
-            Preconditions.checkState(mUserManager != null);
-        }
-
-        int parentUserId;
-
-        long identity = Binder.clearCallingIdentity();
-        try {
-            UserInfo userInfo = mUserManager.getProfileParent(userId);
-            parentUserId = userInfo != null ? userInfo.id : userId;
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-
-        // force profiles into cache
-        getProfileUserIdsForParentUser(parentUserId);
-        return parentUserId;
-    }
-
     @GuardedBy("this")
     private synchronized int[] getProfileUserIdsForParentUser(@UserIdInt int parentUserId) {
         if (parentUserId != mCachedParentUserId) {
@@ -225,8 +227,22 @@
      * Dump info for debugging.
      */
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        int currentUserId = mCurrentUserId;
-        pw.println("Current User: " + currentUserId + " " + Arrays.toString(
-                getProfileUserIdsForParentUser(currentUserId)));
+        boolean systemRunning;
+        synchronized (this) {
+            systemRunning = mUserManager != null;
+        }
+
+        if (systemRunning) {
+            int[] currentUserIds = getProfileUserIdsForParentUser(mCurrentUserId);
+            pw.println("current users: " + Arrays.toString(currentUserIds));
+            for (int userId : currentUserIds) {
+                if (mUserManager.hasUserRestrictionForUser(DISALLOW_SHARE_LOCATION,
+                        UserHandle.of(userId))) {
+                    pw.println("  u" + userId + " restricted");
+                }
+            }
+        } else {
+            pw.println("current user: " + mCurrentUserId);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 2bab9fa..1039cf6 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -17,6 +17,7 @@
 package com.android.server.location.gnss;
 
 import static android.app.AppOpsManager.OP_FINE_LOCATION;
+import static android.location.LocationManager.GPS_PROVIDER;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -25,7 +26,9 @@
 import android.content.Context;
 import android.location.GnssCapabilities;
 import android.location.GnssMeasurementCorrections;
+import android.location.GnssRequest;
 import android.location.IBatchedLocationCallback;
+import android.location.IGnssAntennaInfoListener;
 import android.location.IGnssMeasurementsListener;
 import android.location.IGnssNavigationMessageListener;
 import android.location.IGnssStatusListener;
@@ -38,6 +41,7 @@
 import android.os.IInterface;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.stats.location.LocationStatsEnums;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -51,6 +55,7 @@
 import com.android.server.LocationManagerServiceUtils.LinkedListenerBase;
 import com.android.server.location.AppForegroundHelper;
 import com.android.server.location.CallerIdentity;
+import com.android.server.location.GnssAntennaInfoProvider;
 import com.android.server.location.GnssBatchingProvider;
 import com.android.server.location.GnssCapabilitiesProvider;
 import com.android.server.location.GnssLocationProvider;
@@ -87,6 +92,7 @@
     private final GnssStatusListenerHelper mGnssStatusProvider;
     private final GnssMeasurementsProvider mGnssMeasurementsProvider;
     private final GnssMeasurementCorrectionsProvider mGnssMeasurementCorrectionsProvider;
+    private final GnssAntennaInfoProvider mGnssAntennaInfoProvider;
     private final GnssNavigationMessageProvider mGnssNavigationMessageProvider;
     private final GnssLocationProvider.GnssSystemInfoProvider mGnssSystemInfoProvider;
     private final GnssLocationProvider.GnssMetricsProvider mGnssMetricsProvider;
@@ -96,15 +102,20 @@
     private final IGpsGeofenceHardware mGpsGeofenceProxy;
 
     @GuardedBy("mGnssMeasurementsListeners")
-    private final ArrayMap<IBinder, LinkedListener<IGnssMeasurementsListener>>
+    private final ArrayMap<IBinder, LinkedListener<GnssRequest, IGnssMeasurementsListener>>
             mGnssMeasurementsListeners = new ArrayMap<>();
 
+    @GuardedBy("mGnssAntennaInfoListeners")
+    private final ArrayMap<IBinder,
+            LinkedListener<Void, IGnssAntennaInfoListener>>
+            mGnssAntennaInfoListeners = new ArrayMap<>();
+
     @GuardedBy("mGnssNavigationMessageListeners")
-    private final ArrayMap<IBinder, LinkedListener<IGnssNavigationMessageListener>>
+    private final ArrayMap<IBinder, LinkedListener<Void, IGnssNavigationMessageListener>>
             mGnssNavigationMessageListeners = new ArrayMap<>();
 
     @GuardedBy("mGnssStatusListeners")
-    private final ArrayMap<IBinder, LinkedListener<IGnssStatusListener>>
+    private final ArrayMap<IBinder, LinkedListener<Void, IGnssStatusListener>>
             mGnssStatusListeners = new ArrayMap<>();
 
     @GuardedBy("this")
@@ -118,7 +129,8 @@
     @Nullable private IBatchedLocationCallback mGnssBatchingCallback;
 
     @GuardedBy("mGnssBatchingLock")
-    @Nullable private LinkedListener<IBatchedLocationCallback> mGnssBatchingDeathCallback;
+    @Nullable
+    private LinkedListener<Void, IBatchedLocationCallback> mGnssBatchingDeathCallback;
 
     @GuardedBy("mGnssBatchingLock")
     private boolean mGnssBatchingInProgress = false;
@@ -147,6 +159,7 @@
         mGnssLocationProvider = gnssLocationProvider;
         mGnssStatusProvider = mGnssLocationProvider.getGnssStatusProvider();
         mGnssMeasurementsProvider = mGnssLocationProvider.getGnssMeasurementsProvider();
+        mGnssAntennaInfoProvider = mGnssLocationProvider.getGnssAntennaInfoProvider();
         mGnssMeasurementCorrectionsProvider =
                 mGnssLocationProvider.getGnssMeasurementCorrectionsProvider();
         mGnssNavigationMessageProvider = mGnssLocationProvider.getGnssNavigationMessageProvider();
@@ -272,6 +285,7 @@
             mGnssBatchingCallback = callback;
             mGnssBatchingDeathCallback =
                     new LinkedListener<>(
+                            /* request= */ null,
                             callback,
                             "BatchedLocationCallback",
                             callerIdentity,
@@ -354,38 +368,49 @@
                     uid,
                     foreground);
         }
+        synchronized (mGnssAntennaInfoListeners) {
+            updateListenersOnForegroundChangedLocked(
+                    mGnssAntennaInfoListeners,
+                    mGnssAntennaInfoProvider,
+                    IGnssAntennaInfoListener.Stub::asInterface,
+                    uid,
+                    foreground);
+        }
     }
 
-    private <TListener extends IInterface> void updateListenersOnForegroundChangedLocked(
-            Map<IBinder, ? extends LinkedListenerBase> gnssDataListeners,
-            RemoteListenerHelper<TListener> gnssDataProvider,
+    private <TRequest, TListener extends IInterface> void updateListenersOnForegroundChangedLocked(
+            Map<IBinder, LinkedListener<TRequest, TListener>> gnssDataListeners,
+            RemoteListenerHelper<TRequest, TListener> gnssDataProvider,
             Function<IBinder, TListener> mapBinderToListener,
             int uid,
             boolean foreground) {
-        for (Map.Entry<IBinder, ? extends LinkedListenerBase> entry :
+        for (Map.Entry<IBinder, LinkedListener<TRequest, TListener>> entry :
                 gnssDataListeners.entrySet()) {
-            LinkedListenerBase linkedListener = entry.getValue();
+            LinkedListener<TRequest, TListener> linkedListener = entry.getValue();
             CallerIdentity callerIdentity = linkedListener.getCallerIdentity();
+            TRequest request = linkedListener.getRequest();
             if (callerIdentity.mUid != uid) {
                 continue;
             }
 
             TListener listener = mapBinderToListener.apply(entry.getKey());
             if (foreground || isThrottlingExempt(callerIdentity)) {
-                gnssDataProvider.addListener(listener, callerIdentity);
+                gnssDataProvider.addListener(request, listener, callerIdentity);
             } else {
                 gnssDataProvider.removeListener(listener);
             }
         }
     }
 
-    private <TListener extends IInterface> boolean addGnssDataListenerLocked(
+    private <TListener extends IInterface, TRequest> boolean addGnssDataListenerLocked(
+            @Nullable TRequest request,
             TListener listener,
             String packageName,
             @Nullable String featureId,
             @NonNull String listenerIdentifier,
-            RemoteListenerHelper<TListener> gnssDataProvider,
-            ArrayMap<IBinder, LinkedListener<TListener>> gnssDataListeners,
+            RemoteListenerHelper<TRequest, TListener> gnssDataProvider,
+            ArrayMap<IBinder,
+                    LinkedListener<TRequest, TListener>> gnssDataListeners,
             Consumer<TListener> binderDeathCallback) {
         mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
 
@@ -395,7 +420,7 @@
 
         CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(),
                 Binder.getCallingPid(), packageName, featureId, listenerIdentifier);
-        LinkedListener<TListener> linkedListener = new LinkedListener<>(listener,
+        LinkedListener<TRequest, TListener> linkedListener = new LinkedListener<>(request, listener,
                 listenerIdentifier, callerIdentity, binderDeathCallback);
         IBinder binder = listener.asBinder();
         if (!linkedListener.linkToListenerDeathNotificationLocked(binder)) {
@@ -419,15 +444,15 @@
         }
         if (mAppForegroundHelper.isAppForeground(callerIdentity.mUid)
                 || isThrottlingExempt(callerIdentity)) {
-            gnssDataProvider.addListener(listener, callerIdentity);
+            gnssDataProvider.addListener(request, listener, callerIdentity);
         }
         return true;
     }
 
-    private <TListener extends IInterface> void removeGnssDataListenerLocked(
+    private <TRequest, TListener extends IInterface> void removeGnssDataListenerLocked(
             TListener listener,
-            RemoteListenerHelper<TListener> gnssDataProvider,
-            ArrayMap<IBinder, LinkedListener<TListener>> gnssDataListeners) {
+            RemoteListenerHelper<TRequest, TListener> gnssDataProvider,
+            ArrayMap<IBinder, LinkedListener<TRequest, TListener>> gnssDataListeners) {
         if (gnssDataProvider == null) {
             Log.e(
                     TAG,
@@ -437,7 +462,7 @@
         }
 
         IBinder binder = listener.asBinder();
-        LinkedListener<TListener> linkedListener =
+        LinkedListener<TRequest, TListener> linkedListener =
                 gnssDataListeners.remove(binder);
         if (linkedListener == null) {
             return;
@@ -467,6 +492,7 @@
             @Nullable String featureId) {
         synchronized (mGnssStatusListeners) {
             return addGnssDataListenerLocked(
+                    /* request= */ null,
                     listener,
                     packageName,
                     featureId,
@@ -489,11 +515,17 @@
     /**
      * Adds a GNSS measurements listener.
      */
-    public boolean addGnssMeasurementsListener(
-            IGnssMeasurementsListener listener, String packageName, @Nullable String featureId,
+    public boolean addGnssMeasurementsListener(@Nullable GnssRequest request,
+            IGnssMeasurementsListener listener, String packageName,
+            @Nullable String featureId,
             @NonNull String listenerIdentifier) {
+        if (request != null && request.isFullTracking()) {
+            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.LOCATION_HARDWARE,
+                    null);
+        }
         synchronized (mGnssMeasurementsListeners) {
             return addGnssDataListenerLocked(
+                    request,
                     listener,
                     packageName,
                     featureId,
@@ -531,6 +563,41 @@
     }
 
     /**
+     * Adds a GNSS Antenna Info listener.
+     *
+     * @param listener    called when GNSS antenna info is received
+     * @param packageName name of requesting package
+     * @return true if listener is successfully added, false otherwise
+     */
+    public boolean addGnssAntennaInfoListener(
+            IGnssAntennaInfoListener listener, String packageName,
+            @Nullable String featureId, @NonNull String listenerIdentifier) {
+        synchronized (mGnssAntennaInfoListeners) {
+            return addGnssDataListenerLocked(
+                    /* request= */ null,
+                    listener,
+                    packageName,
+                    featureId,
+                    listenerIdentifier,
+                    mGnssAntennaInfoProvider,
+                    mGnssAntennaInfoListeners,
+                    this::removeGnssAntennaInfoListener);
+        }
+    }
+
+    /**
+     * Removes a GNSS Antenna Info listener.
+     *
+     * @param listener called when GNSS antenna info is received
+     */
+    public void removeGnssAntennaInfoListener(IGnssAntennaInfoListener listener) {
+        synchronized (mGnssAntennaInfoListeners) {
+            removeGnssDataListenerLocked(
+                    listener, mGnssAntennaInfoProvider, mGnssAntennaInfoListeners);
+        }
+    }
+
+    /**
      * Adds a GNSS navigation message listener.
      */
     public boolean addGnssNavigationMessageListener(
@@ -538,6 +605,7 @@
             @Nullable String featureId, @NonNull String listenerIdentifier) {
         synchronized (mGnssNavigationMessageListeners) {
             return addGnssDataListenerLocked(
+                    /* request= */ null,
                     listener,
                     packageName,
                     featureId,
@@ -574,18 +642,26 @@
      */
     public void onReportLocation(List<Location> locations) {
         IBatchedLocationCallback gnssBatchingCallback;
+        LinkedListener<Void, IBatchedLocationCallback> gnssBatchingDeathCallback;
         synchronized (mGnssBatchingLock) {
             gnssBatchingCallback = mGnssBatchingCallback;
+            gnssBatchingDeathCallback = mGnssBatchingDeathCallback;
         }
 
-        if (gnssBatchingCallback == null) {
+        if (gnssBatchingCallback == null || gnssBatchingDeathCallback == null) {
+            return;
+        }
+
+        int userId = UserHandle.getUserId(gnssBatchingDeathCallback.getCallerIdentity().mUid);
+        if (!mLocationManagerInternal.isProviderEnabledForUser(GPS_PROVIDER, userId)) {
+            Log.w(TAG, "reportLocationBatch() called without user permission");
             return;
         }
 
         try {
             gnssBatchingCallback.onLocationBatch(locations);
         } catch (RemoteException e) {
-            Log.e(TAG, "mGnssBatchingCallback.onLocationBatch failed", e);
+            Log.e(TAG, "reportLocationBatch() failed", e);
         }
     }
 
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index b726e57..ac49fa2 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -590,8 +590,8 @@
             throw new IllegalStateException("Failed to create new SID for user", e);
         }
         if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
-            Slog.e(TAG, "Fail to create new SID for user " + userId);
-            return;
+            throw new IllegalStateException("Fail to create new SID for user " + userId
+                    + " response: " + response.getResponseCode());
         }
         saveSyntheticPasswordHandle(response.getPayload(), userId);
     }
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 8358884..3de5cf1 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -62,7 +62,8 @@
     public abstract void transferToRoute(String sessionId, String routeId);
 
     public abstract void sendControlRequest(String routeId, Intent request);
-    public abstract void requestSetVolume(String routeId, int volume);
+    public abstract void setRouteVolume(String routeId, int volume);
+    public abstract void setSessionVolume(String sessionId, int volume);
 
     @NonNull
     public String getUniqueId() {
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index dd536ec..c1ea697 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -131,9 +131,17 @@
     }
 
     @Override
-    public void requestSetVolume(String routeId, int volume) {
+    public void setRouteVolume(String routeId, int volume) {
         if (mConnectionReady) {
-            mActiveConnection.requestSetVolume(routeId, volume);
+            mActiveConnection.setRouteVolume(routeId, volume);
+            updateBinding();
+        }
+    }
+
+    @Override
+    public void setSessionVolume(String sessionId, int volume) {
+        if (mConnectionReady) {
+            mActiveConnection.setSessionVolume(sessionId, volume);
             updateBinding();
         }
     }
@@ -456,7 +464,7 @@
             try {
                 mProvider.requestCreateSession(packageName, routeId, requestId, sessionHints);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "Failed to deliver request to create a session.", ex);
+                Slog.e(TAG, "requestCreateSession: Failed to deliver request.");
             }
         }
 
@@ -464,7 +472,7 @@
             try {
                 mProvider.releaseSession(sessionId);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "Failed to deliver request to release a session.", ex);
+                Slog.e(TAG, "releaseSession: Failed to deliver request.");
             }
         }
 
@@ -472,7 +480,7 @@
             try {
                 mProvider.updateDiscoveryPreference(discoveryPreference);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "updateDiscoveryPreference(): Failed to deliver request.");
+                Slog.e(TAG, "updateDiscoveryPreference: Failed to deliver request.");
             }
         }
 
@@ -480,7 +488,7 @@
             try {
                 mProvider.selectRoute(sessionId, routeId);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "Failed to deliver request to select a route for a session.", ex);
+                Slog.e(TAG, "selectRoute: Failed to deliver request.", ex);
             }
         }
 
@@ -488,7 +496,7 @@
             try {
                 mProvider.deselectRoute(sessionId, routeId);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "Failed to deliver request to deselect a route from a session.", ex);
+                Slog.e(TAG, "deselectRoute: Failed to deliver request.", ex);
             }
         }
 
@@ -496,7 +504,7 @@
             try {
                 mProvider.transferToRoute(sessionId, routeId);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "Failed to deliver request to transfer a session to a route.", ex);
+                Slog.e(TAG, "transferToRoute: Failed to deliver request.", ex);
             }
         }
 
@@ -504,15 +512,23 @@
             try {
                 mProvider.notifyControlRequestSent(routeId, request);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "Failed to deliver request to send control request.", ex);
+                Slog.e(TAG, "sendControlRequest: Failed to deliver request.", ex);
             }
         }
 
-        public void requestSetVolume(String routeId, int volume) {
+        public void setRouteVolume(String routeId, int volume) {
             try {
-                mProvider.requestSetVolume(routeId, volume);
+                mProvider.setRouteVolume(routeId, volume);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "Failed to deliver request to request set volume.", ex);
+                Slog.e(TAG, "setRouteVolume: Failed to deliver request.", ex);
+            }
+        }
+
+        public void setSessionVolume(String sessionId, int volume) {
+            try {
+                mProvider.setSessionVolume(sessionId, volume);
+            } catch (RemoteException ex) {
+                Slog.e(TAG, "setSessionVolume: Failed to deliver request.", ex);
             }
         }
 
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index b113322..2096531 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -320,14 +320,29 @@
         }
     }
 
-    public void requestSetVolume2(IMediaRouter2Client client, MediaRoute2Info route, int volume) {
+    public void setRouteVolume2(IMediaRouter2Client client,
+            MediaRoute2Info route, int volume) {
         Objects.requireNonNull(client, "client must not be null");
         Objects.requireNonNull(route, "route must not be null");
 
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
-                requestSetVolumeLocked(client, route, volume);
+                setRouteVolumeLocked(client, route, volume);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    public void setSessionVolume2(IMediaRouter2Client client, String sessionId, int volume) {
+        Objects.requireNonNull(client, "client must not be null");
+        Objects.requireNonNull(sessionId, "sessionId must not be null");
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                setSessionVolumeLocked(client, sessionId, volume);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -346,7 +361,7 @@
         }
     }
 
-    public void requestSetVolume2Manager(IMediaRouter2Manager manager,
+    public void setRouteVolume2Manager(IMediaRouter2Manager manager,
             MediaRoute2Info route, int volume) {
         Objects.requireNonNull(manager, "manager must not be null");
         Objects.requireNonNull(route, "route must not be null");
@@ -354,7 +369,22 @@
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
-                requestSetVolumeLocked(manager, route, volume);
+                setRouteVolumeLocked(manager, route, volume);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    public void setSessionVolume2Manager(IMediaRouter2Manager manager,
+            String sessionId, int volume) {
+        Objects.requireNonNull(manager, "manager must not be null");
+        Objects.requireNonNull(sessionId, "sessionId must not be null");
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                setSessionVolumeLocked(manager, sessionId, volume);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -587,18 +617,30 @@
         }
     }
 
-    private void requestSetVolumeLocked(IMediaRouter2Client client, MediaRoute2Info route,
+    private void setRouteVolumeLocked(IMediaRouter2Client client, MediaRoute2Info route,
             int volume) {
         final IBinder binder = client.asBinder();
         Client2Record clientRecord = mAllClientRecords.get(binder);
 
         if (clientRecord != null) {
             clientRecord.mUserRecord.mHandler.sendMessage(
-                    obtainMessage(UserHandler::requestSetVolume,
+                    obtainMessage(UserHandler::setRouteVolume,
                             clientRecord.mUserRecord.mHandler, route, volume));
         }
     }
 
+    private void setSessionVolumeLocked(IMediaRouter2Client client, String sessionId,
+            int volume) {
+        final IBinder binder = client.asBinder();
+        Client2Record clientRecord = mAllClientRecords.get(binder);
+
+        if (clientRecord != null) {
+            clientRecord.mUserRecord.mHandler.sendMessage(
+                    obtainMessage(UserHandler::setSessionVolume,
+                            clientRecord.mUserRecord.mHandler, sessionId, volume));
+        }
+    }
+
     private void registerManagerLocked(IMediaRouter2Manager manager,
             int uid, int pid, String packageName, int userId, boolean trusted) {
         final IBinder binder = manager.asBinder();
@@ -660,18 +702,30 @@
         }
     }
 
-    private void requestSetVolumeLocked(IMediaRouter2Manager manager, MediaRoute2Info route,
+    private void setRouteVolumeLocked(IMediaRouter2Manager manager, MediaRoute2Info route,
             int volume) {
         final IBinder binder = manager.asBinder();
         ManagerRecord managerRecord = mAllManagerRecords.get(binder);
 
         if (managerRecord != null) {
             managerRecord.mUserRecord.mHandler.sendMessage(
-                    obtainMessage(UserHandler::requestSetVolume,
+                    obtainMessage(UserHandler::setRouteVolume,
                             managerRecord.mUserRecord.mHandler, route, volume));
         }
     }
 
+    private void setSessionVolumeLocked(IMediaRouter2Manager manager, String sessionId,
+            int volume) {
+        final IBinder binder = manager.asBinder();
+        ManagerRecord managerRecord = mAllManagerRecords.get(binder);
+
+        if (managerRecord != null) {
+            managerRecord.mUserRecord.mHandler.sendMessage(
+                    obtainMessage(UserHandler::setSessionVolume,
+                            managerRecord.mUserRecord.mHandler, sessionId, volume));
+        }
+    }
+
     private List<RoutingSessionInfo> getActiveSessionsLocked(IMediaRouter2Manager manager) {
         final IBinder binder = manager.asBinder();
         ManagerRecord managerRecord = mAllManagerRecords.get(binder);
@@ -1399,13 +1453,23 @@
             }
         }
 
-        private void requestSetVolume(MediaRoute2Info route, int volume) {
+        private void setRouteVolume(MediaRoute2Info route, int volume) {
             final MediaRoute2Provider provider = findProvider(route.getProviderId());
             if (provider != null) {
-                provider.requestSetVolume(route.getOriginalId(), volume);
+                provider.setRouteVolume(route.getOriginalId(), volume);
             }
         }
 
+        private void setSessionVolume(String sessionId, int volume) {
+            final MediaRoute2Provider provider = findProvider(getProviderId(sessionId));
+            if (provider == null) {
+                Slog.w(TAG, "setSessionVolume: couldn't find provider for session "
+                        + "id=" + sessionId);
+                return;
+            }
+            provider.setSessionVolume(getOriginalId(sessionId), volume);
+        }
+
         private List<IMediaRouter2Client> getClients() {
             final List<IMediaRouter2Client> clients = new ArrayList<>();
             MediaRouter2ServiceImpl service = mServiceRef.get();
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 57f0328..b38e47a 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -533,15 +533,29 @@
 
     // Binder call
     @Override
-    public void requestSetVolume2(IMediaRouter2Client client, MediaRoute2Info route, int volume) {
-        mService2.requestSetVolume2(client, route, volume);
+    public void setRouteVolume2(IMediaRouter2Client client,
+            MediaRoute2Info route, int volume) {
+        mService2.setRouteVolume2(client, route, volume);
     }
 
     // Binder call
     @Override
-    public void requestSetVolume2Manager(IMediaRouter2Manager manager,
+    public void setSessionVolume2(IMediaRouter2Client client, String sessionId, int volume) {
+        mService2.setSessionVolume2(client, sessionId, volume);
+    }
+
+    // Binder call
+    @Override
+    public void setRouteVolume2Manager(IMediaRouter2Manager manager,
             MediaRoute2Info route, int volume) {
-        mService2.requestSetVolume2Manager(manager, route, volume);
+        mService2.setRouteVolume2Manager(manager, route, volume);
+    }
+
+    // Binder call
+    @Override
+    public void setSessionVolume2Manager(IMediaRouter2Manager manager,
+            String sessionId, int volume) {
+        mService2.setSessionVolume2Manager(manager, sessionId, volume);
     }
 
     // Binder call
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index b5dcea8..18383c4 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -42,6 +42,7 @@
 import android.util.Log;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
 
 import java.util.Collections;
 import java.util.List;
@@ -67,6 +68,8 @@
             SystemMediaRoute2Provider.class.getPackageName$(),
             SystemMediaRoute2Provider.class.getName());
 
+    @GuardedBy("mLock")
+    private String mSelectedRouteId;
     MediaRoute2Info mDefaultRoute;
     @NonNull List<MediaRoute2Info> mBluetoothRoutes = Collections.EMPTY_LIST;
     final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
@@ -153,9 +156,17 @@
     public void sendControlRequest(@NonNull String routeId, @NonNull Intent request) {
     }
 
-    //TODO: implement method
     @Override
-    public void requestSetVolume(String routeId, int volume) {
+    public void setRouteVolume(String routeId, int volume) {
+        if (!TextUtils.equals(routeId, mSelectedRouteId)) {
+            return;
+        }
+        mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+    }
+
+    @Override
+    public void setSessionVolume(String sessionId, int volume) {
+        // Do nothing since we don't support grouping volume yet.
     }
 
     private void initializeDefaultRoute() {
@@ -241,18 +252,16 @@
                 SYSTEM_SESSION_ID, "" /* clientPackageName */)
                 .setSystemSession(true);
         String activeBtDeviceAddress = mBtRouteProvider.getActiveDeviceAddress();
+        mSelectedRouteId = TextUtils.isEmpty(activeBtDeviceAddress) ? mDefaultRoute.getId()
+                : activeBtDeviceAddress;
+        builder.addSelectedRoute(mSelectedRouteId);
 
         if (!TextUtils.isEmpty(activeBtDeviceAddress)) {
-            // Bluetooth route. Set the route ID with the device's address.
-            builder.addSelectedRoute(activeBtDeviceAddress);
             builder.addTransferrableRoute(mDefaultRoute.getId());
-        } else {
-            // Default device
-            builder.addSelectedRoute(mDefaultRoute.getId());
         }
 
         for (MediaRoute2Info route : mBluetoothRoutes) {
-            if (!TextUtils.equals(activeBtDeviceAddress, route.getId())) {
+            if (!TextUtils.equals(mSelectedRouteId, route.getId())) {
                 builder.addTransferrableRoute(route.getId());
             }
         }
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 1dcff07..bde9ee2 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -1610,6 +1610,22 @@
             pw.decreaseIndent();
             pw.println();
 
+            pw.println("Stats Providers:");
+            pw.increaseIndent();
+            invokeForAllStatsProviderCallbacks((cb) -> {
+                pw.println(cb.mTag + " Xt:");
+                pw.increaseIndent();
+                pw.print(cb.getCachedStats(STATS_PER_IFACE).toString());
+                pw.decreaseIndent();
+                if (includeUid) {
+                    pw.println(cb.mTag + " Uid:");
+                    pw.increaseIndent();
+                    pw.print(cb.getCachedStats(STATS_PER_UID).toString());
+                    pw.decreaseIndent();
+                }
+            });
+            pw.decreaseIndent();
+
             pw.println("Dev stats:");
             pw.increaseIndent();
             mDevRecorder.dumpLocked(pw, fullHistory);
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
index 061cbd8..dc61fb0 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
@@ -231,6 +231,9 @@
 
     public void disableHistory() {
         synchronized (mLock) {
+            for (AtomicFile file : mHistoryFiles) {
+                file.delete();
+            }
             mHistoryDir.delete();
             mHistoryFiles.clear();
         }
@@ -249,6 +252,10 @@
                 final AtomicFile currentOldestFile = mHistoryFiles.get(i);
                 final long creationTime =
                         mFileAttrProvider.getCreationTime(currentOldestFile.getBaseFile());
+                if (DEBUG) {
+                    Slog.d(TAG, "Pruning " + currentOldestFile.getBaseFile().getName()
+                            + " created on " + creationTime);
+                }
                 if (creationTime <= retentionBoundary.getTimeInMillis()) {
                     if (DEBUG) {
                         Slog.d(TAG, "Removed " + currentOldestFile.getBaseFile().getName());
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7ffd899..916b63b 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -94,6 +94,9 @@
 
 import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
 import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
 import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
 import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
 import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER;
@@ -129,6 +132,7 @@
 import android.app.PendingIntent;
 import android.app.Person;
 import android.app.RemoteInput;
+import android.app.StatsManager;
 import android.app.StatusBarManager;
 import android.app.UriGrantsManager;
 import android.app.admin.DeviceAdminInfo;
@@ -185,6 +189,7 @@
 import android.os.ShellCallback;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.VibrationEffect;
@@ -220,6 +225,7 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.StatsEvent;
 import android.util.Xml;
 import android.util.proto.ProtoOutputStream;
 import android.view.accessibility.AccessibilityEvent;
@@ -473,6 +479,8 @@
     private AppOpsManager mAppOps;
     private UsageStatsManagerInternal mAppUsageStats;
     private DevicePolicyManagerInternal mDpm;
+    private StatsManager mStatsManager;
+    private StatsPullAtomCallbackImpl mPullAtomCallback;
 
     private Archive mArchive;
 
@@ -527,11 +535,11 @@
 
     private static class Archive {
         final int mBufferSize;
-        final ArrayDeque<StatusBarNotification> mBuffer;
+        final ArrayDeque<Pair<StatusBarNotification, Integer>> mBuffer;
 
         public Archive(int size) {
             mBufferSize = size;
-            mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize);
+            mBuffer = new ArrayDeque<>(mBufferSize);
         }
 
         public String toString() {
@@ -544,7 +552,7 @@
             return sb.toString();
         }
 
-        public void record(StatusBarNotification nr) {
+        public void record(StatusBarNotification nr, int reason) {
             if (mBuffer.size() == mBufferSize) {
                 mBuffer.removeFirst();
             }
@@ -552,21 +560,24 @@
             // We don't want to store the heavy bits of the notification in the archive,
             // but other clients in the system process might be using the object, so we
             // store a (lightened) copy.
-            mBuffer.addLast(nr.cloneLight());
+            mBuffer.addLast(new Pair<>(nr.cloneLight(), reason));
         }
 
-        public Iterator<StatusBarNotification> descendingIterator() {
+        public Iterator<Pair<StatusBarNotification, Integer>> descendingIterator() {
             return mBuffer.descendingIterator();
         }
 
-        public StatusBarNotification[] getArray(int count) {
+        public StatusBarNotification[] getArray(int count, boolean includeSnoozed) {
             if (count == 0) count = mBufferSize;
             final StatusBarNotification[] a
                     = new StatusBarNotification[Math.min(count, mBuffer.size())];
-            Iterator<StatusBarNotification> iter = descendingIterator();
+            Iterator<Pair<StatusBarNotification, Integer>> iter = descendingIterator();
             int i=0;
             while (iter.hasNext() && i < count) {
-                a[i++] = iter.next();
+                Pair<StatusBarNotification, Integer> pair = iter.next();
+                if (pair.second != REASON_SNOOZED || includeSnoozed) {
+                    a[i++] = pair.first;
+                }
             }
             return a;
         }
@@ -1890,7 +1901,8 @@
             ActivityManager activityManager, GroupHelper groupHelper, IActivityManager am,
             UsageStatsManagerInternal appUsageStats, DevicePolicyManagerInternal dpm,
             IUriGrantsManager ugm, UriGrantsManagerInternal ugmInternal, AppOpsManager appOps,
-            UserManager userManager, NotificationHistoryManager historyManager) {
+            UserManager userManager, NotificationHistoryManager historyManager,
+            StatsManager statsManager) {
         mHandler = handler;
         Resources resources = getContext().getResources();
         mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
@@ -2050,6 +2062,7 @@
         mStripRemoteViewsSizeBytes = getContext().getResources().getInteger(
                 com.android.internal.R.integer.config_notificationStripRemoteViewSizeBytes);
 
+        mStatsManager = statsManager;
     }
 
     @Override
@@ -2093,7 +2106,9 @@
                 LocalServices.getService(UriGrantsManagerInternal.class),
                 (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE),
                 getContext().getSystemService(UserManager.class),
-                new NotificationHistoryManager(getContext(), handler));
+                new NotificationHistoryManager(getContext(), handler),
+                mStatsManager = (StatsManager) getContext().getSystemService(
+                        Context.STATS_MANAGER));
 
         // register for various Intents
         IntentFilter filter = new IntentFilter();
@@ -2168,6 +2183,57 @@
         }
     }
 
+    private void registerNotificationPreferencesPullers() {
+        mPullAtomCallback = new StatsPullAtomCallbackImpl();
+        mStatsManager.registerPullAtomCallback(
+                PACKAGE_NOTIFICATION_PREFERENCES,
+                null, // use default PullAtomMetadata values
+                BackgroundThread.getExecutor(),
+                mPullAtomCallback
+        );
+        mStatsManager.registerPullAtomCallback(
+                PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES,
+                null, // use default PullAtomMetadata values
+                BackgroundThread.getExecutor(),
+                mPullAtomCallback
+        );
+        mStatsManager.registerPullAtomCallback(
+                PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES,
+                null, // use default PullAtomMetadata values
+                BackgroundThread.getExecutor(),
+                mPullAtomCallback
+        );
+    }
+
+    private class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCallback {
+        @Override
+        public int onPullAtom(int atomTag, List<StatsEvent> data) {
+            switch (atomTag) {
+                case PACKAGE_NOTIFICATION_PREFERENCES:
+                case PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES:
+                case PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES:
+                    return pullNotificationStates(atomTag, data);
+                default:
+                    throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
+            }
+        }
+    }
+
+    private int pullNotificationStates(int atomTag, List<StatsEvent> data) {
+        switch(atomTag) {
+            case PACKAGE_NOTIFICATION_PREFERENCES:
+                mPreferencesHelper.pullPackagePreferencesStats(data);
+                break;
+            case PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES:
+                mPreferencesHelper.pullPackageChannelPreferencesStats(data);
+                break;
+            case PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES:
+                mPreferencesHelper.pullPackageChannelGroupPreferencesStats(data);
+                break;
+        }
+        return StatsManager.PULL_SUCCESS;
+    }
+
     private GroupHelper getGroupHelper() {
         mAutoGroupAtCount =
                 getContext().getResources().getInteger(R.integer.config_autoGroupAtCount);
@@ -2246,6 +2312,7 @@
             mRoleObserver.init();
             mLauncherAppsService =
                     (LauncherApps) getContext().getSystemService(Context.LAUNCHER_APPS_SERVICE);
+            registerNotificationPreferencesPullers();
         } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
             // This observer will force an update when observe is called, causing us to
             // bind to listener services.
@@ -2260,12 +2327,26 @@
 
     @Override
     public void onUnlockUser(@NonNull UserInfo userInfo) {
-        mHandler.post(() -> mHistoryManager.onUserUnlocked(userInfo.id));
+        mHandler.post(() -> {
+            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "notifHistoryUnlockUser");
+            try {
+                mHistoryManager.onUserUnlocked(userInfo.id);
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+            }
+        });
     }
 
     @Override
     public void onStopUser(@NonNull UserInfo userInfo) {
-        mHandler.post(() -> mHistoryManager.onUserStopped(userInfo.id));
+        mHandler.post(() -> {
+            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "notifHistoryStopUser");
+            try {
+                mHistoryManager.onUserStopped(userInfo.id);
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+            }
+        });
     }
 
     @GuardedBy("mNotificationLock")
@@ -2592,17 +2673,22 @@
             mAppUsageStats.reportInterruptiveNotification(r.getSbn().getPackageName(),
                     r.getChannel().getId(),
                     getRealUserId(r.getSbn().getUserId()));
-            mHistoryManager.addNotification(new HistoricalNotification.Builder()
-                    .setPackage(r.getSbn().getPackageName())
-                    .setUid(r.getSbn().getUid())
-                    .setChannelId(r.getChannel().getId())
-                    .setChannelName(r.getChannel().getName().toString())
-                    .setPostedTimeMs(System.currentTimeMillis())
-                    .setTitle(getHistoryTitle(r.getNotification()))
-                    .setText(getHistoryText(
-                            r.getSbn().getPackageContext(getContext()), r.getNotification()))
-                    .setIcon(r.getNotification().getSmallIcon())
-                    .build());
+            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "notifHistoryAddItem");
+            try {
+                mHistoryManager.addNotification(new HistoricalNotification.Builder()
+                        .setPackage(r.getSbn().getPackageName())
+                        .setUid(r.getSbn().getUid())
+                        .setChannelId(r.getChannel().getId())
+                        .setChannelName(r.getChannel().getName().toString())
+                        .setPostedTimeMs(System.currentTimeMillis())
+                        .setTitle(getHistoryTitle(r.getNotification()))
+                        .setText(getHistoryText(
+                                r.getSbn().getPackageContext(getContext()), r.getNotification()))
+                        .setIcon(r.getNotification().getSmallIcon())
+                        .build());
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+            }
             r.setRecordedInterruption(true);
         }
     }
@@ -3638,7 +3724,8 @@
          */
         @Override
         @RequiresPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS)
-        public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
+        public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count,
+                boolean includeSnoozed) {
             // enforce() will ensure the calling uid has the correct permission
             getContext().enforceCallingOrSelfPermission(
                     android.Manifest.permission.ACCESS_NOTIFICATIONS,
@@ -3651,7 +3738,7 @@
             if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
                     == AppOpsManager.MODE_ALLOWED) {
                 synchronized (mArchive) {
-                    tmp = mArchive.getArray(count);
+                    tmp = mArchive.getArray(count, includeSnoozed);
                 }
             }
             return tmp;
@@ -3675,7 +3762,12 @@
             if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
                     == AppOpsManager.MODE_ALLOWED) {
                 IntArray currentUserIds = mUserProfiles.getCurrentProfileIds();
-                return mHistoryManager.readNotificationHistory(currentUserIds.toArray());
+                Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "notifHistoryReadHistory");
+                try {
+                    return mHistoryManager.readNotificationHistory(currentUserIds.toArray());
+                } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+                }
             }
             return new NotificationHistory();
         }
@@ -5199,10 +5291,10 @@
                     pw.println("  mMaxPackageEnqueueRate=" + mMaxPackageEnqueueRate);
                 }
                 pw.println("  mArchive=" + mArchive.toString());
-                Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
+                Iterator<Pair<StatusBarNotification, Integer>> iter = mArchive.descendingIterator();
                 int j=0;
                 while (iter.hasNext()) {
-                    final StatusBarNotification sbn = iter.next();
+                    final StatusBarNotification sbn = iter.next().first;
                     if (filter != null && !filter.matches(sbn)) continue;
                     pw.println("    " + sbn);
                     if (++j >= 5) {
@@ -7575,7 +7667,7 @@
         }
 
         // Save it for users of getHistoricalNotifications()
-        mArchive.record(r.getSbn());
+        mArchive.record(r.getSbn(), reason);
 
         final long now = System.currentTimeMillis();
         final LogMaker logMaker = r.getItemLogMaker()
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index fc2d9e7..2f78542 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -27,6 +27,7 @@
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationStats;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
 
@@ -266,5 +267,39 @@
         int getInstanceId() {
             return (r.getSbn().getInstanceId() == null ? 0 : r.getSbn().getInstanceId().getId());
         }
+
+        /**
+         * @return Small hash of the notification ID, and tag (if present).
+         */
+        int getNotificationIdHash() {
+            return smallHash(Objects.hashCode(r.getSbn().getTag()) ^ r.getSbn().getId());
+        }
+
+        /**
+         * @return Small hash of the channel ID, if present, or 0 otherwise.
+         */
+        int getChannelIdHash() {
+            return smallHash(Objects.hashCode(r.getSbn().getNotification().getChannelId()));
+        }
+
+        /**
+         * @return Small hash of the group ID, respecting group override if present. 0 otherwise.
+         */
+        int getGroupIdHash() {
+            return smallHash(Objects.hashCode(r.getSbn().getGroup()));
+        }
+
+        // "Small" hashes will be in the range [0, MAX_HASH).
+        static final int MAX_HASH = (1 << 13);
+
+        /**
+         * Maps in to the range [0, MAX_HASH), keeping similar values distinct.
+         * @param in An arbitrary integer.
+         * @return in mod MAX_HASH, signs chosen to stay in the range [0, MAX_HASH).
+         */
+        @VisibleForTesting
+        static int smallHash(int in) {
+            return Math.floorMod(in, MAX_HASH);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index 015d280..bb23d1e 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -40,28 +40,27 @@
                 /* int32 uid = 2 */ r.getUid(),
                 /* string package_name = 3 */ r.getSbn().getPackageName(),
                 /* int32 instance_id = 4 */ p.getInstanceId(),
-                /* int32 notification_id = 5 */ r.getSbn().getId(),
-                /* string notification_tag = 6 */ r.getSbn().getTag(),
-                /* string channel_id = 7 */ r.getSbn().getChannelIdLogTag(),
-                /* string group_id = 8 */ r.getSbn().getGroupLogTag(),
-                /* int32 group_instance_id = 9 */ 0, // TODO generate and fill instance ids
-                /* bool is_group_summary = 10 */ r.getSbn().getNotification().isGroupSummary(),
-                /* string category = 11 */ r.getSbn().getNotification().category,
-                /* int32 style = 12 */ p.getStyle(),
-                /* int32 num_people = 13 */ p.getNumPeople(),
-                /* int32 position = 14 */ position,
-                /* android.stats.sysui.NotificationImportance importance = 15 */ r.getImportance(),
-                /* int32 alerting = 16 */ buzzBeepBlink,
-                /* NotificationImportanceExplanation importance_source = 17 */
+                /* int32 notification_id_hash = 5 */ p.getNotificationIdHash(),
+                /* int32 channel_id_hash = 6 */ p.getChannelIdHash(),
+                /* string group_id_hash = 7 */ p.getGroupIdHash(),
+                /* int32 group_instance_id = 8 */ 0, // TODO generate and fill instance ids
+                /* bool is_group_summary = 9 */ r.getSbn().getNotification().isGroupSummary(),
+                /* string category = 10 */ r.getSbn().getNotification().category,
+                /* int32 style = 11 */ p.getStyle(),
+                /* int32 num_people = 12 */ p.getNumPeople(),
+                /* int32 position = 13 */ position,
+                /* android.stats.sysui.NotificationImportance importance = 14 */ r.getImportance(),
+                /* int32 alerting = 15 */ buzzBeepBlink,
+                /* NotificationImportanceExplanation importance_source = 16 */
                 r.getImportanceExplanationCode(),
-                /* android.stats.sysui.NotificationImportance importance_initial = 18 */
+                /* android.stats.sysui.NotificationImportance importance_initial = 17 */
                 r.getInitialImportance(),
-                /* NotificationImportanceExplanation importance_initial_source = 19 */
+                /* NotificationImportanceExplanation importance_initial_source = 18 */
                 r.getInitialImportanceExplanationCode(),
-                /* android.stats.sysui.NotificationImportance importance_asst = 20 */
+                /* android.stats.sysui.NotificationImportance importance_asst = 19 */
                 r.getAssistantImportance(),
-                /* int32 assistant_hash = 21 */ p.getAssistantHash(),
-                /* float assistant_ranking_score = 22 */ 0  // TODO connect up ranking score
+                /* int32 assistant_hash = 20 */ p.getAssistantHash(),
+                /* float assistant_ranking_score = 21 */ 0 // TODO connect up ranking score
         );
     }
 
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index a244c48..9f8362c 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -20,6 +20,10 @@
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
 
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -46,6 +50,7 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
+import android.util.StatsEvent;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.R;
@@ -80,6 +85,10 @@
     @VisibleForTesting
     static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 5000;
 
+    private static final int NOTIFICATION_PREFERENCES_PULL_LIMIT = 1000;
+    private static final int NOTIFICATION_CHANNEL_PULL_LIMIT = 2000;
+    private static final int NOTIFICATION_CHANNEL_GROUP_PULL_LIMIT = 1000;
+
     @VisibleForTesting
     static final String TAG_RANKING = "ranking";
     private static final String TAG_PACKAGE = "package";
@@ -1682,6 +1691,88 @@
         }
     }
 
+    /**
+     * Fills out {@link PackageNotificationPreferences} proto and wraps it in a {@link StatsEvent}.
+     */
+    public void pullPackagePreferencesStats(List<StatsEvent> events) {
+        synchronized (mPackagePreferences) {
+            for (int i = 0; i < mPackagePreferences.size(); i++) {
+                if (i > NOTIFICATION_PREFERENCES_PULL_LIMIT) {
+                    break;
+                }
+                StatsEvent.Builder event = StatsEvent.newBuilder()
+                        .setAtomId(PACKAGE_NOTIFICATION_PREFERENCES);
+                final PackagePreferences r = mPackagePreferences.valueAt(i);
+                event.writeInt(r.uid);
+                event.writeInt(r.importance);
+                event.writeInt(r.visibility);
+                event.writeInt(r.lockedAppFields);
+                events.add(event.build());
+            }
+        }
+    }
+
+    /**
+     * Fills out {@link PackageNotificationChannelPreferences} proto and wraps it in a
+     * {@link StatsEvent}.
+     */
+    public void pullPackageChannelPreferencesStats(List<StatsEvent> events) {
+        synchronized (mPackagePreferences) {
+            int totalChannelsPulled = 0;
+            for (int i = 0; i < mPackagePreferences.size(); i++) {
+                if (totalChannelsPulled > NOTIFICATION_CHANNEL_PULL_LIMIT) {
+                    break;
+                }
+                final PackagePreferences r = mPackagePreferences.valueAt(i);
+                for (NotificationChannel channel : r.channels.values()) {
+                    if (++totalChannelsPulled > NOTIFICATION_CHANNEL_PULL_LIMIT) {
+                        break;
+                    }
+                    StatsEvent.Builder event = StatsEvent.newBuilder()
+                            .setAtomId(PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES);
+                    event.writeInt(r.uid);
+                    event.writeString(channel.getId());
+                    event.writeString(channel.getName().toString());
+                    event.writeString(channel.getDescription());
+                    event.writeInt(channel.getImportance());
+                    event.writeInt(channel.getUserLockedFields());
+                    event.writeBoolean(channel.isDeleted());
+                    events.add(event.build());
+                }
+            }
+        }
+    }
+
+    /**
+     * Fills out {@link PackageNotificationChannelGroupPreferences} proto and wraps it in a
+     * {@link StatsEvent}.
+     */
+    public void pullPackageChannelGroupPreferencesStats(List<StatsEvent> events) {
+        synchronized (mPackagePreferences) {
+            int totalGroupsPulled = 0;
+            for (int i = 0; i < mPackagePreferences.size(); i++) {
+                if (totalGroupsPulled > NOTIFICATION_CHANNEL_GROUP_PULL_LIMIT) {
+                    break;
+                }
+                final PackagePreferences r = mPackagePreferences.valueAt(i);
+                for (NotificationChannelGroup groupChannel : r.groups.values()) {
+                    if (++totalGroupsPulled > NOTIFICATION_CHANNEL_GROUP_PULL_LIMIT) {
+                        break;
+                    }
+                    StatsEvent.Builder event = StatsEvent.newBuilder()
+                            .setAtomId(PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES);
+                    event.writeInt(r.uid);
+                    event.writeString(groupChannel.getId());
+                    event.writeString(groupChannel.getName().toString());
+                    event.writeString(groupChannel.getDescription());
+                    event.writeBoolean(groupChannel.isBlocked());
+                    event.writeInt(groupChannel.getUserLockedFields());
+                    events.add(event.build());
+                }
+            }
+        }
+    }
+
     public JSONObject dumpJson(NotificationManagerService.DumpFilter filter) {
         JSONObject ranking = new JSONObject();
         JSONArray PackagePreferencess = new JSONArray();
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index cd4485e..96d3cc1 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -37,6 +37,7 @@
 import android.content.Intent;
 import android.content.PermissionChecker;
 import android.content.pm.ActivityInfo;
+import android.content.pm.CrossProfileAppsInternal;
 import android.content.pm.ICrossProfileApps;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
@@ -53,6 +54,8 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
+import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
 import com.android.server.LocalServices;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
@@ -75,6 +78,8 @@
     CrossProfileAppsServiceImpl(Context context, Injector injector) {
         mContext = context;
         mInjector = injector;
+
+        LocalServices.addService(CrossProfileAppsInternal.class, new LocalService());
     }
 
     @Override
@@ -251,8 +256,8 @@
         Objects.requireNonNull(callingPackage);
         verifyCallingPackage(callingPackage);
 
-        final List<UserHandle> targetUserProfiles = getTargetUserProfilesUnchecked(
-                callingPackage, mInjector.getCallingUserId());
+        final List<UserHandle> targetUserProfiles = getTargetUserProfilesUnchecked(callingPackage,
+                mInjector.getCallingUserId());
         if (targetUserProfiles.isEmpty()) {
             return false;
         }
@@ -274,19 +279,14 @@
     }
 
     private boolean isCrossProfilePackageWhitelisted(String packageName) {
-        final long ident = mInjector.clearCallingIdentity();
-        try {
-            return mInjector.getDevicePolicyManagerInternal()
-                    .getAllCrossProfilePackages().contains(packageName);
-        } finally {
-            mInjector.restoreCallingIdentity(ident);
-        }
+        return mInjector.withCleanCallingIdentity(() ->
+                mInjector.getDevicePolicyManagerInternal()
+                        .getAllCrossProfilePackages().contains(packageName));
     }
 
     private List<UserHandle> getTargetUserProfilesUnchecked(
             String packageName, @UserIdInt int userId) {
-        final long ident = mInjector.clearCallingIdentity();
-        try {
+        return mInjector.withCleanCallingIdentity(() -> {
             final int[] enabledProfileIds =
                     mInjector.getUserManager().getEnabledProfileIds(userId);
 
@@ -301,15 +301,12 @@
                 targetProfiles.add(UserHandle.of(profileId));
             }
             return targetProfiles;
-        } finally {
-            mInjector.restoreCallingIdentity(ident);
-        }
+        });
     }
 
     private boolean isPackageEnabled(String packageName, @UserIdInt int userId) {
         final int callingUid = mInjector.getCallingUid();
-        final long ident = mInjector.clearCallingIdentity();
-        try {
+        return mInjector.withCleanCallingIdentity(() -> {
             final PackageInfo info = mInjector.getPackageManagerInternal()
                     .getPackageInfo(
                             packageName,
@@ -317,15 +314,12 @@
                             callingUid,
                             userId);
             return info != null && info.applicationInfo.enabled;
-        } finally {
-            mInjector.restoreCallingIdentity(ident);
-        }
+        });
     }
 
     private void verifyActivityCanHandleIntent(
             Intent launchIntent, int callingUid, @UserIdInt int userId) {
-        final long ident = mInjector.clearCallingIdentity();
-        try {
+        mInjector.withCleanCallingIdentity(() -> {
             final List<ResolveInfo> activities =
                     mInjector.getPackageManagerInternal().queryIntentActivities(
                             launchIntent,
@@ -338,9 +332,7 @@
                 return;
             }
             throw new SecurityException("Activity cannot handle intent");
-        } finally {
-            mInjector.restoreCallingIdentity(ident);
-        }
+        });
     }
 
     /**
@@ -349,8 +341,7 @@
      */
     private void verifyActivityCanHandleIntentAndExported(
             Intent launchIntent, ComponentName component, int callingUid, @UserIdInt int userId) {
-        final long ident = mInjector.clearCallingIdentity();
-        try {
+        mInjector.withCleanCallingIdentity(() -> {
             final List<ResolveInfo> apps =
                     mInjector.getPackageManagerInternal().queryIntentActivities(
                             launchIntent,
@@ -369,9 +360,7 @@
             }
             throw new SecurityException("Attempt to launch activity without "
                     + " category Intent.CATEGORY_LAUNCHER or activity is not exported" + component);
-        } finally {
-            mInjector.restoreCallingIdentity(ident);
-        }
+        });
     }
 
     @Override
@@ -383,7 +372,13 @@
                     "INTERACT_ACROSS_USERS or INTERACT_ACROSS_USERS_FULL is required to set the"
                             + " app-op for interacting across profiles.");
         }
-        final int callingUserId = mInjector.getCallingUserId();
+        if (!isPermissionGranted(Manifest.permission.MANAGE_APP_OPS_MODES, callingUid)
+                && !isPermissionGranted(
+                        Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, callingUid)) {
+            throw new SecurityException(
+                    "MANAGE_APP_OPS_MODES or CONFIGURE_INTERACT_ACROSS_PROFILES is required to set"
+                            + " the app-op for interacting across profiles.");
+        }
         if (newMode == AppOpsManager.MODE_ALLOWED
                 && !canConfigureInteractAcrossProfiles(packageName)) {
             // The user should not be prompted for apps that cannot request to interact across
@@ -393,7 +388,8 @@
             return;
         }
         final int[] profileIds =
-                mInjector.getUserManager().getProfileIds(callingUserId, /* enabledOnly= */ false);
+                mInjector.getUserManager()
+                        .getProfileIds(mInjector.getCallingUserId(), /* enabledOnly= */ false);
         for (int profileId : profileIds) {
             if (!isPackageInstalled(packageName, profileId)) {
                 continue;
@@ -404,8 +400,7 @@
 
     private boolean isPackageInstalled(String packageName, @UserIdInt int userId) {
         final int callingUid = mInjector.getCallingUid();
-        final long identity = mInjector.clearCallingIdentity();
-        try {
+        return mInjector.withCleanCallingIdentity(() -> {
             final PackageInfo info =
                     mInjector.getPackageManagerInternal()
                             .getPackageInfo(
@@ -414,9 +409,7 @@
                                     callingUid,
                                     userId);
             return info != null;
-        } finally {
-            mInjector.restoreCallingIdentity(identity);
-        }
+        });
     }
 
     private void setInteractAcrossProfilesAppOpForUser(
@@ -438,19 +431,31 @@
                     + packageName + " on user ID " + userId);
             return;
         }
-        mInjector.getAppOpsManager()
-                .setMode(OP_INTERACT_ACROSS_PROFILES,
-                        uid,
-                        packageName,
-                        newMode);
+        final int callingUid = mInjector.getCallingUid();
+        if (isPermissionGranted(
+                Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, callingUid)) {
+            // Clear calling identity since the CONFIGURE_INTERACT_ACROSS_PROFILES permission allows
+            // this particular app-op to be modified without the broader app-op permissions.
+            mInjector.withCleanCallingIdentity(() ->
+                    mInjector.getAppOpsManager()
+                            .setMode(OP_INTERACT_ACROSS_PROFILES, uid, packageName, newMode));
+        } else {
+            mInjector.getAppOpsManager()
+                    .setMode(OP_INTERACT_ACROSS_PROFILES, uid, packageName, newMode);
+        }
         sendCanInteractAcrossProfilesChangedBroadcast(packageName, uid, UserHandle.of(userId));
     }
 
+    /**
+     * Returns whether the given app-op mode is equivalent to the currently-set app-op of the given
+     * package name and UID. Clears identity to avoid permission checks, so ensure the caller does
+     * any necessary permission checks.
+     */
     private boolean currentModeEquals(@Mode int otherMode, String packageName, int uid) {
         final String op =
                 AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES);
-        return otherMode ==
-                mInjector.getAppOpsManager().unsafeCheckOpNoThrow(op, uid, packageName);
+        return mInjector.withCleanCallingIdentity(() -> otherMode
+                == mInjector.getAppOpsManager().unsafeCheckOpNoThrow(op, uid, packageName));
     }
 
     private void sendCanInteractAcrossProfilesChangedBroadcast(
@@ -491,8 +496,7 @@
     }
 
     private boolean hasOtherProfileWithPackageInstalled(String packageName, @UserIdInt int userId) {
-        final long ident = mInjector.clearCallingIdentity();
-        try {
+        return mInjector.withCleanCallingIdentity(() -> {
             final int[] profileIds =
                     mInjector.getUserManager().getProfileIds(userId, /* enabledOnly= */ false);
             for (int profileId : profileIds) {
@@ -500,10 +504,8 @@
                     return true;
                 }
             }
-        } finally {
-            mInjector.restoreCallingIdentity(ident);
-        }
-        return false;
+            return false;
+        });
     }
 
     @Override
@@ -523,12 +525,8 @@
     }
 
     private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) {
-        final long ident = mInjector.clearCallingIdentity();
-        try {
-            return mInjector.getUserManager().isSameProfileGroup(callerUserId, userId);
-        } finally {
-            mInjector.restoreCallingIdentity(ident);
-        }
+        return mInjector.withCleanCallingIdentity(() ->
+                mInjector.getUserManager().isSameProfileGroup(callerUserId, userId));
     }
 
     /**
@@ -557,42 +555,62 @@
             mContext = context;
         }
 
+        @Override
         public int getCallingUid() {
             return Binder.getCallingUid();
         }
 
+        @Override
         public int getCallingPid() {
             return Binder.getCallingPid();
         }
 
+        @Override
         public int getCallingUserId() {
             return UserHandle.getCallingUserId();
         }
 
+        @Override
         public UserHandle getCallingUserHandle() {
             return Binder.getCallingUserHandle();
         }
 
+        @Override
         public long clearCallingIdentity() {
             return Binder.clearCallingIdentity();
         }
 
+        @Override
         public void restoreCallingIdentity(long token) {
             Binder.restoreCallingIdentity(token);
         }
 
+        @Override
+        public void withCleanCallingIdentity(ThrowingRunnable action) {
+            Binder.withCleanCallingIdentity(action);
+        }
+
+        @Override
+        public final <T> T withCleanCallingIdentity(ThrowingSupplier<T> action) {
+            return Binder.withCleanCallingIdentity(action);
+        }
+
+        @Override
         public UserManager getUserManager() {
             return mContext.getSystemService(UserManager.class);
         }
 
+        @Override
         public PackageManagerInternal getPackageManagerInternal() {
             return LocalServices.getService(PackageManagerInternal.class);
         }
 
+        @Override
         public PackageManager getPackageManager() {
             return mContext.getPackageManager();
         }
 
+        @Override
         public AppOpsManager getAppOpsManager() {
             return mContext.getSystemService(AppOpsManager.class);
         }
@@ -643,6 +661,10 @@
 
         void restoreCallingIdentity(long token);
 
+        void withCleanCallingIdentity(ThrowingRunnable action);
+
+        <T> T withCleanCallingIdentity(ThrowingSupplier<T> action);
+
         UserManager getUserManager();
 
         PackageManagerInternal getPackageManagerInternal();
@@ -663,4 +685,30 @@
 
         int checkComponentPermission(String permission, int uid, int owningUid, boolean exported);
     }
+
+    class LocalService extends CrossProfileAppsInternal {
+        @Override
+        public boolean verifyPackageHasInteractAcrossProfilePermission(String packageName,
+                @UserIdInt int userId) throws PackageManager.NameNotFoundException {
+            final int uid = Objects.requireNonNull(
+                    mInjector.getPackageManager().getApplicationInfoAsUser(
+                            Objects.requireNonNull(packageName), /* flags= */ 0, userId)).uid;
+            return verifyUidHasInteractAcrossProfilePermission(packageName, uid);
+        }
+
+        @Override
+        public boolean verifyUidHasInteractAcrossProfilePermission(String packageName, int uid) {
+            Objects.requireNonNull(packageName);
+
+            return isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS_FULL, uid)
+                    || isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS, uid)
+                    || isPermissionGranted(Manifest.permission.INTERACT_ACROSS_PROFILES, uid)
+                    || PermissionChecker.checkPermissionForPreflight(
+                            mContext,
+                            Manifest.permission.INTERACT_ACROSS_PROFILES,
+                            PermissionChecker.PID_UNKNOWN,
+                            uid,
+                            packageName) == PermissionChecker.PERMISSION_GRANTED;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index da07365..f93c663 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.pm;
 
+import android.Manifest.permission;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -306,6 +307,10 @@
             final int callingUserId = injectCallingUserId();
 
             if (targetUserId == callingUserId) return true;
+            if (mContext.checkCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS_FULL)
+                    == PackageManager.PERMISSION_GRANTED) {
+                return true;
+            }
 
             long ident = injectClearCallingIdentity();
             try {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 944280d..4ed4de7 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -214,7 +214,7 @@
 
     private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill";
     private static final int[] EMPTY_CHILD_SESSION_ARRAY = EmptyArray.INT;
-    private static final FileInfo[] EMPTY_FILE_INFO_ARRAY = {};
+    private static final InstallationFile[] EMPTY_INSTALLATION_FILE_ARRAY = {};
 
     private static final String SYSTEM_DATA_LOADER_PACKAGE = "android";
 
@@ -309,33 +309,8 @@
     @GuardedBy("mLock")
     private int mParentSessionId;
 
-    static class FileInfo {
-        public final int location;
-        public final String name;
-        public final Long lengthBytes;
-        public final byte[] metadata;
-        public final byte[] signature;
-
-        public static FileInfo added(int location, String name, Long lengthBytes, byte[] metadata,
-                byte[] signature) {
-            return new FileInfo(location, name, lengthBytes, metadata, signature);
-        }
-
-        public static FileInfo removed(int location, String name) {
-            return new FileInfo(location, name, -1L, null, null);
-        }
-
-        FileInfo(int location, String name, Long lengthBytes, byte[] metadata, byte[] signature) {
-            this.location = location;
-            this.name = name;
-            this.lengthBytes = lengthBytes;
-            this.metadata = metadata;
-            this.signature = signature;
-        }
-    }
-
     @GuardedBy("mLock")
-    private ArrayList<FileInfo> mFiles = new ArrayList<>();
+    private ArrayList<InstallationFile> mFiles = new ArrayList<>();
 
     @GuardedBy("mLock")
     private boolean mStagedSessionApplied;
@@ -508,7 +483,7 @@
             PackageSessionProvider sessionProvider, Looper looper, StagingManager stagingManager,
             int sessionId, int userId, int installerUid, @NonNull InstallSource installSource,
             SessionParams params, long createdMillis,
-            File stageDir, String stageCid, FileInfo[] files, boolean prepared,
+            File stageDir, String stageCid, InstallationFile[] files, boolean prepared,
             boolean committed, boolean sealed,
             @Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
             boolean isFailed, boolean isApplied, int stagedSessionErrorCode,
@@ -539,7 +514,7 @@
         this.mParentSessionId = parentSessionId;
 
         if (files != null) {
-            for (FileInfo file : files) {
+            for (InstallationFile file : files) {
                 mFiles.add(file);
             }
         }
@@ -738,7 +713,7 @@
 
         String[] result = new String[mFiles.size()];
         for (int i = 0, size = mFiles.size(); i < size; ++i) {
-            result[i] = mFiles.get(i).name;
+            result[i] = mFiles.get(i).getName();
         }
         return result;
     }
@@ -2425,7 +2400,7 @@
             assertCallerIsOwnerOrRootLocked();
             assertPreparedAndNotSealedLocked("addFile");
 
-            mFiles.add(FileInfo.added(location, name, lengthBytes, metadata, signature));
+            mFiles.add(new InstallationFile(location, name, lengthBytes, metadata, signature));
         }
     }
 
@@ -2443,7 +2418,7 @@
             assertCallerIsOwnerOrRootLocked();
             assertPreparedAndNotSealedLocked("removeFile");
 
-            mFiles.add(FileInfo.removed(location, getRemoveMarkerName(name)));
+            mFiles.add(new InstallationFile(location, getRemoveMarkerName(name), -1, null, null));
         }
     }
 
@@ -2461,32 +2436,20 @@
         }
 
         final List<InstallationFile> addedFiles = new ArrayList<>(mFiles.size());
-        for (FileInfo file : mFiles) {
-            if (sAddedFilter.accept(new File(this.stageDir, file.name))) {
-                addedFiles.add(new InstallationFile(
-                        file.name, file.lengthBytes, file.metadata));
+        for (InstallationFile file : mFiles) {
+            if (sAddedFilter.accept(new File(this.stageDir, file.getName()))) {
+                addedFiles.add(file);
             }
         }
         final List<String> removedFiles = new ArrayList<>(mFiles.size());
-        for (FileInfo file : mFiles) {
-            if (sRemovedFilter.accept(new File(this.stageDir, file.name))) {
-                String name = file.name.substring(
-                        0, file.name.length() - REMOVE_MARKER_EXTENSION.length());
+        for (InstallationFile file : mFiles) {
+            if (sRemovedFilter.accept(new File(this.stageDir, file.getName()))) {
+                String name = file.getName().substring(
+                        0, file.getName().length() - REMOVE_MARKER_EXTENSION.length());
                 removedFiles.add(name);
             }
         }
 
-        // TODO(b/136132412): update with new APIs
-        if (isIncrementalInstallation()) {
-            try {
-                mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext,
-                        stageDir, params.dataLoaderParams, addedFiles);
-                return true;
-            } catch (IOException e) {
-                throw new PackageManagerException(e);
-            }
-        }
-
         final DataLoaderManager dataLoaderManager = mContext.getSystemService(
                 DataLoaderManager.class);
         if (dataLoaderManager == null) {
@@ -2494,6 +2457,7 @@
                     "Failed to find data loader manager service");
         }
 
+        final boolean manualStartAndDestroy = !isIncrementalInstallation();
         IDataLoaderStatusListener listener = new IDataLoaderStatusListener.Stub() {
             @Override
             public void onStatusChanged(int dataLoaderId, int status) {
@@ -2513,7 +2477,11 @@
 
                     switch (status) {
                         case IDataLoaderStatusListener.DATA_LOADER_CREATED: {
-                            dataLoader.start();
+                            if (manualStartAndDestroy) {
+                                // IncrementalFileStorages will call start after all files are
+                                // created in IncFS.
+                                dataLoader.start();
+                            }
                             break;
                         }
                         case IDataLoaderStatusListener.DATA_LOADER_STARTED: {
@@ -2528,7 +2496,9 @@
                             } else {
                                 dispatchStreamValidateAndCommit();
                             }
-                            dataLoader.destroy();
+                            if (manualStartAndDestroy) {
+                                dataLoader.destroy();
+                            }
                             break;
                         }
                         case IDataLoaderStatusListener.DATA_LOADER_IMAGE_NOT_READY: {
@@ -2536,7 +2506,9 @@
                             onSessionVerificationFailure(
                                     new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
                                             "Failed to prepare image."));
-                            dataLoader.destroy();
+                            if (manualStartAndDestroy) {
+                                dataLoader.destroy();
+                            }
                             break;
                         }
                     }
@@ -2550,6 +2522,17 @@
             }
         };
 
+        if (!manualStartAndDestroy) {
+            try {
+                mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext,
+                        stageDir, params.dataLoaderParams, listener, addedFiles);
+                return false;
+            } catch (IOException e) {
+                throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(),
+                        e.getCause());
+            }
+        }
+
         final FileSystemConnector connector = new FileSystemConnector(addedFiles);
         final FileSystemControlParcel control = new FileSystemControlParcel();
         control.callback = connector;
@@ -2970,13 +2953,13 @@
                 writeIntAttribute(out, ATTR_SESSION_ID, childSessionId);
                 out.endTag(null, TAG_CHILD_SESSION);
             }
-            for (FileInfo fileInfo : mFiles) {
+            for (InstallationFile file : mFiles) {
                 out.startTag(null, TAG_SESSION_FILE);
-                writeIntAttribute(out, ATTR_LOCATION, fileInfo.location);
-                writeStringAttribute(out, ATTR_NAME, fileInfo.name);
-                writeLongAttribute(out, ATTR_LENGTH_BYTES, fileInfo.lengthBytes);
-                writeByteArrayAttribute(out, ATTR_METADATA, fileInfo.metadata);
-                writeByteArrayAttribute(out, ATTR_SIGNATURE, fileInfo.signature);
+                writeIntAttribute(out, ATTR_LOCATION, file.getLocation());
+                writeStringAttribute(out, ATTR_NAME, file.getName());
+                writeLongAttribute(out, ATTR_LENGTH_BYTES, file.getLengthBytes());
+                writeByteArrayAttribute(out, ATTR_METADATA, file.getMetadata());
+                writeByteArrayAttribute(out, ATTR_SIGNATURE, file.getSignature());
                 out.endTag(null, TAG_SESSION_FILE);
             }
         }
@@ -3088,7 +3071,7 @@
         List<String> grantedRuntimePermissions = new ArrayList<>();
         List<String> whitelistedRestrictedPermissions = new ArrayList<>();
         List<Integer> childSessionIds = new ArrayList<>();
-        List<FileInfo> files = new ArrayList<>();
+        List<InstallationFile> files = new ArrayList<>();
         int outerDepth = in.getDepth();
         int type;
         while ((type = in.next()) != XmlPullParser.END_DOCUMENT
@@ -3107,7 +3090,7 @@
                 childSessionIds.add(readIntAttribute(in, ATTR_SESSION_ID, SessionInfo.INVALID_ID));
             }
             if (TAG_SESSION_FILE.equals(in.getName())) {
-                files.add(new FileInfo(
+                files.add(new InstallationFile(
                         readIntAttribute(in, ATTR_LOCATION, 0),
                         readStringAttribute(in, ATTR_NAME),
                         readLongAttribute(in, ATTR_LENGTH_BYTES, -1),
@@ -3135,16 +3118,16 @@
             childSessionIdsArray = EMPTY_CHILD_SESSION_ARRAY;
         }
 
-        FileInfo[] fileInfosArray = null;
+        InstallationFile[] fileArray = null;
         if (!files.isEmpty()) {
-            fileInfosArray = files.toArray(EMPTY_FILE_INFO_ARRAY);
+            fileArray = files.toArray(EMPTY_INSTALLATION_FILE_ARRAY);
         }
 
         InstallSource installSource = InstallSource.create(installInitiatingPackageName,
                 installOriginatingPackageName, installerPackageName);
         return new PackageInstallerSession(callback, context, pm, sessionProvider,
                 installerThread, stagingManager, sessionId, userId, installerUid,
-                installSource, params, createdMillis, stageDir, stageCid, fileInfosArray,
+                installSource, params, createdMillis, stageDir, stageCid, fileArray,
                 prepared, committed, sealed, childSessionIdsArray, parentSessionId,
                 isReady, isFailed, isApplied, stagedSessionErrorCode, stagedSessionErrorMessage);
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index cb94043..3909fdf 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -181,6 +181,8 @@
                     return runInstall();
                 case "install-streaming":
                     return runStreamingInstall();
+                case "install-incremental":
+                    return runIncrementalInstall();
                 case "install-abandon":
                 case "install-destroy":
                     return runInstallAbandon();
@@ -1163,7 +1165,16 @@
         final InstallParams params = makeInstallParams();
         if (params.sessionParams.dataLoaderParams == null) {
             params.sessionParams.setDataLoaderParams(
-                    PackageManagerShellCommandDataLoader.getDataLoaderParams(this));
+                    PackageManagerShellCommandDataLoader.getStreamingDataLoaderParams(this));
+        }
+        return doRunInstall(params);
+    }
+
+    private int runIncrementalInstall() throws RemoteException {
+        final InstallParams params = makeInstallParams();
+        if (params.sessionParams.dataLoaderParams == null) {
+            params.sessionParams.setDataLoaderParams(
+                    PackageManagerShellCommandDataLoader.getIncrementalDataLoaderParams(this));
         }
         return doRunInstall(params);
     }
@@ -3001,17 +3012,21 @@
                         return 1;
                     }
 
-                    session.addFile(LOCATION_DATA_APP, name, sizeBytes, STDIN_PATH_BYTES, null);
+                    // Incremental requires unique metadatas, let's add a name to the dash.
+                    session.addFile(LOCATION_DATA_APP, name, sizeBytes,
+                            ("-" + name).getBytes(StandardCharsets.UTF_8), null);
                     continue;
                 }
 
                 // 3. Local file.
                 final String inPath = arg;
 
-                String name = new File(inPath).getName();
+                final File file = new File(inPath);
+                final String name = file.getName();
+                final long size = file.length();
                 byte[] metadata = inPath.getBytes(StandardCharsets.UTF_8);
 
-                session.addFile(LOCATION_DATA_APP, name, -1, metadata, null);
+                session.addFile(LOCATION_DATA_APP, name, size, metadata, null);
             }
             return 0;
         } finally {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
index 5dca9e1..8f30e7d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
@@ -20,6 +20,7 @@
 import android.content.ComponentName;
 import android.content.pm.DataLoaderParams;
 import android.content.pm.InstallationFile;
+import android.content.pm.PackageInstaller;
 import android.os.ParcelFileDescriptor;
 import android.os.ShellCommand;
 import android.service.dataloader.DataLoaderService;
@@ -54,7 +55,9 @@
 
     private static final String STDIN_PATH = "-";
 
-    static DataLoaderParams getDataLoaderParams(ShellCommand shellCommand) {
+    private static String getDataLoaderParamsArgs(ShellCommand shellCommand) {
+        nativeInitialize();
+
         int commandId;
         synchronized (sShellCommands) {
             // Clean up old references.
@@ -78,8 +81,17 @@
             sShellCommands.put(commandId, new WeakReference<>(shellCommand));
         }
 
-        final String args = SHELL_COMMAND_ID_PREFIX + commandId;
-        return DataLoaderParams.forStreaming(new ComponentName(PACKAGE, CLASS), args);
+        return SHELL_COMMAND_ID_PREFIX + commandId;
+    }
+
+    static DataLoaderParams getStreamingDataLoaderParams(ShellCommand shellCommand) {
+        return DataLoaderParams.forStreaming(new ComponentName(PACKAGE, CLASS),
+                getDataLoaderParamsArgs(shellCommand));
+    }
+
+    static DataLoaderParams getIncrementalDataLoaderParams(ShellCommand shellCommand) {
+        return DataLoaderParams.forIncremental(new ComponentName(PACKAGE, CLASS),
+                getDataLoaderParamsArgs(shellCommand), null);
     }
 
     private static int extractShellCommandId(String args) {
@@ -102,7 +114,7 @@
         }
     }
 
-    static class DataLoader implements DataLoaderService.DataLoader {
+    private static class DataLoader implements DataLoaderService.DataLoader {
         private DataLoaderParams mParams = null;
         private FileSystemConnector mConnector = null;
 
@@ -117,33 +129,22 @@
         @Override
         public boolean onPrepareImage(@NonNull Collection<InstallationFile> addedFiles,
                 @NonNull Collection<String> removedFiles) {
-            final int commandId = extractShellCommandId(mParams.getArguments());
-            if (commandId == INVALID_SHELL_COMMAND_ID) {
-                return false;
-            }
-
-            final WeakReference<ShellCommand> shellCommandRef;
-            synchronized (sShellCommands) {
-                shellCommandRef = sShellCommands.get(commandId, null);
-            }
-            final ShellCommand shellCommand =
-                    shellCommandRef != null ? shellCommandRef.get() : null;
+            ShellCommand shellCommand = lookupShellCommand(mParams.getArguments());
             if (shellCommand == null) {
                 Slog.e(TAG, "Missing shell command.");
                 return false;
             }
             try {
-                for (InstallationFile fileInfo : addedFiles) {
-                    String filePath = new String(fileInfo.getMetadata(), StandardCharsets.UTF_8);
-                    if (STDIN_PATH.equals(filePath) || TextUtils.isEmpty(filePath)) {
-                        final ParcelFileDescriptor inFd = ParcelFileDescriptor.dup(
-                                shellCommand.getInFileDescriptor());
-                        mConnector.writeData(fileInfo.getName(), 0, fileInfo.getSize(), inFd);
+                for (InstallationFile file : addedFiles) {
+                    String filePath = new String(file.getMetadata(), StandardCharsets.UTF_8);
+                    if (TextUtils.isEmpty(filePath) || filePath.startsWith(STDIN_PATH)) {
+                        final ParcelFileDescriptor inFd = getStdInPFD(shellCommand);
+                        mConnector.writeData(file.getName(), 0, file.getLengthBytes(), inFd);
                     } else {
                         ParcelFileDescriptor incomingFd = null;
                         try {
-                            incomingFd = shellCommand.openFileForSystem(filePath, "r");
-                            mConnector.writeData(fileInfo.getName(), 0, incomingFd.getStatSize(),
+                            incomingFd = getLocalFile(shellCommand, filePath);
+                            mConnector.writeData(file.getName(), 0, incomingFd.getStatSize(),
                                     incomingFd);
                         } finally {
                             IoUtils.closeQuietly(incomingFd);
@@ -158,8 +159,45 @@
         }
     }
 
-    @Override
-    public DataLoaderService.DataLoader onCreateDataLoader() {
-        return new DataLoader();
+    static ShellCommand lookupShellCommand(String args) {
+        final int commandId = extractShellCommandId(args);
+        if (commandId == INVALID_SHELL_COMMAND_ID) {
+            return null;
+        }
+
+        final WeakReference<ShellCommand> shellCommandRef;
+        synchronized (sShellCommands) {
+            shellCommandRef = sShellCommands.get(commandId, null);
+        }
+        final ShellCommand shellCommand =
+                shellCommandRef != null ? shellCommandRef.get() : null;
+
+        return shellCommand;
     }
+
+    static ParcelFileDescriptor getStdInPFD(ShellCommand shellCommand) {
+        try {
+            return ParcelFileDescriptor.dup(shellCommand.getInFileDescriptor());
+        } catch (IOException e) {
+            Slog.e(TAG, "Exception while obtaining STDIN fd", e);
+            return null;
+        }
+    }
+
+    static ParcelFileDescriptor getLocalFile(ShellCommand shellCommand, String filePath) {
+        return shellCommand.openFileForSystem(filePath, "r");
+    }
+
+    @Override
+    public DataLoaderService.DataLoader onCreateDataLoader(
+            @NonNull DataLoaderParams dataLoaderParams) {
+        if (dataLoaderParams.getType() == PackageInstaller.DATA_LOADER_TYPE_STREAMING) {
+            // This DataLoader only supports streaming installations.
+            return new DataLoader();
+        }
+        return null;
+    }
+
+    /* Native methods */
+    private static native void nativeInitialize();
 }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index fbea595..0fb4cb0 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4523,7 +4523,11 @@
                 pw.print(prefix); pw.print("  privateFlags="); printFlags(pw,
                         pkg.getPrivateFlags(), PRIVATE_FLAG_DUMP_SPEC); pw.println();
             }
-            pw.print(prefix); pw.print("  forceQueryable="); pw.println(ps.pkg.isForceQueryable());
+            pw.print(prefix); pw.print("  forceQueryable="); pw.print(ps.pkg.isForceQueryable());
+            if (ps.forceQueryableOverride) {
+                pw.print(" (override=true)");
+            }
+            pw.println();
             if (ps.pkg.getQueriesPackages() != null) {
                 pw.append(prefix).append("  queriesPackages=").println(ps.pkg.getQueriesPackages());
             }
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index b9bb9e0..bbd319c 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -182,7 +182,10 @@
     public void updateProcesses() {
         processes = null;
         for (int i = packages.size() - 1; i >= 0; i--) {
-            addProcesses(packages.valueAt(i).pkg.getProcesses());
+            final AndroidPackage pkg = packages.valueAt(i).pkg;
+            if (pkg != null) {
+                addProcesses(pkg.getProcesses());
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 1c02161..cb755f9 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -35,13 +35,16 @@
 import android.app.KeyguardManager;
 import android.app.PendingIntent;
 import android.app.admin.DevicePolicyEventLogger;
+import android.app.admin.DevicePolicyManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
+import android.content.pm.CrossProfileAppsInternal;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.ShortcutServiceInternal;
 import android.content.pm.UserInfo;
 import android.content.pm.UserInfo.UserInfoFlag;
@@ -258,6 +261,10 @@
     /** Installs system packages based on user-type. */
     private final UserSystemPackageInstaller mSystemPackageInstaller;
 
+    private PackageManagerInternal mPmInternal;
+    private CrossProfileAppsInternal mCrossProfileAppsInternal;
+    private DevicePolicyManagerInternal mDevicePolicyManagerInternal;
+
     /**
      * Internal non-parcelable wrapper for UserInfo that is not exposed to other system apps.
      */
@@ -462,8 +469,42 @@
     @GuardedBy("mUsersLock")
     private boolean mForceEphemeralUsers;
 
+    /**
+     * The member mUserStates affects the return value of isUserUnlocked.
+     * If any value in mUserStates changes, then the binder cache for
+     * isUserUnlocked must be invalidated.  When adding mutating methods to
+     * WatchedUserStates, be sure to invalidate the cache in the new
+     * methods.
+     */
+    private class WatchedUserStates {
+        final SparseIntArray states;
+        public WatchedUserStates() {
+            states = new SparseIntArray();
+        }
+        public int get(int userId) {
+            return states.get(userId);
+        }
+        public int get(int userId, int fallback) {
+            return states.indexOfKey(userId) >= 0 ? states.get(userId) : fallback;
+        }
+        public void put(int userId, int state) {
+            states.put(userId, state);
+            invalidateIsUserUnlockedCache();
+        }
+        public void delete(int userId) {
+            states.delete(userId);
+            invalidateIsUserUnlockedCache();
+        }
+        @Override
+        public String toString() {
+            return states.toString();
+        }
+        private void invalidateIsUserUnlockedCache() {
+            UserManager.invalidateIsUserUnlockedCache();
+        }
+    }
     @GuardedBy("mUserStates")
-    private final SparseIntArray mUserStates = new SparseIntArray();
+    private final WatchedUserStates mUserStates = new WatchedUserStates();
 
     private static UserManagerService sInstance;
 
@@ -910,6 +951,8 @@
         intent.putExtra(Intent.EXTRA_QUIET_MODE, inQuietMode);
         intent.putExtra(Intent.EXTRA_USER, profileHandle);
         intent.putExtra(Intent.EXTRA_USER_HANDLE, profileHandle.getIdentifier());
+        getDevicePolicyManagerInternal().broadcastIntentToCrossProfileManifestReceiversAsUser(
+                intent, parentHandle, /* requiresPermission= */ true);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
         mContext.sendBroadcastAsUser(intent, parentHandle);
     }
@@ -3811,11 +3854,15 @@
 
     private void sendProfileRemovedBroadcast(int parentUserId, int removedUserId) {
         Intent managedProfileIntent = new Intent(Intent.ACTION_MANAGED_PROFILE_REMOVED);
-        managedProfileIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY |
-                Intent.FLAG_RECEIVER_FOREGROUND);
         managedProfileIntent.putExtra(Intent.EXTRA_USER, new UserHandle(removedUserId));
         managedProfileIntent.putExtra(Intent.EXTRA_USER_HANDLE, removedUserId);
-        mContext.sendBroadcastAsUser(managedProfileIntent, new UserHandle(parentUserId), null);
+        final UserHandle parentHandle = new UserHandle(parentUserId);
+        getDevicePolicyManagerInternal().broadcastIntentToCrossProfileManifestReceiversAsUser(
+                managedProfileIntent, parentHandle, /* requiresPermission= */ false);
+        managedProfileIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+                | Intent.FLAG_RECEIVER_FOREGROUND);
+        mContext.sendBroadcastAsUser(managedProfileIntent, parentHandle,
+                /* receiverPermission= */null);
     }
 
     @Override
@@ -4801,6 +4848,11 @@
                     || (state == UserState.STATE_RUNNING_UNLOCKED);
         }
 
+        /**
+         * The return values of this method are cached in clients.  If the
+         * logic in this function changes then the cache invalidation code
+         * may need to be revisited.
+         */
         @Override
         public boolean isUserUnlocked(@UserIdInt int userId) {
             int state;
@@ -5056,7 +5108,7 @@
     }
 
     /**
-     * Check if the calling package name matches with the calling UID, throw
+     * Checks if the calling package name matches with the calling UID, throw
      * {@link SecurityException} if not.
      */
     private void verifyCallingPackage(String callingPackage, int callingUid) {
@@ -5066,4 +5118,30 @@
                     + " does not match the calling uid " + callingUid);
         }
     }
+
+    /** Retrieves the internal package manager interface. */
+    private PackageManagerInternal getPackageManagerInternal() {
+        // Don't need to synchonize; worst-case scenario LocalServices will be called twice.
+        if (mPmInternal == null) {
+            mPmInternal = LocalServices.getService(PackageManagerInternal.class);
+        }
+        return mPmInternal;
+    }
+
+    /** Retrieve the internal cross profile apps interface. */
+    private CrossProfileAppsInternal getCrossProfileAppsInternal() {
+        if (mCrossProfileAppsInternal == null) {
+            mCrossProfileAppsInternal = LocalServices.getService(CrossProfileAppsInternal.class);
+        }
+        return mCrossProfileAppsInternal;
+    }
+
+    /** Returns the internal device policy manager interface. */
+    private DevicePolicyManagerInternal getDevicePolicyManagerInternal() {
+        if (mDevicePolicyManagerInternal == null) {
+            mDevicePolicyManagerInternal =
+                    LocalServices.getService(DevicePolicyManagerInternal.class);
+        }
+        return mDevicePolicyManagerInternal;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 91bd7ae..67b1008 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -783,6 +783,7 @@
                 break;
 
             case android.provider.Settings.System.SCREEN_BRIGHTNESS:
+            case android.provider.Settings.System.SCREEN_BRIGHTNESS_FLOAT:
             case android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE:
                 if (callingUid == Process.SYSTEM_UID) {
                     return false;
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index 77bb48e..4c40448 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -89,7 +89,7 @@
      * <ul>
      * <li> 0  - disable whitelist (install all system packages; no logging)</li>
      * <li> 1  - enforce (only install system packages if they are whitelisted)</li>
-     * <li> 2  - log (log when a non-whitelisted package is run)</li>
+     * <li> 2  - log (log non-whitelisted packages)</li>
      * <li> 4  - for all users: implicitly whitelist any package not mentioned in the whitelist</li>
      * <li> 8  - for SYSTEM: implicitly whitelist any package not mentioned in the whitelist</li>
      * <li> 16 - ignore OTAs (don't install system packages during OTAs)</li>
diff --git a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
index b809951..976ce1f 100644
--- a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
+++ b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
@@ -36,6 +36,8 @@
 
     private static final String LOG_TAG = OneTimePermissionUserManager.class.getSimpleName();
 
+    private static final boolean DEBUG = true;
+
     private final @NonNull Context mContext;
     private final @NonNull ActivityManager mActivityManager;
     private final @NonNull AlarmManager mAlarmManager;
@@ -156,6 +158,14 @@
 
         private PackageInactivityListener(int uid, @NonNull String packageName, long timeout,
                 int importanceToResetTimer, int importanceToKeepSessionAlive) {
+
+            if (DEBUG) {
+                Log.d(LOG_TAG,
+                        "Start tracking " + packageName + ". uid=" + uid + " timeout=" + timeout
+                                + " importanceToResetTimer=" + importanceToResetTimer
+                                + " importanceToKeepSessionAlive=" + importanceToKeepSessionAlive);
+            }
+
             mUid = uid;
             mPackageName = packageName;
             mTimeout = timeout;
@@ -182,6 +192,12 @@
             if (uid != mUid) {
                 return;
             }
+
+
+            if (DEBUG) {
+                Log.d(LOG_TAG, "Importance changed for " + mPackageName + " (" + mUid + ")."
+                        + " importance=" + importance);
+            }
             synchronized (mInnerLock) {
                 if (importance > IMPORTANCE_CACHED) {
                     onPackageInactiveLocked();
@@ -257,8 +273,15 @@
             mIsFinished = true;
             cancelAlarmLocked();
             mContext.getMainThreadHandler().post(
-                    () -> mPermissionControllerManager.notifyOneTimePermissionSessionTimeout(
-                            mPackageName));
+                    () -> {
+                        if (DEBUG) {
+                            Log.d(LOG_TAG, "One time session expired for "
+                                    + mPackageName + " (" + mUid + ").");
+                        }
+
+                        mPermissionControllerManager.notifyOneTimePermissionSessionTimeout(
+                                mPackageName);
+                    });
             mActivityManager.removeOnUidImportanceListener(mStartTimerListener);
             mActivityManager.removeOnUidImportanceListener(mSessionKillableListener);
             mActivityManager.removeOnUidImportanceListener(mGoneListener);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4b2dc85..a45a996 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2729,21 +2729,23 @@
                             Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
                             UserHandle.USER_CURRENT_OR_SELF);
                 }
-
-                int min = mPowerManager.getMinimumScreenBrightnessSetting();
-                int max = mPowerManager.getMaximumScreenBrightnessSetting();
-                int step = (max - min + BRIGHTNESS_STEPS - 1) / BRIGHTNESS_STEPS * direction;
-                int brightness = Settings.System.getIntForUser(mContext.getContentResolver(),
-                        Settings.System.SCREEN_BRIGHTNESS,
-                        mPowerManager.getDefaultScreenBrightnessSetting(),
+                float minFloat = mPowerManager.getBrightnessConstraint(
+                        PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
+                float maxFloat = mPowerManager.getBrightnessConstraint(
+                        PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
+                float stepFloat = (maxFloat - minFloat) / BRIGHTNESS_STEPS * direction;
+                float brightnessFloat = Settings.System.getFloatForUser(
+                        mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_FLOAT,
+                        mPowerManager.getBrightnessConstraint(
+                                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT),
                         UserHandle.USER_CURRENT_OR_SELF);
-                brightness += step;
+                brightnessFloat += stepFloat;
                 // Make sure we don't go beyond the limits.
-                brightness = Math.min(max, brightness);
-                brightness = Math.max(min, brightness);
+                brightnessFloat = Math.min(maxFloat, brightnessFloat);
+                brightnessFloat = Math.max(minFloat, brightnessFloat);
 
-                Settings.System.putIntForUser(mContext.getContentResolver(),
-                        Settings.System.SCREEN_BRIGHTNESS, brightness,
+                Settings.System.putFloatForUser(mContext.getContentResolver(),
+                        Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightnessFloat,
                         UserHandle.USER_CURRENT_OR_SELF);
                 startActivityAsUser(new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG),
                         UserHandle.CURRENT_OR_SELF);
diff --git a/services/core/java/com/android/server/policy/TEST_MAPPING b/services/core/java/com/android/server/policy/TEST_MAPPING
index 484017b..094e70f 100644
--- a/services/core/java/com/android/server/policy/TEST_MAPPING
+++ b/services/core/java/com/android/server/policy/TEST_MAPPING
@@ -36,6 +36,9 @@
         },
         {
           "include-filter": "android.permission2.cts.RestrictedStoragePermissionSharedUidTest"
+        },
+        {
+          "include-filter": "android.permission2.cts.RestrictedStoragePermissionTest"
         }
       ]
     },
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index e81214e..67a22d3 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -75,7 +75,6 @@
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -83,7 +82,6 @@
 import android.view.IApplicationToken;
 import android.view.IDisplayFoldListener;
 import android.view.IWindowManager;
-import android.view.InputEventReceiver;
 import android.view.KeyEvent;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
@@ -471,6 +469,10 @@
          * Remove the input consumer from the window manager.
          */
         void dismiss();
+        /**
+         * Dispose the input consumer and input receiver from UI thread.
+         */
+        void dispose();
     }
 
     /**
@@ -505,12 +507,6 @@
         public static final int CAMERA_LENS_COVERED = 1;
 
         /**
-         * Add a input consumer which will consume all input events going to any window below it.
-         */
-        public InputConsumer createInputConsumer(Looper looper, String name,
-                InputEventReceiver.Factory inputEventReceiverFactory, int displayId);
-
-        /**
          * Returns a code that describes the current state of the lid switch.
          */
         public int getLidState();
diff --git a/services/core/java/com/android/server/power/AttentionDetector.java b/services/core/java/com/android/server/power/AttentionDetector.java
index cc72dd6..1ab6ade 100644
--- a/services/core/java/com/android/server/power/AttentionDetector.java
+++ b/services/core/java/com/android/server/power/AttentionDetector.java
@@ -17,7 +17,7 @@
 package com.android.server.power;
 
 import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE;
-import static android.provider.Settings.System.ADAPTIVE_SLEEP;
+import static android.provider.Settings.Secure.ADAPTIVE_SLEEP;
 
 import android.Manifest;
 import android.app.ActivityManager;
@@ -152,8 +152,8 @@
 
     @VisibleForTesting
     void updateEnabledFromSettings(Context context) {
-        mIsSettingEnabled = Settings.System.getIntForUser(context.getContentResolver(),
-                Settings.System.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT) == 1;
+        mIsSettingEnabled = Settings.Secure.getIntForUser(context.getContentResolver(),
+                Settings.Secure.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT) == 1;
     }
 
     public void systemReady(Context context) {
@@ -173,8 +173,8 @@
             // Shouldn't happen since in-process.
         }
 
-        context.getContentResolver().registerContentObserver(Settings.System.getUriFor(
-                Settings.System.ADAPTIVE_SLEEP),
+        context.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
+                Settings.Secure.ADAPTIVE_SLEEP),
                 false, new ContentObserver(new Handler(context.getMainLooper())) {
                     @Override
                     public void onChange(boolean selfChange) {
@@ -194,7 +194,7 @@
         if (!isAttentionServiceSupported() || !serviceHasSufficientPermissions()) {
             // Turns off adaptive sleep in settings for all users if attention service is not
             // available. The setting itself should also be grayed out in this case.
-            Settings.System.putInt(mContentResolver, ADAPTIVE_SLEEP, 0);
+            Settings.Secure.putInt(mContentResolver, ADAPTIVE_SLEEP, 0);
             return nextScreenDimming;
         }
 
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 4d13658..002ab9c 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -83,6 +83,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
 
+import com.android.internal.BrightnessSynchronizer;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.app.IBatteryStats;
@@ -192,6 +193,9 @@
     // This should perhaps be a setting.
     private static final int SCREEN_BRIGHTNESS_BOOST_TIMEOUT = 5 * 1000;
 
+    // Float.NaN cannot be stored in config.xml so -2 is used instead
+    private static final float INVALID_BRIGHTNESS_IN_CONFIG = -2f;
+
     // How long a partial wake lock must be held until we consider it a long wake lock.
     static final long MIN_LONG_WAKE_CHECK_INTERVAL = 60*1000;
 
@@ -485,13 +489,17 @@
     private boolean mProximityPositive;
 
     // Screen brightness setting limits.
-    private int mScreenBrightnessSettingMinimum;
-    private int mScreenBrightnessSettingMaximum;
-    private int mScreenBrightnessSettingDefault;
-
-    // The screen brightness setting, from 0 to 255.
-    // Use -1 if no value has been set.
-    private int mScreenBrightnessSetting;
+    private float mScreenBrightnessSettingMinimum;
+    private float mScreenBrightnessSettingMaximum;
+    private float mScreenBrightnessSettingDefault;
+    public final float mScreenBrightnessMinimum;
+    public final float mScreenBrightnessMaximum;
+    public final float mScreenBrightnessDefault;
+    public final float mScreenBrightnessDoze;
+    public final float mScreenBrightnessDim;
+    public final float mScreenBrightnessMinimumVr;
+    public final float mScreenBrightnessMaximumVr;
+    public final float mScreenBrightnessDefaultVr;
 
     // The screen brightness mode.
     // One of the Settings.System.SCREEN_BRIGHTNESS_MODE_* constants.
@@ -502,6 +510,9 @@
     // Use -1 to disable.
     private int mScreenBrightnessOverrideFromWindowManager = -1;
 
+    private float mScreenBrightnessOverrideFromWindowManagerFloat =
+            PowerManager.BRIGHTNESS_INVALID_FLOAT;
+
     // The window manager has determined the user to be inactive via other means.
     // Set this to false to disable.
     private boolean mUserInactiveOverrideFromWindowManager;
@@ -521,6 +532,8 @@
     // The screen brightness to use while dozing.
     private int mDozeScreenBrightnessOverrideFromDreamManager = PowerManager.BRIGHTNESS_DEFAULT;
 
+    private float mDozeScreenBrightnessOverrideFromDreamManagerFloat =
+            PowerManager.BRIGHTNESS_INVALID_FLOAT;
     // Keep display state when dozing.
     private boolean mDrawWakeLockOverrideFromSidekick;
 
@@ -827,6 +840,91 @@
         mInattentiveSleepWarningOverlayController =
                 mInjector.createInattentiveSleepWarningController();
 
+        // Save brightness values:
+        // Get float values from config.
+        // Store float if valid
+        // Otherwise, get int values and convert to float and then store.
+        final float min = mContext.getResources().getFloat(com.android.internal.R.dimen
+                .config_screenBrightnessSettingMinimumFloat);
+        final float max = mContext.getResources().getFloat(com.android.internal.R.dimen
+                .config_screenBrightnessSettingMaximumFloat);
+        final float def = mContext.getResources().getFloat(com.android.internal.R.dimen
+                .config_screenBrightnessSettingDefaultFloat);
+        final float doze = mContext.getResources().getFloat(com.android.internal.R.dimen
+                .config_screenBrightnessDozeFloat);
+        final float dim = mContext.getResources().getFloat(com.android.internal.R.dimen
+                .config_screenBrightnessDimFloat);
+
+        if (min == INVALID_BRIGHTNESS_IN_CONFIG || max == INVALID_BRIGHTNESS_IN_CONFIG
+                || def == INVALID_BRIGHTNESS_IN_CONFIG) {
+            mScreenBrightnessMinimum = BrightnessSynchronizer.brightnessIntToFloat(
+                    mContext.getResources().getInteger(com.android.internal.R.integer
+                            .config_screenBrightnessSettingMinimum),
+                    PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON,
+                    PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX);
+            mScreenBrightnessMaximum = BrightnessSynchronizer.brightnessIntToFloat(
+                    mContext.getResources().getInteger(com.android.internal.R.integer
+                            .config_screenBrightnessSettingMaximum),
+                    PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON,
+                    PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX);
+            mScreenBrightnessDefault = BrightnessSynchronizer.brightnessIntToFloat(
+                    mContext.getResources().getInteger(com.android.internal.R.integer
+                            .config_screenBrightnessSettingDefault),
+                    PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON,
+                    PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX);
+        } else {
+            mScreenBrightnessMinimum = min;
+            mScreenBrightnessMaximum = max;
+            mScreenBrightnessDefault = def;
+        }
+        if (doze == INVALID_BRIGHTNESS_IN_CONFIG) {
+            mScreenBrightnessDoze = BrightnessSynchronizer.brightnessIntToFloat(
+                    mContext.getResources().getInteger(com.android.internal.R.integer
+                            .config_screenBrightnessDoze), PowerManager.BRIGHTNESS_OFF + 1,
+                    PowerManager.BRIGHTNESS_ON, PowerManager.BRIGHTNESS_MIN,
+                    PowerManager.BRIGHTNESS_MAX);
+        } else {
+            mScreenBrightnessDoze = doze;
+        }
+        if (dim == INVALID_BRIGHTNESS_IN_CONFIG) {
+            mScreenBrightnessDim = BrightnessSynchronizer.brightnessIntToFloat(
+                    mContext.getResources().getInteger(com.android.internal.R.integer
+                            .config_screenBrightnessDim), PowerManager.BRIGHTNESS_OFF + 1,
+                    PowerManager.BRIGHTNESS_ON, PowerManager.BRIGHTNESS_MIN,
+                    PowerManager.BRIGHTNESS_MAX);
+        } else {
+            mScreenBrightnessDim = dim;
+        }
+
+        final float vrMin = mContext.getResources().getFloat(com.android.internal.R.dimen
+                .config_screenBrightnessSettingForVrMinimumFloat);
+        final float vrMax = mContext.getResources().getFloat(com.android.internal.R.dimen
+                .config_screenBrightnessSettingForVrMaximumFloat);
+        final float vrDef = mContext.getResources().getFloat(com.android.internal.R.dimen
+                .config_screenBrightnessSettingForVrDefaultFloat);
+        if (vrMin == INVALID_BRIGHTNESS_IN_CONFIG || vrMax == INVALID_BRIGHTNESS_IN_CONFIG
+                || vrDef == INVALID_BRIGHTNESS_IN_CONFIG) {
+            mScreenBrightnessMinimumVr = BrightnessSynchronizer.brightnessIntToFloat(
+                    mContext.getResources().getInteger(com.android.internal.R.integer
+                            .config_screenBrightnessForVrSettingMinimum),
+                    PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON,
+                    PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX);
+            mScreenBrightnessMaximumVr = BrightnessSynchronizer.brightnessIntToFloat(
+                    mContext.getResources().getInteger(com.android.internal.R.integer
+                            .config_screenBrightnessForVrSettingMaximum),
+                    PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON,
+                    PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX);
+            mScreenBrightnessDefaultVr = BrightnessSynchronizer.brightnessIntToFloat(
+                    mContext.getResources().getInteger(com.android.internal.R.integer
+                            .config_screenBrightnessForVrSettingDefault),
+                    PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON,
+                    PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX);
+        } else {
+            mScreenBrightnessMinimumVr = vrMin;
+            mScreenBrightnessMaximumVr = vrMax;
+            mScreenBrightnessDefaultVr = vrDef;
+        }
+
         synchronized (mLock) {
             mWakeLockSuspendBlocker =
                     mInjector.createSuspendBlocker(this, "PowerManagerService.WakeLocks");
@@ -895,9 +993,12 @@
             mAttentionDetector.systemReady(mContext);
 
             PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-            mScreenBrightnessSettingMinimum = pm.getMinimumScreenBrightnessSetting();
-            mScreenBrightnessSettingMaximum = pm.getMaximumScreenBrightnessSetting();
-            mScreenBrightnessSettingDefault = pm.getDefaultScreenBrightnessSetting();
+            mScreenBrightnessSettingMinimum = pm.getBrightnessConstraint(
+                    PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
+            mScreenBrightnessSettingMaximum = pm.getBrightnessConstraint(
+                    PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
+            mScreenBrightnessSettingDefault = pm.getBrightnessConstraint(
+                    PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT);
 
             SensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper());
 
@@ -2672,7 +2773,7 @@
 
             // Determine appropriate screen brightness and auto-brightness adjustments.
             final boolean autoBrightness;
-            final int screenBrightnessOverride;
+            final float screenBrightnessOverride;
             if (!mBootCompleted) {
                 // Keep the brightness steady during boot. This requires the
                 // bootloader brightness and the default brightness to be identical.
@@ -2680,11 +2781,11 @@
                 screenBrightnessOverride = mScreenBrightnessSettingDefault;
             } else if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) {
                 autoBrightness = false;
-                screenBrightnessOverride = mScreenBrightnessOverrideFromWindowManager;
+                screenBrightnessOverride = mScreenBrightnessOverrideFromWindowManagerFloat;
             } else {
                 autoBrightness = (mScreenBrightnessModeSetting ==
                         Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
-                screenBrightnessOverride = -1;
+                screenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT;
             }
 
             // Update display power request.
@@ -2707,10 +2808,11 @@
                     }
                 }
                 mDisplayPowerRequest.dozeScreenBrightness =
-                        mDozeScreenBrightnessOverrideFromDreamManager;
+                        mDozeScreenBrightnessOverrideFromDreamManagerFloat;
             } else {
                 mDisplayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN;
-                mDisplayPowerRequest.dozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+                mDisplayPowerRequest.dozeScreenBrightness =
+                        PowerManager.BRIGHTNESS_INVALID_FLOAT;
             }
 
             mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest,
@@ -3426,10 +3528,13 @@
         }
     }
 
+    // TODO(brightnessfloat): change to float
     private void setScreenBrightnessOverrideFromWindowManagerInternal(int brightness) {
         synchronized (mLock) {
             if (mScreenBrightnessOverrideFromWindowManager != brightness) {
                 mScreenBrightnessOverrideFromWindowManager = brightness;
+                mScreenBrightnessOverrideFromWindowManagerFloat =
+                        BrightnessSynchronizer.brightnessIntToFloat(mContext, brightness);
                 mDirty |= DIRTY_SETTINGS;
                 updatePowerStateLocked();
             }
@@ -3462,6 +3567,9 @@
                     || mDozeScreenBrightnessOverrideFromDreamManager != screenBrightness) {
                 mDozeScreenStateOverrideFromDreamManager = screenState;
                 mDozeScreenBrightnessOverrideFromDreamManager = screenBrightness;
+                mDozeScreenBrightnessOverrideFromDreamManagerFloat =
+                        BrightnessSynchronizer.brightnessIntToFloat(mContext,
+                                mDozeScreenBrightnessOverrideFromDreamManager);
                 mDirty |= DIRTY_SETTINGS;
                 updatePowerStateLocked();
             }
@@ -3715,10 +3823,9 @@
                     + mMaximumScreenOffTimeoutFromDeviceAdmin + " (enforced="
                     + isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked() + ")");
             pw.println("  mStayOnWhilePluggedInSetting=" + mStayOnWhilePluggedInSetting);
-            pw.println("  mScreenBrightnessSetting=" + mScreenBrightnessSetting);
             pw.println("  mScreenBrightnessModeSetting=" + mScreenBrightnessModeSetting);
-            pw.println("  mScreenBrightnessOverrideFromWindowManager="
-                    + mScreenBrightnessOverrideFromWindowManager);
+            pw.println("  mScreenBrightnessOverrideFromWindowManagerFloat="
+                    + mScreenBrightnessOverrideFromWindowManagerFloat);
             pw.println("  mUserActivityTimeoutOverrideFromWindowManager="
                     + mUserActivityTimeoutOverrideFromWindowManager);
             pw.println("  mUserInactiveOverrideFromWindowManager="
@@ -3728,9 +3835,9 @@
             pw.println("  mDrawWakeLockOverrideFromSidekick=" + mDrawWakeLockOverrideFromSidekick);
             pw.println("  mDozeScreenBrightnessOverrideFromDreamManager="
                     + mDozeScreenBrightnessOverrideFromDreamManager);
-            pw.println("  mScreenBrightnessSettingMinimum=" + mScreenBrightnessSettingMinimum);
-            pw.println("  mScreenBrightnessSettingMaximum=" + mScreenBrightnessSettingMaximum);
-            pw.println("  mScreenBrightnessSettingDefault=" + mScreenBrightnessSettingDefault);
+            pw.println("  mScreenBrightnessSettingMinimumFloat=" + mScreenBrightnessSettingMinimum);
+            pw.println("  mScreenBrightnessSettingMaximumFloat=" + mScreenBrightnessSettingMaximum);
+            pw.println("  mScreenBrightnessSettingDefaultFloat=" + mScreenBrightnessSettingDefault);
             pw.println("  mDoubleTapWakeEnabled=" + mDoubleTapWakeEnabled);
             pw.println("  mIsVrModeEnabled=" + mIsVrModeEnabled);
             pw.println("  mForegroundProfile=" + mForegroundProfile);
@@ -4056,7 +4163,7 @@
             proto.write(
                     PowerServiceSettingsAndConfigurationDumpProto
                             .SCREEN_BRIGHTNESS_OVERRIDE_FROM_WINDOW_MANAGER,
-                    mScreenBrightnessOverrideFromWindowManager);
+                    mScreenBrightnessOverrideFromWindowManagerFloat);
             proto.write(
                     PowerServiceSettingsAndConfigurationDumpProto
                             .USER_ACTIVITY_TIMEOUT_OVERRIDE_FROM_WINDOW_MANAGER_MS,
@@ -4738,6 +4845,29 @@
             }
         }
 
+        public float getBrightnessConstraint(int constraint) {
+            switch (constraint) {
+                case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM:
+                    return mScreenBrightnessMinimum;
+                case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM:
+                    return mScreenBrightnessMaximum;
+                case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT:
+                    return mScreenBrightnessDefault;
+                case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM:
+                    return mScreenBrightnessDim;
+                case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DOZE:
+                    return mScreenBrightnessDoze;
+                case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR:
+                    return mScreenBrightnessMinimumVr;
+                case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM_VR:
+                    return mScreenBrightnessMaximumVr;
+                case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_VR:
+                    return mScreenBrightnessDefaultVr;
+                default:
+                    return PowerManager.BRIGHTNESS_INVALID_FLOAT;
+            }
+        }
+
         @Override // Binder call
         public boolean isInteractive() {
             final long ident = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/power/PreRebootLogger.java b/services/core/java/com/android/server/power/PreRebootLogger.java
new file mode 100644
index 0000000..cda00b4
--- /dev/null
+++ b/services/core/java/com/android/server/power/PreRebootLogger.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Environment;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+
+/**
+ * Provides utils to dump/wipe pre-reboot information.
+ */
+final class PreRebootLogger {
+    private static final String TAG = "PreRebootLogger";
+    private static final String PREREBOOT_DIR = "prereboot";
+
+    private static final String[] BUFFERS_TO_DUMP = {"system"};
+    private static final String[] SERVICES_TO_DUMP = {Context.ROLLBACK_SERVICE, "package"};
+
+    private static final Object sLock = new Object();
+
+    /**
+     * Process pre-reboot information. Dump pre-reboot information to {@link #PREREBOOT_DIR} if
+     * enabled {@link Settings.Global#ADB_ENABLED}; wipe dumped information otherwise.
+     */
+    static void log(Context context) {
+        log(context, getDumpDir());
+    }
+
+    @VisibleForTesting
+    static void log(Context context, @NonNull File dumpDir) {
+        if (Settings.Global.getInt(
+                context.getContentResolver(), Settings.Global.ADB_ENABLED, 0) == 1) {
+            Slog.d(TAG, "Dumping pre-reboot information...");
+            dump(dumpDir);
+        } else {
+            Slog.d(TAG, "Wiping pre-reboot information...");
+            wipe(dumpDir);
+        }
+    }
+
+    private static void dump(@NonNull File dumpDir) {
+        synchronized (sLock) {
+            for (String buffer : BUFFERS_TO_DUMP) {
+                dumpLogsLocked(dumpDir, buffer);
+            }
+            for (String service : SERVICES_TO_DUMP) {
+                dumpServiceLocked(dumpDir, service);
+            }
+        }
+    }
+
+    private static void wipe(@NonNull File dumpDir) {
+        synchronized (sLock) {
+            for (File file : dumpDir.listFiles()) {
+                file.delete();
+            }
+        }
+    }
+
+    private static File getDumpDir() {
+        final File dumpDir = new File(Environment.getDataMiscDirectory(), PREREBOOT_DIR);
+        if (!dumpDir.exists() || !dumpDir.isDirectory()) {
+            throw new UnsupportedOperationException("Pre-reboot dump directory not found");
+        }
+        return dumpDir;
+    }
+
+    @GuardedBy("sLock")
+    private static void dumpLogsLocked(@NonNull File dumpDir, @NonNull String buffer) {
+        try {
+            final File dumpFile = new File(dumpDir, buffer);
+            if (dumpFile.createNewFile()) {
+                dumpFile.setWritable(true /* writable */, true /* ownerOnly */);
+            } else {
+                // Wipes dumped information in existing file before recording new information.
+                new FileWriter(dumpFile, false).flush();
+            }
+
+            final String[] cmdline =
+                    {"logcat", "-d", "-b", buffer, "-f", dumpFile.getAbsolutePath()};
+            Runtime.getRuntime().exec(cmdline).waitFor();
+        } catch (IOException | InterruptedException e) {
+            Slog.d(TAG, "Dump system log buffer before reboot fail", e);
+        }
+    }
+
+    @GuardedBy("sLock")
+    private static void dumpServiceLocked(@NonNull File dumpDir, @NonNull String serviceName) {
+        final IBinder binder = ServiceManager.checkService(serviceName);
+        if (binder == null) {
+            return;
+        }
+
+        try {
+            final File dumpFile = new File(dumpDir, serviceName);
+            final ParcelFileDescriptor fd = ParcelFileDescriptor.open(dumpFile,
+                    ParcelFileDescriptor.MODE_CREATE | ParcelFileDescriptor.MODE_TRUNCATE
+                            | ParcelFileDescriptor.MODE_WRITE_ONLY);
+            binder.dump(fd.getFileDescriptor(), ArrayUtils.emptyArray(String.class));
+        } catch (FileNotFoundException | RemoteException e) {
+            Slog.d(TAG, String.format("Dump %s service before reboot fail", serviceName), e);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index cc1cddd..bc722f1 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -44,6 +44,7 @@
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.Slog;
 import android.util.TimingsTraceLog;
 import android.view.WindowManager;
 
@@ -446,6 +447,15 @@
             SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
         }
 
+        shutdownTimingLog.traceBegin("DumpPreRebootInfo");
+        try {
+            Slog.i(TAG, "Logging pre-reboot information...");
+            PreRebootLogger.log(mContext);
+        } catch (Exception e) {
+            Slog.e(TAG, "Failed to log pre-reboot information", e);
+        }
+        shutdownTimingLog.traceEnd(); // DumpPreRebootInfo
+
         metricStarted(METRIC_SEND_BROADCAST);
         shutdownTimingLog.traceBegin("SendShutdownBroadcast");
         Log.i(TAG, "Sending shutdown broadcast...");
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 5abd9f0..7b96777 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -181,15 +181,6 @@
     private int mNumPackageSessionsWithSuccess;
 
     /**
-     * A temp flag to facilitate merging of the 2 rollback collections managed by
-     * RollbackManagerServiceImpl. True if this rollback is in the process of enabling and was
-     * originally managed by RollbackManagerServiceImpl#mNewRollbacks.
-     * TODO: remove this flag when merge is completed.
-     */
-    @GuardedBy("mLock")
-    private boolean mIsNewRollback = false;
-
-    /**
      * Constructs a new, empty Rollback instance.
      *
      * @param rollbackId the id of the rollback.
@@ -837,18 +828,6 @@
         }
     }
 
-    void setIsNewRollback(boolean newRollback) {
-        synchronized (mLock) {
-            mIsNewRollback = newRollback;
-        }
-    }
-
-    boolean isNewRollback() {
-        synchronized (mLock) {
-            return mIsNewRollback;
-        }
-    }
-
     static String rollbackStateToString(@RollbackState int state) {
         switch (state) {
             case Rollback.ROLLBACK_STATE_ENABLING: return "enabling";
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 1421258..91e7cc9 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -164,8 +164,16 @@
         // Load rollback data from device storage.
         synchronized (mLock) {
             mRollbacks = mRollbackStore.loadRollbacks();
-            for (Rollback rollback : mRollbacks) {
-                mAllocatedRollbackIds.put(rollback.info.getRollbackId(), true);
+            if (!context.getPackageManager().isDeviceUpgrading()) {
+                for (Rollback rollback : mRollbacks) {
+                    mAllocatedRollbackIds.put(rollback.info.getRollbackId(), true);
+                }
+            } else {
+                // Delete rollbacks when build fingerprint has changed.
+                for (Rollback rollback : mRollbacks) {
+                    rollback.delete(mAppDataRollbackHelper);
+                }
+                mRollbacks.clear();
             }
         }
 
@@ -788,14 +796,13 @@
 
         Rollback newRollback;
         synchronized (mLock) {
-            // See if we already have a NewRollback that contains this package
-            // session. If not, create a NewRollback for the parent session
+            // See if we already have a Rollback that contains this package
+            // session. If not, create a new Rollback for the parent session
             // that we will use for all the packages in the session.
-            newRollback = getNewRollbackForPackageSessionLocked(packageSession.getSessionId());
+            newRollback = getRollbackForSessionLocked(packageSession.getSessionId());
             if (newRollback == null) {
                 newRollback = createNewRollbackLocked(parentSession);
                 mRollbacks.add(newRollback);
-                newRollback.setIsNewRollback(true);
             }
         }
         newRollback.addToken(token);
@@ -1148,24 +1155,23 @@
             }
 
             if (success) {
-                Rollback newRollback;
+                Rollback rollback;
                 synchronized (mLock) {
-                    newRollback = getNewRollbackForPackageSessionLocked(sessionId);
-                    if (newRollback != null && newRollback.notifySessionWithSuccess()) {
-                        mRollbacks.remove(newRollback);
-                        newRollback.setIsNewRollback(false);
-                    } else {
-                        // Not all child sessions finished with success.
-                        // Don't enable the rollback yet.
-                        newRollback = null;
+                    rollback = getRollbackForSessionLocked(sessionId);
+                    if (rollback == null || rollback.isStaged() || !rollback.isEnabling()
+                            || !rollback.notifySessionWithSuccess()) {
+                        return;
                     }
+                    // All child sessions finished with success. We can enable this rollback now.
+                    // TODO: refactor #completeEnableRollback so we won't remove 'rollback' from
+                    // mRollbacks here and add it back in #completeEnableRollback later.
+                    mRollbacks.remove(rollback);
                 }
-
-                if (newRollback != null) {
-                    Rollback rollback = completeEnableRollback(newRollback);
-                    if (rollback != null && !rollback.isStaged()) {
-                        makeRollbackAvailable(rollback);
-                    }
+                // TODO: Now #completeEnableRollback returns the same rollback object as the
+                // parameter on success. It would be more readable to return a boolean to indicate
+                // success or failure.
+                if (completeEnableRollback(rollback) != null) {
+                    makeRollbackAvailable(rollback);
                 }
             } else {
                 synchronized (mLock) {
@@ -1354,22 +1360,4 @@
         }
         return null;
     }
-
-    /**
-     * Returns the NewRollback associated with the given package session.
-     * Returns null if no NewRollback is found for the given package
-     * session.
-     */
-    @WorkerThread
-    @GuardedBy("mLock")
-    Rollback getNewRollbackForPackageSessionLocked(int packageSessionId) {
-        // We expect mRollbacks to be a very small list; linear search
-        // should be plenty fast.
-        for (Rollback rollback: mRollbacks) {
-            if (rollback.isNewRollback() && rollback.containsSessionId(packageSessionId)) {
-                return rollback;
-            }
-        }
-        return null;
-    }
 }
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 34d2c16..8164526 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -36,7 +36,7 @@
 import android.service.textclassifier.TextClassifierService;
 import android.service.textclassifier.TextClassifierService.ConnectionState;
 import android.text.TextUtils;
-import android.util.LruCache;
+import android.util.ArrayMap;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.textclassifier.ConversationActions;
@@ -65,6 +65,7 @@
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Queue;
 
@@ -146,11 +147,7 @@
     private final Object mLock;
     @GuardedBy("mLock")
     final SparseArray<UserState> mUserStates = new SparseArray<>();
-    // SystemTextClassifier.onDestroy() is not guaranteed to be called, use LruCache here
-    // to avoid leak.
-    @GuardedBy("mLock")
-    private final LruCache<TextClassificationSessionId, TextClassificationContext>
-            mSessionContextCache = new LruCache<>(40);
+    private final SessionCache mSessionCache;
     private final TextClassificationConstants mSettings;
     @Nullable
     private final String mDefaultTextClassifierPackage;
@@ -165,6 +162,7 @@
         PackageManager packageManager = mContext.getPackageManager();
         mDefaultTextClassifierPackage = packageManager.getDefaultTextClassifierPackageName();
         mSystemTextClassifierPackage = packageManager.getSystemTextClassifierPackageName();
+        mSessionCache = new SessionCache(mLock);
     }
 
     private void startListenSettings() {
@@ -314,7 +312,7 @@
                 classificationContext.getUseDefaultTextClassifier(),
                 service -> {
                     service.onCreateTextClassificationSession(classificationContext, sessionId);
-                    mSessionContextCache.put(sessionId, classificationContext);
+                    mSessionCache.put(sessionId, classificationContext);
                 },
                 "onCreateTextClassificationSession",
                 NO_OP_CALLBACK);
@@ -326,14 +324,14 @@
         Objects.requireNonNull(sessionId);
 
         synchronized (mLock) {
-            TextClassificationContext textClassificationContext =
-                    mSessionContextCache.get(sessionId);
+            final StrippedTextClassificationContext textClassificationContext =
+                    mSessionCache.get(sessionId);
             final int userId = textClassificationContext != null
-                    ? textClassificationContext.getUserId()
+                    ? textClassificationContext.userId
                     : UserHandle.getCallingUserId();
             final boolean useDefaultTextClassifier =
                     textClassificationContext != null
-                            ? textClassificationContext.getUseDefaultTextClassifier()
+                            ? textClassificationContext.useDefaultTextClassifier
                             : true;
             handleRequest(
                     userId,
@@ -342,7 +340,7 @@
                     useDefaultTextClassifier,
                     service -> {
                         service.onDestroyTextClassificationSession(sessionId);
-                        mSessionContextCache.remove(sessionId);
+                        mSessionCache.remove(sessionId);
                     },
                     "onDestroyTextClassificationSession",
                     NO_OP_CALLBACK);
@@ -409,7 +407,7 @@
                     pw.decreaseIndent();
                 }
             }
-            pw.println("Number of active sessions: " + mSessionContextCache.size());
+            pw.println("Number of active sessions: " + mSessionCache.size());
         }
     }
 
@@ -568,6 +566,81 @@
         }
     }
 
+    /**
+     * Stores the stripped down version of {@link TextClassificationContext}s, i.e. {@link
+     * StrippedTextClassificationContext},  keyed by {@link TextClassificationSessionId}. Sessions
+     * are cleaned up automatically when the client process is dead.
+     */
+    static final class SessionCache {
+        @NonNull
+        private final Object mLock;
+        @NonNull
+        @GuardedBy("mLock")
+        private final Map<TextClassificationSessionId, StrippedTextClassificationContext> mCache =
+                new ArrayMap<>();
+        @NonNull
+        @GuardedBy("mLock")
+        private final Map<TextClassificationSessionId, DeathRecipient> mDeathRecipients =
+                new ArrayMap<>();
+
+        SessionCache(@NonNull Object lock) {
+            mLock = Objects.requireNonNull(lock);
+        }
+
+        void put(@NonNull TextClassificationSessionId sessionId,
+                @NonNull TextClassificationContext textClassificationContext) {
+            synchronized (mLock) {
+                mCache.put(sessionId,
+                        new StrippedTextClassificationContext(textClassificationContext));
+                try {
+                    DeathRecipient deathRecipient = () -> remove(sessionId);
+                    sessionId.getToken().linkToDeath(deathRecipient, /* flags= */ 0);
+                    mDeathRecipients.put(sessionId, deathRecipient);
+                } catch (RemoteException e) {
+                    Slog.w(LOG_TAG, "SessionCache: Failed to link to death", e);
+                }
+            }
+        }
+
+        @Nullable
+        StrippedTextClassificationContext get(@NonNull TextClassificationSessionId sessionId) {
+            Objects.requireNonNull(sessionId);
+            synchronized (mLock) {
+                return mCache.get(sessionId);
+            }
+        }
+
+        void remove(@NonNull TextClassificationSessionId sessionId) {
+            Objects.requireNonNull(sessionId);
+            synchronized (mLock) {
+                DeathRecipient deathRecipient = mDeathRecipients.get(sessionId);
+                if (deathRecipient != null) {
+                    sessionId.getToken().unlinkToDeath(deathRecipient, /* flags= */ 0);
+                }
+                mDeathRecipients.remove(sessionId);
+                mCache.remove(sessionId);
+            }
+        }
+
+        int size() {
+            synchronized (mLock) {
+                return mCache.size();
+            }
+        }
+    }
+
+    /** A stripped down version of {@link TextClassificationContext}. */
+    static class StrippedTextClassificationContext {
+        @UserIdInt
+        public final int userId;
+        public final boolean useDefaultTextClassifier;
+
+        StrippedTextClassificationContext(TextClassificationContext textClassificationContext) {
+            userId = textClassificationContext.getUserId();
+            useDefaultTextClassifier = textClassificationContext.getUseDefaultTextClassifier();
+        }
+    }
+
     private final class UserState {
         @UserIdInt
         final int mUserId;
@@ -596,7 +669,8 @@
                 return mDefaultServiceState;
             }
             String textClassifierServicePackageOverride =
-                    mSettings.getTextClassifierServicePackageOverride();
+                    Binder.withCleanCallingIdentity(
+                            mSettings::getTextClassifierServicePackageOverride);
             if (!TextUtils.isEmpty(textClassifierServicePackageOverride)) {
                 if (textClassifierServicePackageOverride.equals(mDefaultTextClassifierPackage)) {
                     return mDefaultServiceState;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index ad57e1f..e26dac6 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -7686,7 +7686,7 @@
         mainWindow.getContentInsets(insets);
         InsetUtils.addInsets(insets, getLetterboxInsets());
         return new RemoteAnimationTarget(task.mTaskId, record.getMode(),
-                record.mAdapter.mCapturedLeash, !task.fillsParent(),
+                record.mAdapter.mCapturedLeash, !fillsParent(),
                 mainWindow.mWinAnimator.mLastClipRect, insets,
                 getPrefixOrderIndex(), record.mAdapter.mPosition,
                 record.mAdapter.mStackBounds, task.getWindowConfiguration(),
@@ -7704,4 +7704,9 @@
         }
         win.getAnimationFrames(outFrame, outInsets, outStableInsets, outSurfaceInsets);
     }
+
+    void setPictureInPictureParams(PictureInPictureParams p) {
+        pictureInPictureArgs.copyOnlySet(p);
+        getTask().getRootTask().setPictureInPictureParams(p);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 9030fce..ab8e975 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -60,18 +60,6 @@
 import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
 
-import static com.android.server.wm.TaskProto.ACTIVITIES;
-import static com.android.server.wm.TaskProto.ACTIVITY_TYPE;
-import static com.android.server.wm.TaskProto.BOUNDS;
-import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS;
-import static com.android.server.wm.TaskProto.DISPLAY_ID;
-import static com.android.server.wm.TaskProto.LAST_NON_FULLSCREEN_BOUNDS;
-import static com.android.server.wm.TaskProto.MIN_HEIGHT;
-import static com.android.server.wm.TaskProto.MIN_WIDTH;
-import static com.android.server.wm.TaskProto.ORIG_ACTIVITY;
-import static com.android.server.wm.TaskProto.REAL_ACTIVITY;
-import static com.android.server.wm.TaskProto.RESIZE_MODE;
-import static com.android.server.wm.TaskProto.RESUMED_ACTIVITY;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
@@ -115,14 +103,26 @@
 import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
 import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+import static com.android.server.wm.TaskProto.ACTIVITIES;
+import static com.android.server.wm.TaskProto.ACTIVITY_TYPE;
 import static com.android.server.wm.TaskProto.ADJUSTED_BOUNDS;
 import static com.android.server.wm.TaskProto.ADJUSTED_FOR_IME;
 import static com.android.server.wm.TaskProto.ADJUST_DIVIDER_AMOUNT;
 import static com.android.server.wm.TaskProto.ADJUST_IME_AMOUNT;
 import static com.android.server.wm.TaskProto.ANIMATING_BOUNDS;
+import static com.android.server.wm.TaskProto.BOUNDS;
 import static com.android.server.wm.TaskProto.DEFER_REMOVAL;
+import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS;
+import static com.android.server.wm.TaskProto.DISPLAY_ID;
 import static com.android.server.wm.TaskProto.FILLS_PARENT;
+import static com.android.server.wm.TaskProto.LAST_NON_FULLSCREEN_BOUNDS;
 import static com.android.server.wm.TaskProto.MINIMIZE_AMOUNT;
+import static com.android.server.wm.TaskProto.MIN_HEIGHT;
+import static com.android.server.wm.TaskProto.MIN_WIDTH;
+import static com.android.server.wm.TaskProto.ORIG_ACTIVITY;
+import static com.android.server.wm.TaskProto.REAL_ACTIVITY;
+import static com.android.server.wm.TaskProto.RESIZE_MODE;
+import static com.android.server.wm.TaskProto.RESUMED_ACTIVITY;
 import static com.android.server.wm.TaskProto.ROOT_TASK_ID;
 import static com.android.server.wm.TaskProto.SURFACE_HEIGHT;
 import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
@@ -828,6 +828,17 @@
         setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
                 false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
                 false /* creating */);
+        if (windowingMode == WINDOWING_MODE_PINNED) {
+            // This stack will be visible before SystemUI requests PiP animation to start, and if
+            // the selected animation type is fade in, this stack will be close first. If the
+            // animation does not start early, user may see this full screen window appear again
+            // after the closing transition finish, which could cause screen to flicker.
+            // Check if animation should start here in advance.
+            final BoundsAnimationController controller = getDisplay().mBoundsAnimationController;
+            if (controller.isAnimationTypeFadeIn()) {
+                setPinnedStackAlpha(0f);
+            }
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index ea5a71a..1859fae 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2807,6 +2807,7 @@
         }
     }
 
+    // TODO(148895075): deprecate and replace with task equivalents
     @Override
     public List<ActivityManager.StackInfo> getAllStackInfos() {
         enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getAllStackInfos()");
@@ -2833,6 +2834,7 @@
         }
     }
 
+    // TODO(148895075): deprecate and replace with task equivalents
     @Override
     public List<ActivityManager.StackInfo> getAllStackInfosOnDisplay(int displayId) {
         enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getAllStackInfos()");
@@ -4165,7 +4167,7 @@
                             return;
                         }
                         // Only update the saved args from the args that are set
-                        r.pictureInPictureArgs.copyOnlySet(params);
+                        r.setPictureInPictureParams(params);
                         final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
                         final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
                         // Adjust the source bounds by the insets for the transition down
@@ -4213,7 +4215,7 @@
                         "setPictureInPictureParams", token, params);
 
                 // Only update the saved args from the args that are set
-                r.pictureInPictureArgs.copyOnlySet(params);
+                r.setPictureInPictureParams(params);
                 if (r.inPinnedWindowingMode()) {
                     // If the activity is already in picture-in-picture, update the pinned stack now
                     // if it is not already expanding to fullscreen. Otherwise, the arguments will
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index 9b464c2..5385e2f 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -552,6 +552,10 @@
         return animationType;
     }
 
+    boolean isAnimationTypeFadeIn() {
+        return mAnimationType == FADE_IN;
+    }
+
     public Handler getHandler() {
         return mHandler;
     }
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
index 06e7b48..9e93e14 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
@@ -18,6 +18,9 @@
 
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 
+import android.content.res.Resources;
+import android.text.TextUtils;
+
 import com.android.server.wm.DisplayContent.TaskContainers;
 
 /**
@@ -42,7 +45,18 @@
      */
     protected final TaskContainers mTaskContainers;
 
-    DisplayAreaPolicy(WindowManagerService wmService,
+    /**
+     * Construct a new {@link DisplayAreaPolicy}
+     *
+     * @param wmService the window manager service instance
+     * @param content the display content for which the policy applies
+     * @param root the root display area under which the policy operates
+     * @param imeContainer the ime container that the policy must attach
+     * @param taskContainers the task container that the policy must attach
+     *
+     * @see #attachDisplayAreas()
+     */
+    protected DisplayAreaPolicy(WindowManagerService wmService,
             DisplayContent content, DisplayArea.Root root,
             DisplayArea<? extends WindowContainer> imeContainer, TaskContainers taskContainers) {
         mWmService = wmService;
@@ -119,5 +133,55 @@
                     throw new IllegalArgumentException("don't know how to sort " + token);
             }
         }
+
+        /** Provider for {@link DisplayAreaPolicy.Default platform-default display area policy}. */
+        static class Provider implements DisplayAreaPolicy.Provider {
+            @Override
+            public DisplayAreaPolicy instantiate(WindowManagerService wmService,
+                    DisplayContent content, DisplayArea.Root root,
+                    DisplayArea<? extends WindowContainer> imeContainer,
+                    TaskContainers taskContainers) {
+                return new DisplayAreaPolicy.Default(wmService, content, root, imeContainer,
+                        taskContainers);
+            }
+        }
+    }
+
+    /**
+     * Provider for {@link DisplayAreaPolicy} instances.
+     *
+     * By implementing this interface and overriding the
+     * {@code config_deviceSpecificDisplayAreaPolicyProvider}, a device-specific implementations
+     * of {@link DisplayAreaPolicy} can be supplied.
+     */
+    public interface Provider {
+        /**
+         * Instantiate a new DisplayAreaPolicy.
+         *
+         * @see DisplayAreaPolicy#DisplayAreaPolicy
+         */
+        DisplayAreaPolicy instantiate(WindowManagerService wmService,
+                DisplayContent content, DisplayArea.Root root,
+                DisplayArea<? extends WindowContainer> imeContainer,
+                TaskContainers taskContainers);
+
+        /**
+         * Instantiate the device-specific {@link Provider}.
+         */
+        static Provider fromResources(Resources res) {
+            String name = res.getString(
+                    com.android.internal.R.string.config_deviceSpecificDisplayAreaPolicyProvider);
+            if (TextUtils.isEmpty(name)) {
+                return new DisplayAreaPolicy.Default.Provider();
+            }
+            try {
+                return (Provider) Class.forName(name).newInstance();
+            } catch (ReflectiveOperationException | ClassCastException e) {
+                throw new IllegalStateException("Couldn't instantiate class " + name
+                        + " for config_deviceSpecificDisplayAreaPolicyProvider:"
+                        + " make sure it has a public zero-argument constructor"
+                        + " and implements DisplayAreaPolicy.Provider", e);
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 810aa34..3b658c0 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -304,8 +304,7 @@
 
     private final DisplayArea.Root mRootDisplayArea = new DisplayArea.Root(mWmService);
 
-    private final DisplayAreaPolicy mDisplayAreaPolicy = new DisplayAreaPolicy.Default(
-            mWmService, this, mRootDisplayArea, mImeWindowsContainers, mTaskContainers);
+    private final DisplayAreaPolicy mDisplayAreaPolicy;
 
     private WindowState mTmpWindow;
     private WindowState mTmpWindow2;
@@ -1027,6 +1026,8 @@
         super.addChild(mWindowContainers, null);
         super.addChild(mOverlayContainers, null);
 
+        mDisplayAreaPolicy = mWmService.mDisplayAreaPolicyProvider.instantiate(
+                mWmService, this, mRootDisplayArea, mImeWindowsContainers, mTaskContainers);
         mWindowContainers.addChildren();
 
         // Sets the display content for the children.
@@ -5659,7 +5660,8 @@
         return activityType == ACTIVITY_TYPE_STANDARD
                 && (windowingMode == WINDOWING_MODE_FULLSCREEN
                 || windowingMode == WINDOWING_MODE_FREEFORM
-                || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+                || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+                || windowingMode == WINDOWING_MODE_MULTI_WINDOW);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index c78707a..ca6bd2d 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -57,8 +57,8 @@
 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
@@ -855,7 +855,7 @@
             case TYPE_WALLPAPER:
                 // Dreams and wallpapers don't have an app window token and can thus not be
                 // letterboxed. Hence always let them extend under the cutout.
-                attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+                attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
                 break;
             case TYPE_NOTIFICATION_SHADE:
                 // If the Keyguard is in a hidden state (occluded by another window), we force to
@@ -1511,15 +1511,16 @@
         // nav bar and ensure the application doesn't see the event.
         if (navVisible || navAllowedHidden) {
             if (mInputConsumer != null) {
+                mInputConsumer.dismiss();
                 mHandler.sendMessage(
                         mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer));
                 mInputConsumer = null;
             }
         } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) {
-            mInputConsumer = mService.createInputConsumer(mHandler.getLooper(),
+            mInputConsumer = mDisplayContent.getInputMonitor().createInputConsumer(
+                    mHandler.getLooper(),
                     INPUT_CONSUMER_NAVIGATION,
-                    HideNavInputEventReceiver::new,
-                    displayFrames.mDisplayId);
+                    HideNavInputEventReceiver::new);
             // As long as mInputConsumer is active, hover events are not dispatched to the app
             // and the pointer icon is likely to become stale. Hide it to avoid confusion.
             InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL);
@@ -2298,15 +2299,24 @@
         // cropped / shifted to the displayFrame in WindowState.
         final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen
                 && type != TYPE_BASE_APPLICATION;
-
         // Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in
         // the cutout safe zone.
-        if (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
-                || cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER) {
+        if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
             final Rect displayCutoutSafeExceptMaybeBars = sTmpDisplayCutoutSafeExceptMaybeBarsRect;
             displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe);
+            if (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES) {
+                if (displayFrames.mDisplayWidth < displayFrames.mDisplayHeight) {
+                    displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE;
+                    displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
+                } else {
+                    displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE;
+                    displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE;
+                }
+            }
+
             if (layoutInScreen && layoutInsetDecor && !requestedFullscreen
-                    && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) {
+                    && (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
+                    || cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)) {
                 // At the top we have the status bar, so apps that are
                 // LAYOUT_IN_SCREEN | LAYOUT_INSET_DECOR but not FULLSCREEN
                 // already expect that there's an inset there and we don't need to exclude
@@ -2314,7 +2324,8 @@
                 displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE;
             }
             if (layoutInScreen && layoutInsetDecor && !requestedHideNavigation
-                    && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) {
+                    && (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
+                    || cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)) {
                 // Same for the navigation bar.
                 switch (mNavigationBarPosition) {
                     case NAV_BAR_BOTTOM:
@@ -3173,7 +3184,7 @@
 
     private void disposeInputConsumer(InputConsumer inputConsumer) {
         if (inputConsumer != null) {
-            inputConsumer.dismiss();
+            inputConsumer.dispose();
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index faa6e52..b638e49 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -103,9 +103,17 @@
         @Override
         public void dismiss() {
             synchronized (mService.mGlobalLock) {
-                if (mInputMonitor.destroyInputConsumer(mWindowHandle.name)) {
-                    mInputEventReceiver.dispose();
-                }
+                mInputMonitor.mInputConsumers.remove(mName);
+                hide(mInputMonitor.mInputTransaction);
+                mInputMonitor.updateInputWindowsLw(true /* force */);
+            }
+        }
+
+        @Override
+        public void dispose() {
+            synchronized (mService.mGlobalLock) {
+                disposeChannelsLw();
+                mInputEventReceiver.dispose();
             }
         }
     }
@@ -415,18 +423,18 @@
     }
 
     private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> {
-        InputConsumerImpl navInputConsumer;
-        InputConsumerImpl pipInputConsumer;
-        InputConsumerImpl wallpaperInputConsumer;
-        InputConsumerImpl recentsAnimationInputConsumer;
+        InputConsumerImpl mNavInputConsumer;
+        InputConsumerImpl mPipInputConsumer;
+        InputConsumerImpl mWallpaperInputConsumer;
+        InputConsumerImpl mRecentsAnimationInputConsumer;
 
-        private boolean mAddInputConsumerHandle;
+        private boolean mAddNavInputConsumerHandle;
         private boolean mAddPipInputConsumerHandle;
         private boolean mAddWallpaperInputConsumerHandle;
         private boolean mAddRecentsAnimationInputConsumerHandle;
 
-        boolean inDrag;
-        WallpaperController wallpaperController;
+        boolean mInDrag;
+        WallpaperController mWallpaperController;
 
         // An invalid window handle that tells SurfaceFlinger not update the input info.
         final InputWindowHandle mInvalidInputWindow = new InputWindowHandle(null, mDisplayId);
@@ -434,20 +442,20 @@
         private void updateInputWindows(boolean inDrag) {
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows");
 
-            navInputConsumer = getInputConsumer(INPUT_CONSUMER_NAVIGATION);
-            pipInputConsumer = getInputConsumer(INPUT_CONSUMER_PIP);
-            wallpaperInputConsumer = getInputConsumer(INPUT_CONSUMER_WALLPAPER);
-            recentsAnimationInputConsumer = getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
+            mNavInputConsumer = getInputConsumer(INPUT_CONSUMER_NAVIGATION);
+            mPipInputConsumer = getInputConsumer(INPUT_CONSUMER_PIP);
+            mWallpaperInputConsumer = getInputConsumer(INPUT_CONSUMER_WALLPAPER);
+            mRecentsAnimationInputConsumer = getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
 
-            mAddInputConsumerHandle = navInputConsumer != null;
-            mAddPipInputConsumerHandle = pipInputConsumer != null;
-            mAddWallpaperInputConsumerHandle = wallpaperInputConsumer != null;
-            mAddRecentsAnimationInputConsumerHandle = recentsAnimationInputConsumer != null;
+            mAddNavInputConsumerHandle = mNavInputConsumer != null;
+            mAddPipInputConsumerHandle = mPipInputConsumer != null;
+            mAddWallpaperInputConsumerHandle = mWallpaperInputConsumer != null;
+            mAddRecentsAnimationInputConsumerHandle = mRecentsAnimationInputConsumer != null;
 
             mTmpRect.setEmpty();
             mDisableWallpaperTouchEvents = false;
-            this.inDrag = inDrag;
-            wallpaperController = mDisplayContent.mWallpaperController;
+            mInDrag = inDrag;
+            mWallpaperController = mDisplayContent.mWallpaperController;
 
             resetInputConsumers(mInputTransaction);
 
@@ -455,7 +463,7 @@
                     true /* traverseTopToBottom */);
 
             if (mAddWallpaperInputConsumerHandle) {
-                wallpaperInputConsumer.show(mInputTransaction, 0);
+                mWallpaperInputConsumer.show(mInputTransaction, 0);
             }
 
             if (mApplyImmediately) {
@@ -494,8 +502,8 @@
                 if (recentsAnimationController != null
                         && recentsAnimationController.shouldApplyInputConsumer(w.mActivityRecord)) {
                     if (recentsAnimationController.updateInputConsumerForApp(
-                            recentsAnimationInputConsumer.mWindowHandle, hasFocus)) {
-                        recentsAnimationInputConsumer.show(mInputTransaction, w);
+                            mRecentsAnimationInputConsumer.mWindowHandle, hasFocus)) {
+                        mRecentsAnimationInputConsumer.show(mInputTransaction, w);
                         mAddRecentsAnimationInputConsumerHandle = false;
                     }
                 }
@@ -505,25 +513,25 @@
                 if (mAddPipInputConsumerHandle) {
                     // Update the bounds of the Pip input consumer to match the window bounds.
                     w.getBounds(mTmpRect);
-                    pipInputConsumer.layout(mInputTransaction, mTmpRect);
+                    mPipInputConsumer.layout(mInputTransaction, mTmpRect);
 
                     // The touchable region is relative to the surface top-left
                     mTmpRect.offsetTo(0, 0);
-                    pipInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect);
-                    pipInputConsumer.show(mInputTransaction, w);
+                    mPipInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect);
+                    mPipInputConsumer.show(mInputTransaction, w);
                     mAddPipInputConsumerHandle = false;
                 }
             }
 
-            if (mAddInputConsumerHandle) {
-                navInputConsumer.show(mInputTransaction, w);
-                mAddInputConsumerHandle = false;
+            if (mAddNavInputConsumerHandle) {
+                mNavInputConsumer.show(mInputTransaction, w);
+                mAddNavInputConsumerHandle = false;
             }
 
             if (mAddWallpaperInputConsumerHandle) {
                 if (w.mAttrs.type == TYPE_WALLPAPER && w.isVisibleLw()) {
                     // Add the wallpaper input consumer above the first visible wallpaper.
-                    wallpaperInputConsumer.show(mInputTransaction, w);
+                    mWallpaperInputConsumer.show(mInputTransaction, w);
                     mAddWallpaperInputConsumerHandle = false;
                 }
             }
@@ -531,13 +539,13 @@
             if ((privateFlags & PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS) != 0) {
                 mDisableWallpaperTouchEvents = true;
             }
-            final boolean hasWallpaper = wallpaperController.isWallpaperTarget(w)
+            final boolean hasWallpaper = mWallpaperController.isWallpaperTarget(w)
                     && !mService.mPolicy.isKeyguardShowing()
                     && !mDisableWallpaperTouchEvents;
 
             // If there's a drag in progress and 'child' is a potential drop target,
             // make sure it's been told about the drag
-            if (inDrag && isVisible && w.getDisplayContent().isDefaultDisplay) {
+            if (mInDrag && isVisible && w.getDisplayContent().isDefaultDisplay) {
                 mService.mDragDropController.sendDragStartedIfNeededLocked(w);
             }
 
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index d0179ad..51b9916 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -44,6 +44,7 @@
 import android.view.WindowInsetsAnimationCallback;
 import android.view.WindowInsetsAnimationControlListener;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.DisplayThread;
 
 /**
@@ -107,11 +108,11 @@
             changed = true;
         }
         if (changed) {
-            startAnimation(mShowingTransientTypes, true, () -> {
+            mPolicy.getStatusBarManagerInternal().showTransient(mDisplayContent.getDisplayId(),
+                    mShowingTransientTypes.toArray());
+            updateBarControlTarget(mFocusedWin);
+            startAnimation(true /* show */, () -> {
                 synchronized (mDisplayContent.mWmService.mGlobalLock) {
-                    mPolicy.getStatusBarManagerInternal().showTransient(
-                            mDisplayContent.getDisplayId(),
-                            mShowingTransientTypes.toArray());
                     mStateController.notifyInsetsChanged();
                 }
             });
@@ -122,7 +123,7 @@
         if (mShowingTransientTypes.size() == 0) {
             return;
         }
-        startAnimation(mShowingTransientTypes, false, () -> {
+        startAnimation(false /* show */, () -> {
             synchronized (mDisplayContent.mWmService.mGlobalLock) {
                 mShowingTransientTypes.clear();
                 mStateController.notifyInsetsChanged();
@@ -268,18 +269,20 @@
         return isDockedStackVisible || isFreeformStackVisible || isResizing;
     }
 
-    private void startAnimation(IntArray internalTypes, boolean show, Runnable callback) {
+    @VisibleForTesting
+    void startAnimation(boolean show, Runnable callback) {
         int typesReady = 0;
         final SparseArray<InsetsSourceControl> controls = new SparseArray<>();
-        updateBarControlTarget(mFocusedWin);
-        for (int i = internalTypes.size() - 1; i >= 0; i--) {
+        final IntArray showingTransientTypes = mShowingTransientTypes;
+        for (int i = showingTransientTypes.size() - 1; i >= 0; i--) {
             InsetsSourceProvider provider =
-                    mStateController.getSourceProvider(internalTypes.get(i));
-            if (provider == null) continue;
-            InsetsSourceControl control = provider.getControl(provider.getControlTarget());
-            if (control == null || control.getLeash() == null) continue;
-            typesReady |= InsetsState.toPublicType(internalTypes.get(i));
-            controls.put(control.getType(), control);
+                    mStateController.getSourceProvider(showingTransientTypes.get(i));
+            InsetsSourceControl control = provider.getControl(mTransientControlTarget);
+            if (control == null || control.getLeash() == null) {
+                continue;
+            }
+            typesReady |= InsetsState.toPublicType(showingTransientTypes.get(i));
+            controls.put(control.getType(), new InsetsSourceControl(control));
         }
         controlAnimationUnchecked(typesReady, controls, show, callback);
     }
@@ -335,7 +338,6 @@
             private InsetsPolicyAnimationControlListener mListener;
 
             InsetsPolicyAnimationControlCallbacks(InsetsPolicyAnimationControlListener listener) {
-                super();
                 mListener = listener;
             }
 
@@ -353,9 +355,11 @@
                         InsetsController.INTERPOLATOR, true,
                         show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
                                 : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN);
+                SurfaceAnimationThread.getHandler().post(
+                        () -> mListener.onReady(mAnimationControl, typesReady));
             }
 
-            /** Called on SurfaceAnimationThread lock without global WM lock held. */
+            /** Called on SurfaceAnimationThread without global WM lock held. */
             @Override
             public void scheduleApplyChangeInsets() {
                 InsetsState state = getState();
@@ -384,7 +388,7 @@
                 return overrideState;
             }
 
-            /** Called on SurfaceAnimationThread lock without global WM lock held. */
+            /** Called on SurfaceAnimationThread without global WM lock held. */
             @Override
             public void applySurfaceParams(
                     final SyncRtSurfaceTransactionApplier.SurfaceParams... params) {
@@ -396,14 +400,12 @@
                 t.apply();
             }
 
-            /** Called on SurfaceAnimationThread lock without global WM lock held. */
             @Override
             public void startAnimation(InsetsAnimationControlImpl controller,
                     WindowInsetsAnimationControlListener listener, int types,
                     WindowInsetsAnimationCallback.InsetsAnimation animation,
                     WindowInsetsAnimationCallback.AnimationBounds bounds,
                     int layoutDuringAnimation) {
-                SurfaceAnimationThread.getHandler().post(() -> listener.onReady(controller, types));
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 7986659..0d3f6b9 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -59,6 +59,7 @@
     private final InsetsSourceControl mFakeControl;
     private @Nullable InsetsSourceControl mControl;
     private @Nullable InsetsControlTarget mControlTarget;
+    private @Nullable InsetsControlTarget mPendingControlTarget;
     private @Nullable InsetsControlTarget mFakeControlTarget;
 
     private @Nullable ControlAdapter mAdapter;
@@ -140,8 +141,9 @@
             mSource.setVisibleFrame(null);
         } else if (mControllable) {
             mWin.setControllableInsetProvider(this);
-            if (mControlTarget != null) {
-                updateControlForTarget(mControlTarget, true /* force */);
+            if (mPendingControlTarget != null) {
+                updateControlForTarget(mPendingControlTarget, true /* force */);
+                mPendingControlTarget = null;
             }
         }
     }
@@ -245,7 +247,7 @@
             setWindow(null, null, null);
         }
         if (mWin == null) {
-            mControlTarget = target;
+            mPendingControlTarget = target;
             return;
         }
         if (target == mControlTarget && !force) {
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index caaa430..2d7d3f1 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.view.InsetsState.ITYPE_CAPTION_BAR;
 import static android.view.InsetsState.ITYPE_IME;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
@@ -89,6 +90,12 @@
         if (type == ITYPE_NAVIGATION_BAR) {
             state.removeSource(ITYPE_IME);
             state.removeSource(ITYPE_STATUS_BAR);
+            state.removeSource(ITYPE_CAPTION_BAR);
+        }
+
+        // Status bar doesn't get influenced by caption bar
+        if (type == ITYPE_STATUS_BAR) {
+            state.removeSource(ITYPE_CAPTION_BAR);
         }
 
         // IME needs different frames for certain cases (e.g. navigation bar in gesture nav).
@@ -212,18 +219,18 @@
     /**
      * Called when the focused window that is able to control the system bars changes.
      *
-     * @param topControlling The target that is now able to control the top bar appearance
-     *                       and visibility.
+     * @param statusControlling The target that is now able to control the status bar appearance
+     *                          and visibility.
      * @param navControlling The target that is now able to control the nav bar appearance
      *                       and visibility.
      */
-    void onBarControlTargetChanged(@Nullable InsetsControlTarget topControlling,
-            @Nullable InsetsControlTarget fakeTopControlling,
+    void onBarControlTargetChanged(@Nullable InsetsControlTarget statusControlling,
+            @Nullable InsetsControlTarget fakeStatusControlling,
             @Nullable InsetsControlTarget navControlling,
             @Nullable InsetsControlTarget fakeNavControlling) {
-        onControlChanged(ITYPE_STATUS_BAR, topControlling);
+        onControlChanged(ITYPE_STATUS_BAR, statusControlling);
         onControlChanged(ITYPE_NAVIGATION_BAR, navControlling);
-        onControlFakeTargetChanged(ITYPE_STATUS_BAR, fakeTopControlling);
+        onControlFakeTargetChanged(ITYPE_STATUS_BAR, fakeStatusControlling);
         onControlFakeTargetChanged(ITYPE_NAVIGATION_BAR, fakeNavControlling);
         notifyPendingInsetsControlChanged();
     }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 9fb8e2f..9c23ed3 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -99,6 +99,7 @@
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
 import android.app.AppGlobals;
+import android.app.PictureInPictureParams;
 import android.app.TaskInfo;
 import android.app.WindowConfiguration;
 import android.content.ComponentName;
@@ -463,6 +464,11 @@
      */
     ITaskOrganizer mTaskOrganizer;
 
+    /**
+     * Last Picture-in-Picture params applicable to the task. Updated when the app
+     * enters Picture-in-Picture or when setPictureInPictureParams is called.
+     */
+    PictureInPictureParams mPictureInPictureParams = new PictureInPictureParams.Builder().build();
 
     /**
      * Don't use constructor directly. Use {@link #create(ActivityTaskManagerService, int,
@@ -3221,6 +3227,12 @@
         //                    order changes.
         final Task top = getTopMostTask();
         info.resizeMode = top != null ? top.mResizeMode : mResizeMode;
+
+        if (mPictureInPictureParams.empty()) {
+            info.pictureInPictureParams = null;
+        } else {
+            info.pictureInPictureParams = mPictureInPictureParams;
+        }
     }
 
     /**
@@ -3959,4 +3971,10 @@
     void onWindowFocusChanged(boolean hasFocus) {
         updateShadowsRadius(hasFocus, getPendingTransaction());
     }
+
+    void setPictureInPictureParams(PictureInPictureParams p) {
+        mPictureInPictureParams.copyOnlySet(p);
+        mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
+                this, true /* force */);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 0a0530c9..4b13a0c 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -43,6 +43,7 @@
 import android.view.SurfaceControl;
 import android.view.WindowContainerTransaction;
 
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.function.pooled.PooledConsumer;
 import com.android.internal.util.function.pooled.PooledLambda;
 
@@ -379,7 +380,8 @@
     }
 
     @Override
-    public List<RunningTaskInfo> getChildTasks(IWindowContainer parent) {
+    public List<RunningTaskInfo> getChildTasks(IWindowContainer parent,
+            @Nullable int[] activityTypes) {
         enforceStackPermission("getChildTasks()");
         final long ident = Binder.clearCallingIdentity();
         try {
@@ -405,6 +407,10 @@
                 for (int i = dc.getStackCount() - 1; i >= 0; --i) {
                     final ActivityStack as = dc.getStackAt(i);
                     if (as.getTile() == container) {
+                        if (activityTypes != null
+                                && !ArrayUtils.contains(activityTypes, as.getActivityType())) {
+                            continue;
+                        }
                         final RunningTaskInfo info = new RunningTaskInfo();
                         as.fillTaskInfo(info);
                         out.add(info);
@@ -417,6 +423,40 @@
         }
     }
 
+    @Override
+    public List<RunningTaskInfo> getRootTasks(int displayId, @Nullable int[] activityTypes) {
+        enforceStackPermission("getRootTasks()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final DisplayContent dc =
+                        mService.mRootWindowContainer.getDisplayContent(displayId);
+                if (dc == null) {
+                    throw new IllegalArgumentException("Display " + displayId + " doesn't exist");
+                }
+                ArrayList<RunningTaskInfo> out = new ArrayList<>();
+                for (int i = dc.getStackCount() - 1; i >= 0; --i) {
+                    final ActivityStack task = dc.getStackAt(i);
+                    if (task.getTile() != null) {
+                        // a tile is supposed to look like a parent, so don't include their
+                        // "children" here. They can be accessed via getChildTasks()
+                        continue;
+                    }
+                    if (activityTypes != null
+                            && !ArrayUtils.contains(activityTypes, task.getActivityType())) {
+                        continue;
+                    }
+                    final RunningTaskInfo info = new RunningTaskInfo();
+                    task.fillTaskInfo(info);
+                    out.add(info);
+                }
+                return out;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
     private int sanitizeAndApplyChange(WindowContainer container,
             WindowContainerTransaction.Change change) {
         if (!(container instanceof Task)) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a370093..8f9caea 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -187,6 +187,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.WorkSource;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.service.vr.IVrManager;
 import android.service.vr.IVrStateCallbacks;
@@ -229,7 +230,6 @@
 import android.view.InputChannel;
 import android.view.InputDevice;
 import android.view.InputEvent;
-import android.view.InputEventReceiver;
 import android.view.InputWindowHandle;
 import android.view.InsetsState;
 import android.view.KeyEvent;
@@ -309,6 +309,8 @@
         implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowManagerService" : TAG_WM;
 
+    private static final String WM_USE_BLAST_ADAPTER_FLAG = "wm_use_blast_adapter";
+
     static final int LAYOUT_REPEAT_THRESHOLD = 4;
 
     static final boolean PROFILE_ORIENTATION = false;
@@ -413,6 +415,8 @@
 
     final WindowTracing mWindowTracing;
 
+    final DisplayAreaPolicy.Provider mDisplayAreaPolicyProvider;
+
     final private KeyguardDisableHandler mKeyguardDisableHandler;
     // TODO: eventually unify all keyguard state in a common place instead of having it spread over
     // AM's KeyguardController and the policy's KeyguardServiceDelegate.
@@ -623,6 +627,9 @@
     // The root of the device window hierarchy.
     RootWindowContainer mRoot;
 
+    // Whether the system should use BLAST for ViewRootImpl
+    final boolean mUseBLAST;
+
     int mDockedStackCreateMode = SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
     Rect mDockedStackCreateBounds;
 
@@ -1137,6 +1144,10 @@
         mAnimator = new WindowAnimator(this);
         mRoot = new RootWindowContainer(this);
 
+        mUseBLAST = DeviceConfig.getBoolean(
+                    DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
+                    WM_USE_BLAST_ADAPTER_FLAG, false);
+
         mWindowPlacerLocked = new WindowSurfacePlacer(this);
         mTaskSnapshotController = new TaskSnapshotController(this);
 
@@ -1255,6 +1266,10 @@
 
         LocalServices.addService(WindowManagerInternal.class, new LocalService());
         mEmbeddedWindowController = new EmbeddedWindowController(mGlobalLock);
+
+        mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources(
+                mContext.getResources());
+
         setGlobalShadowSettings();
     }
 
@@ -5051,6 +5066,11 @@
     }
 
     @Override
+    public boolean useBLAST() {
+        return mUseBLAST;
+    }
+
+    @Override
     public void getInitialDisplaySize(int displayId, Point size) {
         synchronized (mGlobalLock) {
             final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
@@ -5742,20 +5762,6 @@
     }
 
     @Override
-    public WindowManagerPolicy.InputConsumer createInputConsumer(Looper looper, String name,
-            InputEventReceiver.Factory inputEventReceiverFactory, int displayId) {
-        synchronized (mGlobalLock) {
-            DisplayContent displayContent = mRoot.getDisplayContent(displayId);
-            if (displayContent != null) {
-                return displayContent.getInputMonitor().createInputConsumer(looper, name,
-                        inputEventReceiverFactory);
-            } else {
-                return null;
-            }
-        }
-    }
-
-    @Override
     public void createInputConsumer(IBinder token, String name, int displayId,
             InputChannel inputChannel) {
         synchronized (mGlobalLock) {
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 49c7e0a..e888f2a 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -2,6 +2,7 @@
     name: "libservices.core",
     defaults: ["libservices.core-libs"],
 
+    cpp_std: "c++2a",
     cflags: [
         "-Wall",
         "-Werror",
@@ -55,6 +56,7 @@
         "com_android_server_am_CachedAppOptimizer.cpp",
         "com_android_server_am_LowMemDetector.cpp",
         "com_android_server_incremental_IncrementalManagerService.cpp",
+        "com_android_server_pm_PackageManagerShellCommandDataLoader.cpp",
         "onload.cpp",
         ":lib_networkStatsFactory_native",
     ],
@@ -125,6 +127,8 @@
         "libnetdbpf",
         "libnetdutils",
         "libpsi",
+        "libdataloader",
+        "libincfs",
         "android.hardware.audio.common@2.0",
         "android.hardware.broadcastradio@1.0",
         "android.hardware.broadcastradio@1.1",
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 1a8f1f9..d0eaa48 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -26,17 +26,18 @@
 #include <android/hardware/gnss/1.0/IGnssMeasurement.h>
 #include <android/hardware/gnss/1.1/IGnssMeasurement.h>
 #include <android/hardware/gnss/2.0/IGnssMeasurement.h>
+#include <android/hardware/gnss/2.1/IGnssAntennaInfo.h>
 #include <android/hardware/gnss/2.1/IGnssMeasurement.h>
 #include <android/hardware/gnss/measurement_corrections/1.0/IMeasurementCorrections.h>
 #include <android/hardware/gnss/measurement_corrections/1.1/IMeasurementCorrections.h>
 #include <android/hardware/gnss/visibility_control/1.0/IGnssVisibilityControl.h>
 #include <nativehelper/JNIHelp.h>
-#include "jni.h"
-#include "hardware_legacy/power.h"
-#include "utils/Log.h"
-#include "utils/misc.h"
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/Log.h"
+#include "hardware_legacy/power.h"
+#include "jni.h"
+#include "utils/Log.h"
+#include "utils/misc.h"
 
 #include <arpa/inet.h>
 #include <cinttypes>
@@ -54,6 +55,12 @@
 static jclass class_gnssNavigationMessage;
 static jclass class_gnssClock;
 static jclass class_gnssConfiguration_halInterfaceVersion;
+static jclass class_gnssAntennaInfo;
+static jclass class_phaseCenterOffsetCoordinates;
+static jclass class_phaseCenterVariationCorrections;
+static jclass class_signalGainCorrections;
+static jclass class_arrayList;
+static jclass class_doubleArray;
 
 static jobject mCallbacksObj = nullptr;
 
@@ -78,6 +85,7 @@
 static jmethodID method_reportGeofencePauseStatus;
 static jmethodID method_reportGeofenceResumeStatus;
 static jmethodID method_reportMeasurementData;
+static jmethodID method_reportAntennaInfo;
 static jmethodID method_reportNavigationMessages;
 static jmethodID method_reportLocationBatch;
 static jmethodID method_reportGnssServiceDied;
@@ -114,6 +122,12 @@
 static jmethodID method_gnssClockCtor;
 static jmethodID method_gnssMeasurementCtor;
 static jmethodID method_halInterfaceVersionCtor;
+static jmethodID method_gnssAntennaInfoCtor;
+static jmethodID method_phaseCenterOffsetCoordinatesCtor;
+static jmethodID method_phaseCenterVariationCorrectionsCtor;
+static jmethodID method_signalGainCorrectionsCtor;
+static jmethodID method_arrayListCtor;
+static jmethodID method_arrayListAdd;
 
 /*
  * Save a pointer to JavaVm to attach/detach threads executing
@@ -171,6 +185,8 @@
 using IGnssConfiguration_V2_1 = android::hardware::gnss::V2_1::IGnssConfiguration;
 using IGnssDebug_V1_0 = android::hardware::gnss::V1_0::IGnssDebug;
 using IGnssDebug_V2_0 = android::hardware::gnss::V2_0::IGnssDebug;
+using IGnssAntennaInfo = android::hardware::gnss::V2_1::IGnssAntennaInfo;
+using IGnssAntennaInfoCallback = android::hardware::gnss::V2_1::IGnssAntennaInfoCallback;
 using IGnssMeasurement_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurement;
 using IGnssMeasurement_V1_1 = android::hardware::gnss::V1_1::IGnssMeasurement;
 using IGnssMeasurement_V2_0 = android::hardware::gnss::V2_0::IGnssMeasurement;
@@ -241,6 +257,7 @@
 sp<IMeasurementCorrections_V1_0> gnssCorrectionsIface_V1_0 = nullptr;
 sp<IMeasurementCorrections_V1_1> gnssCorrectionsIface_V1_1 = nullptr;
 sp<IGnssVisibilityControl> gnssVisibilityControlIface = nullptr;
+sp<IGnssAntennaInfo> gnssAntennaInfoIface = nullptr;
 
 #define WAKE_LOCK_NAME  "GPS"
 
@@ -1051,6 +1068,195 @@
 }
 
 /*
+ * GnssAntennaInfoCallback implements the callback methods required for the
+ * GnssAntennaInfo interface.
+ */
+struct GnssAntennaInfoCallback : public IGnssAntennaInfoCallback {
+    // Methods from V2_1::GnssAntennaInfoCallback follow.
+    Return<void> gnssAntennaInfoCb(
+            const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos);
+
+private:
+    jobject translateAllGnssAntennaInfos(
+            JNIEnv* env,
+            const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos);
+    jobject translateSingleGnssAntennaInfo(
+            JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo);
+    jobject translatePhaseCenterOffsetCoordinates(
+            JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo);
+    jobject translatePhaseCenterVariationCorrections(
+            JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo);
+    jobject translateSignalGainCorrections(
+            JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo);
+    jobjectArray translate2dDoubleArray(JNIEnv* env,
+                                        const hidl_vec<IGnssAntennaInfoCallback::Row>& array);
+    void translateAndReportGnssAntennaInfo(
+            const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos);
+    void reportAntennaInfo(JNIEnv* env, const jobject antennaInfosArray);
+};
+
+Return<void> GnssAntennaInfoCallback::gnssAntennaInfoCb(
+        const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
+    translateAndReportGnssAntennaInfo(gnssAntennaInfos);
+    return Void();
+}
+
+jobjectArray GnssAntennaInfoCallback::translate2dDoubleArray(
+        JNIEnv* env, const hidl_vec<IGnssAntennaInfoCallback::Row>& array) {
+    jsize numRows = array.size();
+    if (numRows == 0) {
+        // Empty array
+        return NULL;
+    }
+    jsize numCols = array[0].row.size();
+    if (numCols <= 1) {
+        // phi angle separation is computed as 180.0 / (numColumns - 1), so can't be < 2.
+        return NULL;
+    }
+
+    // Allocate array of double arrays
+    jobjectArray returnArray = env->NewObjectArray(numRows, class_doubleArray, NULL);
+
+    // Create each double array
+    for (uint8_t i = 0; i < numRows; i++) {
+        jdoubleArray doubleArray = env->NewDoubleArray(numCols);
+        env->SetDoubleArrayRegion(doubleArray, (jsize)0, numCols, array[i].row.data());
+        env->SetObjectArrayElement(returnArray, (jsize)i, doubleArray);
+        env->DeleteLocalRef(doubleArray);
+    }
+    return returnArray;
+}
+
+jobject GnssAntennaInfoCallback::translateAllGnssAntennaInfos(
+        JNIEnv* env, const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
+    jobject arrayList = env->NewObject(class_arrayList,
+                                       method_arrayListCtor); // Create new ArrayList instance
+
+    for (auto gnssAntennaInfo : gnssAntennaInfos) {
+        jobject gnssAntennaInfoObject = translateSingleGnssAntennaInfo(env, gnssAntennaInfo);
+
+        env->CallBooleanMethod(arrayList, method_arrayListAdd,
+                               gnssAntennaInfoObject); // Add the antennaInfo to the ArrayList
+
+        // Delete Local Refs
+        env->DeleteLocalRef(gnssAntennaInfoObject);
+    }
+    return arrayList;
+}
+
+jobject GnssAntennaInfoCallback::translatePhaseCenterOffsetCoordinates(
+        JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
+    jobject phaseCenterOffsetCoordinates =
+            env->NewObject(class_phaseCenterOffsetCoordinates,
+                           method_phaseCenterOffsetCoordinatesCtor,
+                           gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.x,
+                           gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.xUncertainty,
+                           gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.y,
+                           gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.yUncertainty,
+                           gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.z,
+                           gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.zUncertainty);
+
+    return phaseCenterOffsetCoordinates;
+}
+
+jobject GnssAntennaInfoCallback::translatePhaseCenterVariationCorrections(
+        JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
+    if (gnssAntennaInfo.phaseCenterVariationCorrectionMillimeters == NULL ||
+        gnssAntennaInfo.phaseCenterVariationCorrectionUncertaintyMillimeters == NULL) {
+        return NULL;
+    }
+
+    jobjectArray phaseCenterVariationCorrectionsArray =
+            translate2dDoubleArray(env, gnssAntennaInfo.phaseCenterVariationCorrectionMillimeters);
+    jobjectArray phaseCenterVariationCorrectionsUncertaintiesArray =
+            translate2dDoubleArray(env,
+                                   gnssAntennaInfo
+                                           .phaseCenterVariationCorrectionUncertaintyMillimeters);
+
+    if (phaseCenterVariationCorrectionsArray == NULL ||
+        phaseCenterVariationCorrectionsUncertaintiesArray == NULL) {
+        return NULL;
+    }
+
+    jobject phaseCenterVariationCorrections =
+            env->NewObject(class_phaseCenterVariationCorrections,
+                           method_phaseCenterVariationCorrectionsCtor,
+                           phaseCenterVariationCorrectionsArray,
+                           phaseCenterVariationCorrectionsUncertaintiesArray);
+
+    env->DeleteLocalRef(phaseCenterVariationCorrectionsArray);
+    env->DeleteLocalRef(phaseCenterVariationCorrectionsUncertaintiesArray);
+
+    return phaseCenterVariationCorrections;
+}
+
+jobject GnssAntennaInfoCallback::translateSignalGainCorrections(
+        JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
+    if (gnssAntennaInfo.signalGainCorrectionDbi == NULL ||
+        gnssAntennaInfo.signalGainCorrectionUncertaintyDbi == NULL) {
+        return NULL;
+    }
+    jobjectArray signalGainCorrectionsArray =
+            translate2dDoubleArray(env, gnssAntennaInfo.signalGainCorrectionDbi);
+    jobjectArray signalGainCorrectionsUncertaintiesArray =
+            translate2dDoubleArray(env, gnssAntennaInfo.signalGainCorrectionUncertaintyDbi);
+
+    if (signalGainCorrectionsArray == NULL || signalGainCorrectionsUncertaintiesArray == NULL) {
+        return NULL;
+    }
+
+    jobject signalGainCorrections =
+            env->NewObject(class_signalGainCorrections, method_signalGainCorrectionsCtor,
+                           signalGainCorrectionsArray, signalGainCorrectionsUncertaintiesArray);
+
+    env->DeleteLocalRef(signalGainCorrectionsArray);
+    env->DeleteLocalRef(signalGainCorrectionsUncertaintiesArray);
+
+    return signalGainCorrections;
+}
+
+jobject GnssAntennaInfoCallback::translateSingleGnssAntennaInfo(
+        JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
+    jobject phaseCenterOffsetCoordinates =
+            translatePhaseCenterOffsetCoordinates(env, gnssAntennaInfo);
+
+    // Nullable
+    jobject phaseCenterVariationCorrections =
+            translatePhaseCenterVariationCorrections(env, gnssAntennaInfo);
+
+    // Nullable
+    jobject signalGainCorrections = translateSignalGainCorrections(env, gnssAntennaInfo);
+
+    jobject gnssAntennaInfoObject =
+            env->NewObject(class_gnssAntennaInfo, method_gnssAntennaInfoCtor,
+                           gnssAntennaInfo.carrierFrequencyMHz, phaseCenterOffsetCoordinates,
+                           phaseCenterVariationCorrections, signalGainCorrections);
+
+    // Delete Local Refs
+    env->DeleteLocalRef(phaseCenterOffsetCoordinates);
+    env->DeleteLocalRef(phaseCenterVariationCorrections);
+    env->DeleteLocalRef(signalGainCorrections);
+
+    return gnssAntennaInfoObject;
+}
+
+void GnssAntennaInfoCallback::translateAndReportGnssAntennaInfo(
+        const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
+    JNIEnv* env = getJniEnv();
+
+    jobject arrayList = translateAllGnssAntennaInfos(env, gnssAntennaInfos);
+
+    reportAntennaInfo(env, arrayList);
+
+    env->DeleteLocalRef(arrayList);
+}
+
+void GnssAntennaInfoCallback::reportAntennaInfo(JNIEnv* env, const jobject antennaInfosArray) {
+    env->CallVoidMethod(mCallbacksObj, method_reportAntennaInfo, antennaInfosArray);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+/*
  * GnssMeasurementCallback implements the callback methods required for the
  * GnssMeasurement interface.
  */
@@ -1708,6 +1914,7 @@
             "(II)V");
     method_reportGeofencePauseStatus = env->GetMethodID(clazz, "reportGeofencePauseStatus",
             "(II)V");
+    method_reportAntennaInfo = env->GetMethodID(clazz, "reportAntennaInfo", "(Ljava/util/List;)V");
     method_reportMeasurementData = env->GetMethodID(
             clazz,
             "reportMeasurementData",
@@ -1791,6 +1998,36 @@
     class_gnssMeasurement = (jclass) env->NewGlobalRef(gnssMeasurementClass);
     method_gnssMeasurementCtor = env->GetMethodID(class_gnssMeasurement, "<init>", "()V");
 
+    jclass gnssAntennaInfoClass = env->FindClass("android/location/GnssAntennaInfo");
+    class_gnssAntennaInfo = (jclass)env->NewGlobalRef(gnssAntennaInfoClass);
+    method_gnssAntennaInfoCtor =
+            env->GetMethodID(class_gnssAntennaInfo, "<init>",
+                             "(D"
+                             "Landroid/location/GnssAntennaInfo$PhaseCenterOffsetCoordinates;"
+                             "Landroid/location/GnssAntennaInfo$PhaseCenterVariationCorrections;"
+                             "Landroid/location/GnssAntennaInfo$SignalGainCorrections;"
+                             ")V");
+
+    jclass phaseCenterOffsetCoordinatesClass =
+            env->FindClass("android/location/GnssAntennaInfo$PhaseCenterOffsetCoordinates");
+    class_phaseCenterOffsetCoordinates =
+            (jclass)env->NewGlobalRef(phaseCenterOffsetCoordinatesClass);
+    method_phaseCenterOffsetCoordinatesCtor =
+            env->GetMethodID(class_phaseCenterOffsetCoordinates, "<init>", "(DDDDDD)V");
+
+    jclass phaseCenterVariationCorrectionsClass =
+            env->FindClass("android/location/GnssAntennaInfo$PhaseCenterVariationCorrections");
+    class_phaseCenterVariationCorrections =
+            (jclass)env->NewGlobalRef(phaseCenterVariationCorrectionsClass);
+    method_phaseCenterVariationCorrectionsCtor =
+            env->GetMethodID(class_phaseCenterVariationCorrections, "<init>", "([[D[[D)V");
+
+    jclass signalGainCorrectionsClass =
+            env->FindClass("android/location/GnssAntennaInfo$SignalGainCorrections");
+    class_signalGainCorrections = (jclass)env->NewGlobalRef(signalGainCorrectionsClass);
+    method_signalGainCorrectionsCtor =
+            env->GetMethodID(class_signalGainCorrections, "<init>", "([[D[[D)V");
+
     jclass locationClass = env->FindClass("android/location/Location");
     class_location = (jclass) env->NewGlobalRef(locationClass);
     method_locationCtor = env->GetMethodID(class_location, "<init>", "(Ljava/lang/String;)V");
@@ -1809,6 +2046,14 @@
             (jclass) env->NewGlobalRef(gnssConfiguration_halInterfaceVersionClass);
     method_halInterfaceVersionCtor =
             env->GetMethodID(class_gnssConfiguration_halInterfaceVersion, "<init>", "(II)V");
+
+    jclass arrayListClass = env->FindClass("java/util/ArrayList");
+    class_arrayList = (jclass)env->NewGlobalRef(arrayListClass);
+    method_arrayListCtor = env->GetMethodID(class_arrayList, "<init>", "()V");
+    method_arrayListAdd = env->GetMethodID(class_arrayList, "add", "(Ljava/lang/Object;)Z");
+
+    jclass doubleArrayClass = env->FindClass("[D");
+    class_doubleArray = (jclass)env->NewGlobalRef(doubleArrayClass);
 }
 
 /* Initialization needed at system boot and whenever GNSS service dies. */
@@ -1935,6 +2180,15 @@
     }
 
     if (gnssHal_V2_1 != nullptr) {
+        auto gnssAntennaInfo = gnssHal_V2_1->getExtensionGnssAntennaInfo();
+        if (!gnssAntennaInfo.isOk()) {
+            ALOGD("Unable to get a handle to GnssAntennaInfo");
+        } else {
+            gnssAntennaInfoIface = gnssAntennaInfo;
+        }
+    }
+
+    if (gnssHal_V2_1 != nullptr) {
         auto gnssCorrections = gnssHal_V2_1->getExtensionMeasurementCorrections_1_1();
         if (!gnssCorrections.isOk()) {
             ALOGD("Unable to get a handle to GnssMeasurementCorrections 1.1 interface");
@@ -2725,6 +2979,52 @@
     return checkHidlReturn(result, "IGnssGeofencing resumeGeofence() failed.");
 }
 
+static jboolean android_location_GnssAntennaInfoProvider_is_antenna_info_supported(JNIEnv* env,
+                                                                                   jclass clazz) {
+    if (gnssAntennaInfoIface != nullptr) {
+        return JNI_TRUE;
+    }
+    return JNI_FALSE;
+}
+
+static jboolean android_location_GnssAntennaInfoProvider_start_antenna_info_listening(
+        JNIEnv* /* env */, jobject /* obj */) {
+    if (gnssAntennaInfoIface == nullptr) {
+        ALOGE("%s: IGnssAntennaInfo interface not available.", __func__);
+        return JNI_FALSE;
+    }
+
+    sp<GnssAntennaInfoCallback> cbIface = new GnssAntennaInfoCallback();
+
+    auto result = gnssAntennaInfoIface->setCallback(cbIface);
+
+    if (!checkHidlReturn(result, "IGnssAntennaInfo setCallback() failed.")) {
+        return JNI_FALSE;
+    }
+
+    IGnssAntennaInfo::GnssAntennaInfoStatus initRet = result;
+    if (initRet != IGnssAntennaInfo::GnssAntennaInfoStatus::SUCCESS) {
+        ALOGE("An error has been found on GnssAntennaInfoInterface::init, status=%d",
+              static_cast<int32_t>(initRet));
+        return JNI_FALSE;
+    } else {
+        ALOGD("gnss antenna info has been enabled");
+    }
+
+    return JNI_TRUE;
+}
+
+static jboolean android_location_GnssAntennaInfoProvider_stop_antenna_info_listening(
+        JNIEnv* /* env */, jobject /* obj */) {
+    if (gnssAntennaInfoIface == nullptr) {
+        ALOGE("%s: IGnssAntennaInfo interface not available.", __func__);
+        return JNI_FALSE;
+    }
+
+    auto result = gnssAntennaInfoIface->close();
+    return checkHidlReturn(result, "IGnssAntennaInfo close() failed.");
+}
+
 static jboolean android_location_GnssMeasurementsProvider_is_measurement_supported(
     JNIEnv* env, jclass clazz) {
     if (gnssMeasurementIface != nullptr) {
@@ -3286,6 +3586,19 @@
             reinterpret_cast<void *>(android_location_GnssBatchingProvider_cleanup_batching)},
 };
 
+static const JNINativeMethod sAntennaInfoMethods[] = {
+        /* name, signature, funcPtr */
+        {"native_is_antenna_info_supported", "()Z",
+         reinterpret_cast<void*>(
+                 android_location_GnssAntennaInfoProvider_is_antenna_info_supported)},
+        {"native_start_antenna_info_listening", "()Z",
+         reinterpret_cast<void*>(
+                 android_location_GnssAntennaInfoProvider_start_antenna_info_listening)},
+        {"native_stop_antenna_info_listening", "()Z",
+         reinterpret_cast<void*>(
+                 android_location_GnssAntennaInfoProvider_stop_antenna_info_listening)},
+};
+
 static const JNINativeMethod sGeofenceMethods[] = {
      /* name, signature, funcPtr */
     {"native_is_geofence_supported",
@@ -3407,6 +3720,8 @@
 };
 
 int register_android_server_location_GnssLocationProvider(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "com/android/server/location/GnssAntennaInfoProvider",
+                             sAntennaInfoMethods, NELEM(sAntennaInfoMethods));
     jniRegisterNativeMethods(
             env,
             "com/android/server/location/GnssBatchingProvider",
diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
new file mode 100644
index 0000000..d803c1d
--- /dev/null
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_ADB
+#define LOG_TAG "PackageManagerShellCommandDataLoader-jni"
+#include <android-base/logging.h>
+
+#include <android-base/unique_fd.h>
+#include <nativehelper/JNIHelp.h>
+
+#include <core_jni_helpers.h>
+
+#include "dataloader.h"
+
+#include <chrono>
+#include <thread>
+
+namespace android {
+
+namespace {
+
+using android::base::unique_fd;
+
+static constexpr int BUFFER_SIZE = 256 * 1024;
+static constexpr int BLOCKS_COUNT = BUFFER_SIZE / INCFS_DATA_FILE_BLOCK_SIZE;
+
+struct JniIds {
+    jclass packageManagerShellCommandDataLoader;
+    jmethodID pmscdLookupShellCommand;
+    jmethodID pmscdGetStdInPFD;
+    jmethodID pmscdGetLocalFile;
+
+    jmethodID parcelFileDescriptorGetFileDescriptor;
+
+    jclass ioUtils;
+    jmethodID ioUtilsCloseQuietly;
+
+    JniIds(JNIEnv* env) {
+        packageManagerShellCommandDataLoader = (jclass)env->NewGlobalRef(
+                FindClassOrDie(env, "com/android/server/pm/PackageManagerShellCommandDataLoader"));
+        pmscdLookupShellCommand =
+                GetStaticMethodIDOrDie(env, packageManagerShellCommandDataLoader,
+                                       "lookupShellCommand",
+                                       "(Ljava/lang/String;)Landroid/os/ShellCommand;");
+        pmscdGetStdInPFD =
+                GetStaticMethodIDOrDie(env, packageManagerShellCommandDataLoader, "getStdInPFD",
+                                       "(Landroid/os/ShellCommand;)Landroid/os/"
+                                       "ParcelFileDescriptor;");
+        pmscdGetLocalFile =
+                GetStaticMethodIDOrDie(env, packageManagerShellCommandDataLoader, "getLocalFile",
+                                       "(Landroid/os/ShellCommand;Ljava/lang/String;)Landroid/os/"
+                                       "ParcelFileDescriptor;");
+
+        auto parcelFileDescriptor = FindClassOrDie(env, "android/os/ParcelFileDescriptor");
+        parcelFileDescriptorGetFileDescriptor =
+                GetMethodIDOrDie(env, parcelFileDescriptor, "getFileDescriptor",
+                                 "()Ljava/io/FileDescriptor;");
+
+        ioUtils = (jclass)env->NewGlobalRef(FindClassOrDie(env, "libcore/io/IoUtils"));
+        ioUtilsCloseQuietly = GetStaticMethodIDOrDie(env, ioUtils, "closeQuietly",
+                                                     "(Ljava/lang/AutoCloseable;)V");
+    }
+};
+
+const JniIds& jniIds(JNIEnv* env) {
+    static const JniIds ids(env);
+    return ids;
+}
+
+static inline unique_fd convertPfdToFdAndDup(JNIEnv* env, const JniIds& jni, jobject pfd) {
+    if (!pfd) {
+        ALOGE("Missing In ParcelFileDescriptor.");
+        return {};
+    }
+    auto managedFd = env->CallObjectMethod(pfd, jni.parcelFileDescriptorGetFileDescriptor);
+    if (!pfd) {
+        ALOGE("Missing In FileDescriptor.");
+        return {};
+    }
+    return unique_fd{dup(jniGetFDFromFileDescriptor(env, managedFd))};
+}
+
+static inline std::pair<unique_fd, bool> openIncomingFile(JNIEnv* env, const JniIds& jni,
+                                                          jobject shellCommand,
+                                                          IncFsSpan metadata) {
+    jobject pfd = nullptr;
+    const bool stdin = (metadata.size == 0 || *metadata.data == '-');
+    if (stdin) {
+        // stdin
+        pfd = env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader,
+                                          jni.pmscdGetStdInPFD, shellCommand);
+    } else {
+        // file
+        const std::string filePath(metadata.data, metadata.size);
+        pfd = env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader,
+                                          jni.pmscdGetLocalFile, shellCommand,
+                                          env->NewStringUTF(filePath.c_str()));
+    }
+
+    auto result = convertPfdToFdAndDup(env, jni, pfd);
+    if (pfd) {
+        // Can be closed after dup.
+        env->CallStaticVoidMethod(jni.ioUtils, jni.ioUtilsCloseQuietly, pfd);
+    }
+
+    return {std::move(result), stdin};
+}
+
+static inline JNIEnv* GetJNIEnvironment(JavaVM* vm) {
+    JNIEnv* env;
+    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        return 0;
+    }
+    return env;
+}
+
+static inline JNIEnv* GetOrAttachJNIEnvironment(JavaVM* jvm) {
+    JNIEnv* env = GetJNIEnvironment(jvm);
+    if (!env) {
+        int result = jvm->AttachCurrentThread(&env, nullptr);
+        CHECK_EQ(result, JNI_OK) << "thread attach failed";
+        struct VmDetacher {
+            VmDetacher(JavaVM* vm) : mVm(vm) {}
+            ~VmDetacher() { mVm->DetachCurrentThread(); }
+
+        private:
+            JavaVM* const mVm;
+        };
+        static thread_local VmDetacher detacher(jvm);
+    }
+    return env;
+}
+
+class PackageManagerShellCommandDataLoaderDataLoader : public android::dataloader::DataLoader {
+public:
+    PackageManagerShellCommandDataLoaderDataLoader(JavaVM* jvm) : mJvm(jvm) { CHECK(mJvm); }
+
+private:
+    // Lifecycle.
+    bool onCreate(const android::dataloader::DataLoaderParams& params,
+                  android::dataloader::FilesystemConnectorPtr ifs,
+                  android::dataloader::StatusListenerPtr statusListener,
+                  android::dataloader::ServiceConnectorPtr,
+                  android::dataloader::ServiceParamsPtr) final {
+        mArgs = params.arguments();
+        mIfs = ifs;
+        return true;
+    }
+    bool onStart() final { return true; }
+    void onStop() final {}
+    void onDestroy() final {}
+
+    // IFS callbacks.
+    void onPendingReads(const dataloader::PendingReads& pendingReads) final {}
+    void onPageReads(const dataloader::PageReads& pageReads) final {}
+
+    // FS callbacks.
+    bool onPrepareImage(const dataloader::DataLoaderInstallationFiles& addedFiles) final {
+        JNIEnv* env = GetOrAttachJNIEnvironment(mJvm);
+        const auto& jni = jniIds(env);
+
+        jobject shellCommand = env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader,
+                                                           jni.pmscdLookupShellCommand,
+                                                           env->NewStringUTF(mArgs.c_str()));
+        if (!shellCommand) {
+            ALOGE("Missing shell command.");
+            return false;
+        }
+
+        std::vector<char> buffer;
+        buffer.reserve(BUFFER_SIZE);
+
+        std::vector<IncFsDataBlock> blocks;
+        blocks.reserve(BLOCKS_COUNT);
+
+        for (auto&& file : addedFiles) {
+            auto [incomingFd, stdin] = openIncomingFile(env, jni, shellCommand, file.metadata);
+            if (incomingFd < 0) {
+                ALOGE("Failed to open an IncFS file for metadata: %.*s, final file name is: %s. "
+                      "Error %d",
+                      int(file.metadata.size), file.metadata.data, file.name, errno);
+                return false;
+            }
+
+            const auto fileId = IncFs_FileIdFromMetadata(file.metadata);
+
+            const auto incfsFd(mIfs->openWrite(fileId));
+            if (incfsFd < 0) {
+                ALOGE("Failed to open an IncFS file for metadata: %.*s, final file name is: %s. "
+                      "Error %d",
+                      int(file.metadata.size), file.metadata.data, file.name, errno);
+                return false;
+            }
+
+            IncFsSize size = file.size;
+            IncFsSize remaining = size;
+            IncFsSize totalSize = 0;
+            IncFsBlockIndex blockIdx = 0;
+            while (remaining > 0) {
+                constexpr auto capacity = BUFFER_SIZE;
+                auto size = buffer.size();
+                if (capacity - size < INCFS_DATA_FILE_BLOCK_SIZE) {
+                    if (!flashToIncFs(incfsFd, false, &blocks, &blockIdx, &buffer)) {
+                        return false;
+                    }
+                    continue;
+                }
+
+                auto toRead = std::min<IncFsSize>(remaining, capacity - size);
+                buffer.resize(size + toRead);
+                auto read = ::read(incomingFd, buffer.data() + size, toRead);
+                if (read == 0) {
+                    if (stdin) {
+                        // eof of stdin, waiting...
+                        ALOGE("eof of stdin, waiting...: %d, remaining: %d, block: %d, read: %d",
+                              int(totalSize), int(remaining), int(blockIdx), int(read));
+                        using namespace std::chrono_literals;
+                        std::this_thread::sleep_for(10ms);
+                        continue;
+                    }
+                    break;
+                }
+                if (read < 0) {
+                    ALOGE("Underlying file read error: %.*s: %d", int(file.metadata.size),
+                          file.metadata.data, int(read));
+                    return false;
+                }
+
+                buffer.resize(size + read);
+                remaining -= read;
+                totalSize += read;
+            }
+            if (!buffer.empty() && !flashToIncFs(incfsFd, true, &blocks, &blockIdx, &buffer)) {
+                return false;
+            }
+        }
+
+        ALOGE("All done.");
+        return true;
+    }
+
+    bool flashToIncFs(int incfsFd, bool eof, std::vector<IncFsDataBlock>* blocks,
+                      IncFsBlockIndex* blockIdx, std::vector<char>* buffer) {
+        int consumed = 0;
+        const auto fullBlocks = buffer->size() / INCFS_DATA_FILE_BLOCK_SIZE;
+        for (int i = 0; i < fullBlocks; ++i) {
+            const auto inst = IncFsDataBlock{
+                    .fileFd = incfsFd,
+                    .pageIndex = (*blockIdx)++,
+                    .compression = INCFS_COMPRESSION_KIND_NONE,
+                    .kind = INCFS_BLOCK_KIND_DATA,
+                    .dataSize = INCFS_DATA_FILE_BLOCK_SIZE,
+                    .data = buffer->data() + consumed,
+            };
+            blocks->push_back(inst);
+            consumed += INCFS_DATA_FILE_BLOCK_SIZE;
+        }
+        const auto remain = buffer->size() - fullBlocks * INCFS_DATA_FILE_BLOCK_SIZE;
+        if (remain && eof) {
+            const auto inst = IncFsDataBlock{
+                    .fileFd = incfsFd,
+                    .pageIndex = (*blockIdx)++,
+                    .compression = INCFS_COMPRESSION_KIND_NONE,
+                    .kind = INCFS_BLOCK_KIND_DATA,
+                    .dataSize = static_cast<uint16_t>(remain),
+                    .data = buffer->data() + consumed,
+            };
+            blocks->push_back(inst);
+            consumed += remain;
+        }
+
+        auto res = mIfs->writeBlocks({blocks->data(), blocks->data() + blocks->size()});
+
+        blocks->clear();
+        buffer->erase(buffer->begin(), buffer->begin() + consumed);
+
+        if (res < 0) {
+            ALOGE("Failed to write block to IncFS: %d", int(res));
+            return false;
+        }
+        return true;
+    }
+
+    JavaVM* const mJvm;
+    std::string mArgs;
+    android::dataloader::FilesystemConnectorPtr mIfs;
+};
+
+static void nativeInitialize(JNIEnv* env, jclass klass) {
+    jniIds(env);
+}
+
+static const JNINativeMethod method_table[] = {
+        {"nativeInitialize", "()V", (void*)nativeInitialize},
+};
+
+} // namespace
+
+int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(
+        JNIEnv* env) {
+    android::dataloader::DataLoader::initialize(
+            [](auto jvm, const auto& params) -> android::dataloader::DataLoaderPtr {
+                if (params.type() == DATA_LOADER_TYPE_INCREMENTAL) {
+                    // This DataLoader only supports incremental installations.
+                    return std::make_unique<PackageManagerShellCommandDataLoaderDataLoader>(jvm);
+                }
+                return {};
+            });
+    return jniRegisterNativeMethods(env,
+                                    "com/android/server/pm/PackageManagerShellCommandDataLoader",
+                                    method_table, NELEM(method_table));
+}
+
+} // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index c186494..e575432 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -59,6 +59,7 @@
 int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(
         JNIEnv* env);
 int register_android_server_incremental_IncrementalManagerService(JNIEnv* env);
+int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(JNIEnv* env);
 int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env);
 };
 
@@ -112,6 +113,7 @@
     register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(
             env);
     register_android_server_incremental_IncrementalManagerService(env);
+    register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(env);
     register_android_server_stats_pull_StatsPullAtomService(env);
     return JNI_VERSION_1_4;
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 109f39d..d2ec436 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -81,6 +81,8 @@
 import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
 import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
 import static android.app.admin.DevicePolicyManager.WIPE_SILENTLY;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
 import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
 import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
@@ -92,6 +94,7 @@
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
+import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
 import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
 import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
@@ -162,6 +165,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.CrossProfileApps;
+import android.content.pm.CrossProfileAppsInternal;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
@@ -174,6 +178,7 @@
 import android.content.pm.ServiceInfo;
 import android.content.pm.StringParceledListSlice;
 import android.content.pm.UserInfo;
+import android.content.pm.parsing.AndroidPackage;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.database.Cursor;
@@ -408,6 +413,9 @@
     private static final String ATTR_APPLICATION_RESTRICTIONS_MANAGER
             = "application-restrictions-manager";
 
+    private static final String CALLED_FROM_PARENT = "calledFromParent";
+    private static final String NOT_CALLED_FROM_PARENT = "notCalledFromParent";
+
     // Comprehensive list of delegations.
     private static final String DELEGATIONS[] = {
         DELEGATION_CERT_INSTALL,
@@ -490,6 +498,7 @@
 
         SYSTEM_SETTINGS_WHITELIST = new ArraySet<>();
         SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_BRIGHTNESS);
+        SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_BRIGHTNESS_FLOAT);
         SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_BRIGHTNESS_MODE);
         SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_OFF_TIMEOUT);
 
@@ -553,6 +562,16 @@
     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
     private static final long ADMIN_APP_PASSWORD_COMPLEXITY = 123562444L;
 
+    /**
+     * Admin apps targeting Android R+ may not use
+     * {@link android.app.admin.DevicePolicyManager#setSecureSetting} to change the deprecated
+     * {@link android.provider.Settings.Secure#LOCATION_MODE} setting. Instead they should use
+     * {@link android.app.admin.DevicePolicyManager#setLocationEnabled}.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+    private static final long USE_SET_LOCATION_ENABLED = 117835097L;
+
     final Context mContext;
     final Injector mInjector;
     final IPackageManager mIPackageManager;
@@ -3957,7 +3976,7 @@
     @Override
     void handleStartUser(int userId) {
         updateScreenCaptureDisabled(userId,
-                getScreenCaptureDisabled(null, userId));
+                getScreenCaptureDisabled(null, userId, false));
         pushUserRestrictions(userId);
         // When system user is started (device boot), load cache for all users.
         // This is to mitigate the potential race between loading the cache and keyguard
@@ -4460,7 +4479,7 @@
                 .createEvent(DevicePolicyEnums.SET_PASSWORD_QUALITY)
                 .setAdmin(who)
                 .setInt(quality)
-                .setBoolean(parent)
+                .setStrings(parent ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT)
                 .write();
     }
 
@@ -5248,8 +5267,9 @@
     public int getPasswordComplexity(boolean parent) {
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.GET_USER_PASSWORD_COMPLEXITY_LEVEL)
-                .setStrings(mInjector.getPackageManager()
-                        .getPackagesForUid(mInjector.binderGetCallingUid()))
+                .setStrings(parent ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT,
+                        mInjector.getPackageManager().getPackagesForUid(
+                                mInjector.binderGetCallingUid()))
                 .write();
         final int callingUserId = mInjector.userHandleGetCallingUserId();
 
@@ -6923,6 +6943,7 @@
                 .createEvent(DevicePolicyEnums.WIPE_DATA_WITH_REASON)
                 .setAdmin(admin.info.getComponent())
                 .setInt(flags)
+                .setStrings(calledOnParentInstance ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT)
                 .write();
         String internalReason = String.format(
                 "DevicePolicyManager.wipeDataWithReason() from %s, organization-owned? %s",
@@ -7566,7 +7587,7 @@
      * Set whether the screen capture is disabled for the user managed by the specified admin.
      */
     @Override
-    public void setScreenCaptureDisabled(ComponentName who, boolean disabled) {
+    public void setScreenCaptureDisabled(ComponentName who, boolean disabled, boolean parent) {
         if (!mHasFeature) {
             return;
         }
@@ -7574,11 +7595,15 @@
         final int userHandle = UserHandle.getCallingUserId();
         synchronized (getLockObject()) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(who,
-                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent);
+            if (parent) {
+                enforceProfileOwnerOfOrganizationOwnedDevice(ap);
+            }
             if (ap.disableScreenCapture != disabled) {
                 ap.disableScreenCapture = disabled;
                 saveSettingsLocked(userHandle);
-                updateScreenCaptureDisabled(userHandle, disabled);
+                final int affectedUserId = parent ? getProfileParentId(userHandle) : userHandle;
+                updateScreenCaptureDisabled(affectedUserId, disabled);
             }
         }
         DevicePolicyEventLogger
@@ -7593,20 +7618,25 @@
      * active admin (if given admin is null).
      */
     @Override
-    public boolean getScreenCaptureDisabled(ComponentName who, int userHandle) {
+    public boolean getScreenCaptureDisabled(ComponentName who, int userHandle, boolean parent) {
         if (!mHasFeature) {
             return false;
         }
         synchronized (getLockObject()) {
+            if (parent) {
+                final ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+                        DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER, parent);
+                enforceProfileOwnerOfOrganizationOwnedDevice(ap);
+            }
             if (who != null) {
-                ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
-                return (admin != null) ? admin.disableScreenCapture : false;
+                ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
+                return (admin != null) && admin.disableScreenCapture;
             }
 
-            DevicePolicyData policy = getUserData(userHandle);
-            final int N = policy.mAdminList.size();
-            for (int i = 0; i < N; i++) {
-                ActiveAdmin admin = policy.mAdminList.get(i);
+            boolean includeParent = isOrganizationOwnedDeviceWithManagedProfile()
+                    && !isManagedProfile(userHandle);
+            List<ActiveAdmin> admins = getActiveAdminsForAffectedUser(userHandle, includeParent);
+            for (ActiveAdmin admin: admins) {
                 if (admin.disableScreenCapture) {
                     return true;
                 }
@@ -7617,14 +7647,11 @@
 
     private void updateScreenCaptureDisabled(int userHandle, boolean disabled) {
         mPolicyCache.setScreenCaptureDisabled(userHandle, disabled);
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    mInjector.getIWindowManager().refreshScreenCaptureDisabled(userHandle);
-                } catch (RemoteException e) {
-                    Log.w(LOG_TAG, "Unable to notify WindowManager.", e);
-                }
+        mHandler.post(() -> {
+            try {
+                mInjector.getIWindowManager().refreshScreenCaptureDisabled(userHandle);
+            } catch (RemoteException e) {
+                Log.w(LOG_TAG, "Unable to notify WindowManager.", e);
             }
         });
     }
@@ -8082,6 +8109,7 @@
                 .createEvent(DevicePolicyEnums.SET_CAMERA_DISABLED)
                 .setAdmin(who)
                 .setBoolean(disabled)
+                .setStrings(parent ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT)
                 .write();
     }
 
@@ -8159,7 +8187,7 @@
                 .createEvent(DevicePolicyEnums.SET_KEYGUARD_DISABLED_FEATURES)
                 .setAdmin(who)
                 .setInt(which)
-                .setBoolean(parent)
+                .setStrings(parent ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT)
                 .write();
     }
 
@@ -8876,9 +8904,12 @@
                 UserInfo parent = mUserManager.getProfileParent(userId);
                 Intent intent = new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED);
                 intent.putExtra(Intent.EXTRA_USER, new UserHandle(userId));
+                UserHandle parentHandle = new UserHandle(parent.id);
+                mLocalService.broadcastIntentToCrossProfileManifestReceiversAsUser(intent,
+                        parentHandle, /* requiresPermission= */ true);
                 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY |
                         Intent.FLAG_RECEIVER_FOREGROUND);
-                mContext.sendBroadcastAsUser(intent, new UserHandle(parent.id));
+                mContext.sendBroadcastAsUser(intent, parentHandle);
             });
         }
     }
@@ -10666,7 +10697,7 @@
         DevicePolicyEventLogger
                 .createEvent(eventId)
                 .setAdmin(who)
-                .setStrings(key)
+                .setStrings(key, parent ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT)
                 .write();
         if (SecurityLog.isLoggingEnabled()) {
             final int eventTag = enabledFromThisOwner
@@ -10810,8 +10841,8 @@
                 .createEvent(DevicePolicyEnums.SET_APPLICATION_HIDDEN)
                 .setAdmin(callerPackage)
                 .setBoolean(isDelegate)
-                .setBoolean(parent)
-                .setStrings(packageName, hidden ? "hidden" : "not_hidden")
+                .setStrings(packageName, hidden ? "hidden" : "not_hidden",
+                        parent ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT)
                 .write();
         return result;
     }
@@ -11554,13 +11585,22 @@
 
     @Override
     public void setLocationEnabled(ComponentName who, boolean locationEnabled) {
-        Objects.requireNonNull(who, "ComponentName is null");
-        enforceDeviceOwner(who);
+        enforceDeviceOwner(Objects.requireNonNull(who));
 
-        UserHandle userHandle = mInjector.binderGetCallingUserHandle();
-        mInjector.binderWithCleanCallingIdentity(
-                () -> mInjector.getLocationManager().setLocationEnabledForUser(locationEnabled,
-                        userHandle));
+        UserHandle user = mInjector.binderGetCallingUserHandle();
+
+        mInjector.binderWithCleanCallingIdentity(() -> {
+            boolean wasLocationEnabled = mInjector.getLocationManager().isLocationEnabledForUser(
+                    user);
+            mInjector.getLocationManager().setLocationEnabledForUser(locationEnabled, user);
+
+            // make a best effort to only show the notification if the admin is actually changing
+            // something. this is subject to race conditions with settings changes, but those are
+            // unlikely to realistically interfere
+            if (wasLocationEnabled != locationEnabled) {
+                showLocationSettingsChangedNotification(user);
+            }
+        });
 
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_SECURE_SETTING)
@@ -11571,6 +11611,25 @@
                 .write();
     }
 
+    private void showLocationSettingsChangedNotification(UserHandle user) {
+        PendingIntent locationSettingsIntent = mInjector.pendingIntentGetActivityAsUser(mContext, 0,
+                new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
+                        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), PendingIntent.FLAG_UPDATE_CURRENT,
+                null, user);
+        Notification notification = new Notification.Builder(mContext,
+                SystemNotificationChannels.DEVICE_ADMIN)
+                .setSmallIcon(R.drawable.ic_info_outline)
+                .setContentTitle(mContext.getString(R.string.location_changed_notification_title))
+                .setContentText(mContext.getString(R.string.location_changed_notification_text))
+                .setColor(mContext.getColor(R.color.system_notification_accent_color))
+                .setShowWhen(true)
+                .setContentIntent(locationSettingsIntent)
+                .setAutoCancel(true)
+                .build();
+        mInjector.getNotificationManager().notify(SystemMessage.NOTE_LOCATION_CHANGED,
+                notification);
+    }
+
     @Override
     public void requestSetLocationProviderAllowed(ComponentName who, String provider,
             boolean providerAllowed) {
@@ -11641,6 +11700,12 @@
                 throw new SecurityException(String.format(
                         "Permission denial: Profile owners cannot update %1$s", setting));
             }
+            if (setting.equals(Settings.Secure.LOCATION_MODE)
+                    && isSetSecureSettingLocationModeCheckEnabled(who.getPackageName(),
+                    callingUserId)) {
+                throw new UnsupportedOperationException(Settings.Secure.LOCATION_MODE + " is "
+                        + "deprecated. Please use setLocationEnabled() instead.");
+            }
             if (setting.equals(Settings.Secure.INSTALL_NON_MARKET_APPS)) {
                 if (getTargetSdk(who.getPackageName(), callingUserId) >= Build.VERSION_CODES.O) {
                     throw new UnsupportedOperationException(Settings.Secure.INSTALL_NON_MARKET_APPS
@@ -11685,6 +11750,9 @@
                     saveSettingsLocked(callingUserId);
                 }
                 mInjector.settingsSecurePutStringForUser(setting, value, callingUserId);
+                if (setting.equals(Settings.Secure.LOCATION_MODE)) {
+                    showLocationSettingsChangedNotification(UserHandle.of(callingUserId));
+                }
             });
         }
         DevicePolicyEventLogger
@@ -11694,6 +11762,16 @@
                 .write();
     }
 
+    private boolean isSetSecureSettingLocationModeCheckEnabled(String packageName, int userId) {
+        try {
+            return mIPlatformCompat.isChangeEnabledByPackageName(USE_SET_LOCATION_ENABLED,
+                    packageName, userId);
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "Failed to get a response from PLATFORM_COMPAT_SERVICE", e);
+            return getTargetSdk(packageName, userId) > Build.VERSION_CODES.Q;
+        }
+    }
+
     @Override
     public void setMasterVolumeMuted(ComponentName who, boolean on) {
         Objects.requireNonNull(who, "ComponentName is null");
@@ -12193,6 +12271,105 @@
             return DevicePolicyManagerService.this.getAllCrossProfilePackages();
         }
 
+        /**
+         * Sends the {@code intent} to the packages with cross profile capabilities.
+         *
+         * <p>This means the application must have the {@code crossProfile} property and
+         * and at least one of the following permissions:
+         *
+         * <ul>
+         *     <li>{@link android.Manifest.permission.INTERACT_ACROSS_PROFILES}
+         *     <li>{@link android.Manifest.permission.INTERACT_ACROSS_USERS}
+         *     <li>{@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission or the
+         *     {@link AppOpsManager.OP_INTERACT_ACROSS_PROFILES} app operation authorization.
+         * </ul>
+         *
+         * <p>Note: The intent itself is not modified but copied before use.
+         *
+         * @param intent Template for the intent sent to the packages.
+         * @param parentHandle Handle of the user that will receive the intents.
+         * @param requiresPermission If false, all packages with the {@code crossProfile} property
+         *                           will receive the intent.
+         */
+        @Override
+        public void broadcastIntentToCrossProfileManifestReceiversAsUser(Intent intent,
+                UserHandle parentHandle, boolean requiresPermission) {
+            Objects.requireNonNull(intent);
+            Objects.requireNonNull(parentHandle);
+            final int userId = parentHandle.getIdentifier();
+            Slog.i(LOG_TAG,
+                    String.format("Sending %s broadcast to manifest receivers.",
+                            intent.getAction()));
+            try {
+                final List<ResolveInfo> receivers = mIPackageManager.queryIntentReceivers(
+                        intent, /* resolvedType= */ null,
+                        STOCK_PM_FLAGS, parentHandle.getIdentifier()).getList();
+                for (ResolveInfo receiver : receivers) {
+                    final String packageName = receiver.getComponentInfo().packageName;
+                    if (checkCrossProfilePackagePermissions(packageName, userId,
+                            requiresPermission)) {
+                        Slog.i(LOG_TAG,
+                                String.format("Sending %s broadcast to %s.", intent.getAction(),
+                                        packageName));
+                        final Intent packageIntent = new Intent(intent)
+                                .setComponent(receiver.getComponentInfo().getComponentName())
+                                .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+                        mContext.sendBroadcastAsUser(packageIntent, parentHandle);
+                    }
+                }
+            } catch (RemoteException ex) {
+                Slog.w(LOG_TAG,
+                        String.format("Cannot get list of broadcast receivers for %s because: %s.",
+                                intent.getAction(), ex));
+            }
+        }
+
+        /**
+         * Checks whether the package {@code packageName} has the required permissions to receive
+         * cross-profile broadcasts on behalf of the user {@code userId}.
+         */
+        private boolean checkCrossProfilePackagePermissions(String packageName,
+                @UserIdInt int userId, boolean requiresPermission) {
+            final PackageManagerInternal pmInternal = LocalServices.getService(
+                    PackageManagerInternal.class);
+            final AndroidPackage androidPackage = pmInternal.getPackage(packageName);
+            if (androidPackage == null || !androidPackage.isCrossProfile()) {
+                return false;
+            }
+            if (!requiresPermission) {
+                return true;
+            }
+            if (!isPackageEnabled(packageName, userId)) {
+                return false;
+            }
+            try {
+                final CrossProfileAppsInternal crossProfileAppsService = LocalServices.getService(
+                        CrossProfileAppsInternal.class);
+                return crossProfileAppsService.verifyPackageHasInteractAcrossProfilePermission(
+                        packageName, userId);
+            } catch (NameNotFoundException ex) {
+                Slog.w(LOG_TAG,
+                        String.format("Cannot find the package %s to check for permissions.",
+                                packageName));
+                return false;
+            }
+        }
+
+        private boolean isPackageEnabled(String packageName, @UserIdInt int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                final PackageInfo info = mInjector.getPackageManagerInternal()
+                        .getPackageInfo(
+                                packageName,
+                                MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+                                callingUid,
+                                userId);
+                return info != null && info.applicationInfo.enabled;
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
     }
 
     private Intent createShowAdminSupportIntent(ComponentName admin, int userId) {
diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp
index 323e7f1..b13d330 100644
--- a/services/incremental/Android.bp
+++ b/services/incremental/Android.bp
@@ -43,6 +43,7 @@
         "service.incremental.proto",
         "libutils",
         "libvold_binder",
+        "libc++fs",
     ],
     shared_libs: [
         "libandroidfw",
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index b2c316a..3fcb57a 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -117,10 +117,9 @@
 
 binder::Status BinderIncrementalService::createStorage(const std::string& path,
                                                        const DataLoaderParamsParcel& params,
+                                                       const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& listener,
                                                        int32_t createMode, int32_t* _aidl_return) {
-    *_aidl_return = mImpl.createStorage(path, const_cast<DataLoaderParamsParcel&&>(params),
-                                        android::incremental::IncrementalService::CreateOptions(
-                                                createMode));
+    *_aidl_return = mImpl.createStorage(path, const_cast<DataLoaderParamsParcel&&>(params), listener, android::incremental::IncrementalService::CreateOptions(createMode));
     return ok();
 }
 
diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h
index 51d7de3..4075da6 100644
--- a/services/incremental/BinderIncrementalService.h
+++ b/services/incremental/BinderIncrementalService.h
@@ -38,9 +38,7 @@
     void onInvalidStorage(int mountId);
 
     binder::Status openStorage(const std::string& path, int32_t* _aidl_return) final;
-    binder::Status createStorage(const std::string& path,
-                                 const ::android::content::pm::DataLoaderParamsParcel& params,
-                                 int32_t createMode, int32_t* _aidl_return) final;
+    binder::Status createStorage(const ::std::string& path, const ::android::content::pm::DataLoaderParamsParcel& params, const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& listener, int32_t createMode, int32_t* _aidl_return) final;
     binder::Status createLinkedStorage(const std::string& path, int32_t otherStorageId,
                                        int32_t createMode, int32_t* _aidl_return) final;
     binder::Status makeBindMount(int32_t storageId, const std::string& sourcePath,
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 3b51377..980ae08 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -30,12 +30,12 @@
 #include <binder/BinderService.h>
 #include <binder/ParcelFileDescriptor.h>
 #include <binder/Status.h>
-#include <openssl/sha.h>
 #include <sys/stat.h>
 #include <uuid/uuid.h>
 #include <zlib.h>
 
 #include <ctime>
+#include <filesystem>
 #include <iterator>
 #include <span>
 #include <stack>
@@ -46,6 +46,7 @@
 
 using namespace std::literals;
 using namespace android::content::pm;
+namespace fs = std::filesystem;
 
 namespace android::incremental {
 
@@ -57,6 +58,7 @@
 struct Constants {
     static constexpr auto backing = "backing_store"sv;
     static constexpr auto mount = "mount"sv;
+    static constexpr auto mountKeyPrefix = "MT_"sv;
     static constexpr auto storagePrefix = "st"sv;
     static constexpr auto mountpointMdPrefix = ".mountpoint."sv;
     static constexpr auto infoMdName = ".info"sv;
@@ -105,7 +107,7 @@
     std::string res(path);
     std::replace(res.begin(), res.end(), '/', '_');
     std::replace(res.begin(), res.end(), '@', '_');
-    return res;
+    return std::string(constants().mountKeyPrefix) + res;
 }
 
 static std::pair<std::string, std::string> makeMountDir(std::string_view incrementalDir,
@@ -236,26 +238,11 @@
     if (!mIncrementalManager) {
         LOG(FATAL) << "IncrementalManager service is unavailable";
     }
-    // TODO(b/136132412): check that root dir should already exist
-    // TODO(b/136132412): enable mount existing dirs after SELinux rules are merged
-    // mountExistingImages();
+    mountExistingImages();
 }
 
 FileId IncrementalService::idFromMetadata(std::span<const uint8_t> metadata) {
-    incfs::FileId id = {};
-    if (size_t(metadata.size()) <= sizeof(id)) {
-        memcpy(&id, metadata.data(), metadata.size());
-    } else {
-        uint8_t buffer[SHA_DIGEST_LENGTH];
-        static_assert(sizeof(buffer) >= sizeof(id));
-
-        SHA_CTX ctx;
-        SHA1_Init(&ctx);
-        SHA1_Update(&ctx, metadata.data(), metadata.size());
-        SHA1_Final(buffer, &ctx);
-        memcpy(&id, buffer, sizeof(id));
-    }
-    return id;
+    return IncFs_FileIdFromMetadata({(const char*)metadata.data(), metadata.size()});
 }
 
 IncrementalService::~IncrementalService() = default;
@@ -342,21 +329,14 @@
     }
 
     std::thread([this, mounts = std::move(mounts)]() {
-        std::vector<IfsMountPtr> failedLoaderMounts;
         for (auto&& ifs : mounts) {
-            if (prepareDataLoader(*ifs, nullptr)) {
+            if (prepareDataLoader(*ifs)) {
                 LOG(INFO) << "Successfully started data loader for mount " << ifs->mountId;
             } else {
+                // TODO(b/133435829): handle data loader start failures
                 LOG(WARNING) << "Failed to start data loader for mount " << ifs->mountId;
-                failedLoaderMounts.push_back(std::move(ifs));
             }
         }
-
-        while (!failedLoaderMounts.empty()) {
-            LOG(WARNING) << "Deleting failed mount " << failedLoaderMounts.back()->mountId;
-            deleteStorage(*failedLoaderMounts.back());
-            failedLoaderMounts.pop_back();
-        }
         mPrepareDataLoaders.set_value_at_thread_exit();
     }).detach();
     return mPrepareDataLoaders.get_future();
@@ -375,9 +355,9 @@
     }
 }
 
-StorageId IncrementalService::createStorage(std::string_view mountPoint,
-                                            DataLoaderParamsParcel&& dataLoaderParams,
-                                            CreateOptions options) {
+StorageId IncrementalService::createStorage(
+        std::string_view mountPoint, DataLoaderParamsParcel&& dataLoaderParams,
+        const DataLoaderStatusListener& dataLoaderStatusListener, CreateOptions options) {
     LOG(INFO) << "createStorage: " << mountPoint << " | " << int(options);
     if (!path::isAbsolute(mountPoint)) {
         LOG(ERROR) << "path is not absolute: " << mountPoint;
@@ -509,7 +489,7 @@
     // Done here as well, all data structures are in good state.
     secondCleanupOnFailure.release();
 
-    if (!prepareDataLoader(*ifs, &dataLoaderParams)) {
+    if (!prepareDataLoader(*ifs, &dataLoaderParams, &dataLoaderStatusListener)) {
         LOG(ERROR) << "prepareDataLoader() failed";
         deleteStorageLocked(*ifs, std::move(l));
         return kInvalidStorageId;
@@ -767,7 +747,6 @@
         if (params.metadata.data && params.metadata.size > 0) {
             metadataBytes.assign(params.metadata.data, params.metadata.data + params.metadata.size);
         }
-        mIncrementalManager->newFileForDataLoader(ifs->mountId, id, metadataBytes);
         return 0;
     }
     return -EINVAL;
@@ -840,9 +819,10 @@
         metadata::BindPoint bp;
         bp.set_storage_id(storage);
         bp.set_allocated_dest_path(&target);
-        bp.set_source_subdir(std::string(path::relativize(storageRoot, source)));
+        bp.set_allocated_source_subdir(&source);
         const auto metadata = bp.SerializeAsString();
         bp.release_dest_path();
+        bp.release_source_subdir();
         mdFileName = makeBindMdName();
         auto node =
                 mIncFs->makeFile(ifs.control, path::join(ifs.root, constants().mount, mdFileName),
@@ -957,25 +937,20 @@
 }
 
 void IncrementalService::mountExistingImages() {
-    auto d = std::unique_ptr<DIR, decltype(&::closedir)>(::opendir(mIncrementalDir.c_str()),
-                                                         ::closedir);
-    while (auto e = ::readdir(d.get())) {
-        if (e->d_type != DT_DIR) {
+    for (const auto& entry : fs::directory_iterator(mIncrementalDir)) {
+        const auto path = entry.path().u8string();
+        const auto name = entry.path().filename().u8string();
+        if (!base::StartsWith(name, constants().mountKeyPrefix)) {
             continue;
         }
-        if (e->d_name == "."sv || e->d_name == ".."sv) {
-            continue;
-        }
-        auto root = path::join(mIncrementalDir, e->d_name);
-        if (!mountExistingImage(root, e->d_name)) {
-            IncFsMount::cleanupFilesystem(root);
+        const auto root = path::join(mIncrementalDir, name);
+        if (!mountExistingImage(root, name)) {
+            IncFsMount::cleanupFilesystem(path);
         }
     }
 }
 
 bool IncrementalService::mountExistingImage(std::string_view root, std::string_view key) {
-    LOG(INFO) << "Trying to mount: " << key;
-
     auto mountTarget = path::join(root, constants().mount);
     const auto backing = path::join(root, constants().backing);
 
@@ -1059,22 +1034,13 @@
         return false;
     }
 
-    DataLoaderParamsParcel dlParams;
-    dlParams.type = (DataLoaderType)m.loader().type();
-    dlParams.packageName = std::move(*m.mutable_loader()->mutable_package_name());
-    dlParams.className = std::move(*m.mutable_loader()->mutable_class_name());
-    dlParams.arguments = std::move(*m.mutable_loader()->mutable_arguments());
-    if (!prepareDataLoader(*ifs, &dlParams)) {
-        deleteStorage(*ifs);
-        return false;
-    }
-
     mMounts[ifs->mountId] = std::move(ifs);
     return true;
 }
 
 bool IncrementalService::prepareDataLoader(IncrementalService::IncFsMount& ifs,
-                                           DataLoaderParamsParcel* params) {
+                                           DataLoaderParamsParcel* params,
+                                           const DataLoaderStatusListener* externalListener) {
     if (!mSystemReady.load(std::memory_order_relaxed)) {
         std::unique_lock l(ifs.lock);
         if (params) {
@@ -1117,7 +1083,8 @@
     fsControlParcel.incremental->pendingReads.reset(
             base::unique_fd(::dup(ifs.control.pendingReads)));
     fsControlParcel.incremental->log.reset(base::unique_fd(::dup(ifs.control.logs)));
-    sp<IncrementalDataLoaderListener> listener = new IncrementalDataLoaderListener(*this);
+    sp<IncrementalDataLoaderListener> listener =
+            new IncrementalDataLoaderListener(*this, *externalListener);
     bool created = false;
     auto status = mIncrementalManager->prepareDataLoader(ifs.mountId, fsControlParcel, *dlp,
                                                          listener, &created);
@@ -1180,7 +1147,7 @@
         }
 
         // Create new lib file without signature info
-        incfs::NewFileParams libFileParams;
+        incfs::NewFileParams libFileParams{};
         libFileParams.size = uncompressedLen;
         libFileParams.verification.hashAlgorithm = INCFS_HASH_NONE;
         // Metadata of the new lib file is its relative path
@@ -1247,6 +1214,11 @@
 
 binder::Status IncrementalService::IncrementalDataLoaderListener::onStatusChanged(MountId mountId,
                                                                                   int newStatus) {
+    if (externalListener) {
+        // Give an external listener a chance to act before we destroy something.
+        externalListener->onStatusChanged(mountId, newStatus);
+    }
+
     std::unique_lock l(incrementalService.mLock);
     const auto& ifs = incrementalService.getIfsLocked(mountId);
     if (!ifs) {
@@ -1288,6 +1260,12 @@
         case IDataLoaderStatusListener::DATA_LOADER_STOPPED: {
             break;
         }
+        case IDataLoaderStatusListener::DATA_LOADER_IMAGE_READY: {
+            break;
+        }
+        case IDataLoaderStatusListener::DATA_LOADER_IMAGE_NOT_READY: {
+            break;
+        }
         default: {
             LOG(WARNING) << "Unknown data loader status: " << newStatus
                          << " for mount: " << mountId;
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index 2444dde..75d066b 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -59,6 +59,8 @@
 using TimePoint = std::chrono::time_point<Clock>;
 using Seconds = std::chrono::seconds;
 
+using DataLoaderStatusListener = ::android::sp<::android::content::pm::IDataLoaderStatusListener>;
+
 class IncrementalService final {
 public:
     explicit IncrementalService(ServiceManagerWrapper&& sm, std::string_view rootDir);
@@ -94,7 +96,9 @@
 
     std::optional<std::future<void>> onSystemReady();
 
-    StorageId createStorage(std::string_view mountPoint, DataLoaderParamsParcel&& dataLoaderParams,
+    StorageId createStorage(std::string_view mountPoint,
+                            DataLoaderParamsParcel&& dataLoaderParams,
+                            const DataLoaderStatusListener& dataLoaderStatusListener,
                             CreateOptions options = CreateOptions::Default);
     StorageId createLinkedStorage(std::string_view mountPoint, StorageId linkedStorage,
                                   CreateOptions options = CreateOptions::Default);
@@ -122,7 +126,6 @@
     }
 
     RawMetadata getMetadata(StorageId storage, FileId node) const;
-    std::string getSignatureData(StorageId storage, FileId node) const;
 
     std::vector<std::string> listFiles(StorageId storage) const;
     bool startLoading(StorageId storage) const;
@@ -130,13 +133,14 @@
                                  std::string_view libDirRelativePath, std::string_view abi);
     class IncrementalDataLoaderListener : public android::content::pm::BnDataLoaderStatusListener {
     public:
-        IncrementalDataLoaderListener(IncrementalService& incrementalService)
-              : incrementalService(incrementalService) {}
+        IncrementalDataLoaderListener(IncrementalService& incrementalService, DataLoaderStatusListener externalListener)
+              : incrementalService(incrementalService), externalListener(externalListener) {}
         // Callbacks interface
         binder::Status onStatusChanged(MountId mount, int newStatus) override;
 
     private:
         IncrementalService& incrementalService;
+        DataLoaderStatusListener externalListener;
     };
 
 private:
@@ -202,7 +206,7 @@
                            std::string&& source, std::string&& target, BindKind kind,
                            std::unique_lock<std::mutex>& mainLock);
 
-    bool prepareDataLoader(IncFsMount& ifs, DataLoaderParamsParcel* params);
+    bool prepareDataLoader(IncFsMount& ifs, DataLoaderParamsParcel* params = nullptr, const DataLoaderStatusListener* externalListener = nullptr);
     BindPathMap::const_iterator findStorageLocked(std::string_view path) const;
     StorageId findStorageId(std::string_view path) const;
 
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index f0b5672..6421583 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -60,8 +60,6 @@
                                              bool* _aidl_return) const = 0;
     virtual binder::Status startDataLoader(MountId mountId, bool* _aidl_return) const = 0;
     virtual binder::Status destroyDataLoader(MountId mountId) const = 0;
-    virtual binder::Status newFileForDataLoader(MountId mountId, FileId fileid,
-                                                const std::vector<uint8_t>& metadata) const = 0;
     virtual binder::Status showHealthBlockedUI(MountId mountId) const = 0;
 };
 
@@ -128,13 +126,6 @@
     binder::Status destroyDataLoader(MountId mountId) const override {
         return mInterface->destroyDataLoader(mountId);
     }
-    binder::Status newFileForDataLoader(MountId mountId, FileId fileid,
-                                        const std::vector<uint8_t>& metadata) const override {
-        return mInterface->newFileForDataLoader(mountId,
-                                                {(const uint8_t*)fileid.data,
-                                                 (const uint8_t*)fileid.data + sizeof(fileid.data)},
-                                                metadata);
-    }
     binder::Status showHealthBlockedUI(MountId mountId) const override {
         return mInterface->showHealthBlockedUI(mountId);
     }
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 9cdc83e..3aa846c 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -266,34 +266,12 @@
     DataLoaderParamsParcel mDataLoaderParcel;
 };
 
-/*
-TEST_F(IncrementalServiceTest, testBootMountExistingImagesSuccess) {
-    TemporaryDir tempDir;
-    setUpExistingMountDir(tempDir.path);
-    mVold->mountIncFsSuccess();
-    mVold->bindMountSuccess();
-    mIncrementalManager->prepareDataLoaderSuccess();
-    ON_CALL(*mIncrementalManager, destroyDataLoader(_)).WillByDefault(Return(binder::Status::ok()));
-
-    EXPECT_CALL(*mVold, mountIncFs(_, _, _, _)).Times(1);
-    EXPECT_CALL(*mIncrementalManager, prepareDataLoader(_, _, _, _, _)).Times(1);
-
-    MockServiceManager serviceManager = MockServiceManager(mVold, mIncrementalManager, mIncFs);
-    std::unique_ptr<IncrementalService> incrementalService =
-            std::make_unique<IncrementalService>(serviceManager, tempDir.path);
-    auto finished = incrementalService->onSystemReady();
-    if (finished) {
-        finished->wait();
-    }
-}
-*/
-
 TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsFails) {
     mVold->mountIncFsFails();
     EXPECT_CALL(*mIncrementalManager, prepareDataLoader(_, _, _, _, _)).Times(0);
     TemporaryDir tempDir;
     int storageId =
-            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
                                                IncrementalService::CreateOptions::CreateNew);
     ASSERT_LT(storageId, 0);
 }
@@ -303,7 +281,7 @@
     EXPECT_CALL(*mIncrementalManager, prepareDataLoader(_, _, _, _, _)).Times(0);
     TemporaryDir tempDir;
     int storageId =
-            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
                                                IncrementalService::CreateOptions::CreateNew);
     ASSERT_LT(storageId, 0);
 }
@@ -316,7 +294,7 @@
     EXPECT_CALL(*mVold, unmountIncFs(_));
     TemporaryDir tempDir;
     int storageId =
-            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
                                                IncrementalService::CreateOptions::CreateNew);
     ASSERT_LT(storageId, 0);
 }
@@ -330,7 +308,7 @@
     EXPECT_CALL(*mVold, unmountIncFs(_));
     TemporaryDir tempDir;
     int storageId =
-            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
                                                IncrementalService::CreateOptions::CreateNew);
     ASSERT_LT(storageId, 0);
 }
@@ -344,7 +322,7 @@
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     TemporaryDir tempDir;
     int storageId =
-            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
                                                IncrementalService::CreateOptions::CreateNew);
     ASSERT_LT(storageId, 0);
 }
@@ -358,7 +336,7 @@
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     TemporaryDir tempDir;
     int storageId =
-            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
                                                IncrementalService::CreateOptions::CreateNew);
     ASSERT_GE(storageId, 0);
     mIncrementalService->deleteStorage(storageId);
@@ -373,7 +351,7 @@
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     TemporaryDir tempDir;
     int storageId =
-            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
                                                IncrementalService::CreateOptions::CreateNew);
     ASSERT_GE(storageId, 0);
     mIncrementalManager->setDataLoaderStatusNotReady();
@@ -389,7 +367,7 @@
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     TemporaryDir tempDir;
     int storageId =
-            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
                                                IncrementalService::CreateOptions::CreateNew);
     ASSERT_GE(storageId, 0);
     mIncrementalManager->setDataLoaderStatusReady();
@@ -404,13 +382,13 @@
     mIncrementalManager->startDataLoaderSuccess();
     TemporaryDir tempDir;
     int storageId =
-            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
                                                IncrementalService::CreateOptions::CreateNew);
     std::string dir_path("test");
 
     std::string tempPath(tempDir.path);
     std::replace(tempPath.begin(), tempPath.end(), '/', '_');
-    std::string mount_dir = std::string(mRootDir.path) + "/" + tempPath.substr(1);
+    std::string mount_dir = std::string(mRootDir.path) + "/MT_" + tempPath.substr(1);
     std::string normalized_dir_path = mount_dir + "/mount/st_1_0/" + dir_path;
 
     // Expecting incfs to call makeDir on a path like:
@@ -428,7 +406,7 @@
     mIncrementalManager->startDataLoaderSuccess();
     TemporaryDir tempDir;
     int storageId =
-            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
                                                IncrementalService::CreateOptions::CreateNew);
     auto first = "first"sv;
     auto second = "second"sv;
@@ -436,7 +414,7 @@
 
     std::string tempPath(tempDir.path);
     std::replace(tempPath.begin(), tempPath.end(), '/', '_');
-    std::string mount_dir = std::string(mRootDir.path) + "/" + tempPath.substr(1);
+    std::string mount_dir = std::string(mRootDir.path) + "/MT_" + tempPath.substr(1);
 
     InSequence seq;
     auto parent_path = std::string(first) + "/" + std::string(second);
diff --git a/services/robotests/src/com/android/server/location/GnssAntennaInfoProviderTest.java b/services/robotests/src/com/android/server/location/GnssAntennaInfoProviderTest.java
new file mode 100644
index 0000000..76f7ad6
--- /dev/null
+++ b/services/robotests/src/com/android/server/location/GnssAntennaInfoProviderTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+/**
+ * Unit tests for {@link GnssAntennaInfoProvider}.
+ */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class GnssAntennaInfoProviderTest {
+    @Mock
+    private GnssAntennaInfoProvider.GnssAntennaInfoProviderNative mMockNative;
+    private GnssAntennaInfoProvider mTestProvider;
+
+    /** Setup. */
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mMockNative.startAntennaInfoListening()).thenReturn(true);
+        when(mMockNative.stopAntennaInfoListening()).thenReturn(true);
+
+        mTestProvider = new GnssAntennaInfoProvider(RuntimeEnvironment.application,
+                new Handler(Looper.myLooper()), mMockNative) {
+            @Override
+            public boolean isGpsEnabled() {
+                return true;
+            }
+        };
+    }
+
+    /**
+     * Test that registerWithService calls the native startAntennaInfoListening method.
+     */
+    @Test
+    public void register_nativeStarted() {
+        mTestProvider.registerWithService();
+        verify(mMockNative, times(1)).startAntennaInfoListening();
+    }
+
+    /**
+     * Test that unregisterFromService calls the native stopAntennaInfoListening method.
+     */
+    @Test
+    public void unregister_nativeStopped() {
+        mTestProvider.registerWithService();
+        mTestProvider.unregisterFromService();
+        verify(mMockNative, times(1)).stopAntennaInfoListening();
+    }
+
+    /**
+     * Test that GnssAntennaInfoProvider.isAntennaInfoSupported() returns the result of the
+     * native isAntennaInfoSupported method.
+     */
+    @Test
+    public void isSupported_nativeIsSupported() {
+        when(mMockNative.isAntennaInfoSupported()).thenReturn(true);
+        assertThat(mTestProvider.isAvailableInPlatform()).isTrue();
+
+        when(mMockNative.isAntennaInfoSupported()).thenReturn(false);
+        assertThat(mTestProvider.isAvailableInPlatform()).isFalse();
+    }
+}
diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
index 1a7b1d3..6190802 100644
--- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
+++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
@@ -55,6 +55,8 @@
 
 import androidx.test.core.app.ApplicationProvider;
 
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
+import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
 import com.android.server.LocalServices;
 import com.android.server.testing.shadows.ShadowApplicationPackageManager;
 import com.android.server.testing.shadows.ShadowUserManager;
@@ -190,6 +192,8 @@
     public void grantPermissions() {
         grantPermissions(
                 Manifest.permission.MANAGE_APP_OPS_MODES,
+                Manifest.permission.UPDATE_APP_OPS_STATS,
+                Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES,
                 Manifest.permission.INTERACT_ACROSS_USERS,
                 Manifest.permission.INTERACT_ACROSS_USERS_FULL);
     }
@@ -213,9 +217,26 @@
     }
 
     @Test
+    public void setInteractAcrossProfilesAppOp_noPermissions_throwsSecurityException() {
+        denyPermissions(
+                Manifest.permission.MANAGE_APP_OPS_MODES,
+                Manifest.permission.UPDATE_APP_OPS_STATS,
+                Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES,
+                Manifest.permission.INTERACT_ACROSS_USERS,
+                Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+        try {
+            mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
+                    CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
+            fail();
+        } catch (SecurityException expected) {}
+    }
+
+    @Test
     public void setInteractAcrossProfilesAppOp_missingInteractAcrossUsersAndFull_throwsSecurityException() {
-        denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS);
-        denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+        denyPermissions(
+                Manifest.permission.INTERACT_ACROSS_USERS,
+                Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+        grantPermissions(Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES);
         try {
             mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
                     CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
@@ -231,8 +252,38 @@
     }
 
     @Test
+    public void setInteractAcrossProfilesAppOp_configureInteractAcrossProfilesPermissionWithoutAppOpsPermissions_setsAppOp() {
+        denyPermissions(
+                Manifest.permission.MANAGE_APP_OPS_MODES,
+                Manifest.permission.UPDATE_APP_OPS_STATS);
+        grantPermissions(
+                Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES,
+                Manifest.permission.INTERACT_ACROSS_USERS);
+
+        mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
+                CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
+
+        assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED);
+    }
+
+    @Test
+    public void setInteractAcrossProfilesAppOp_appOpsPermissionsWithoutConfigureInteractAcrossProfilesPermission_setsAppOp() {
+        denyPermissions(Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES);
+        grantPermissions(
+                Manifest.permission.MANAGE_APP_OPS_MODES,
+                Manifest.permission.UPDATE_APP_OPS_STATS,
+                Manifest.permission.INTERACT_ACROSS_USERS);
+
+        mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
+                CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
+
+        assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED);
+    }
+
+    @Test
     public void setInteractAcrossProfilesAppOp_setsAppOpWithUsersAndWithoutFull() {
         denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+        grantPermissions(Manifest.permission.INTERACT_ACROSS_USERS);
         mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
                 CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
         assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED);
@@ -241,6 +292,7 @@
     @Test
     public void setInteractAcrossProfilesAppOp_setsAppOpWithFullAndWithoutUsers() {
         denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS);
+        grantPermissions(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
         mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp(
                 CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED);
         assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED);
@@ -479,6 +531,16 @@
         public void restoreCallingIdentity(long token) {}
 
         @Override
+        public void withCleanCallingIdentity(ThrowingRunnable action) {
+            action.run();
+        }
+
+        @Override
+        public <T> T withCleanCallingIdentity(ThrowingSupplier<T> action) {
+            return action.get();
+        }
+
+        @Override
         public UserManager getUserManager() {
             return mUserManager;
         }
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 339ff6b..ff34ebd 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -24,6 +24,7 @@
         "service-permission",
         "service-blobstore",
         "androidx.test.runner",
+        "androidx.test.ext.truth",
         "mockito-target-extended-minus-junit4",
         "platform-test-annotations",
         "truth-prebuilt",
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index e0e374b..00495f3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -683,8 +683,6 @@
                 mService.new MaybeReadyJobQueueFunctor();
         mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT = 5;
         mService.mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = HOUR_IN_MILLIS;
-        mService.mConstants.MIN_CONNECTIVITY_COUNT = 2;
-        mService.mConstants.MIN_READY_JOBS_COUNT = 1;
 
         JobStatus job = createJobStatus(
                 "testRareJobBatching",
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java
new file mode 100644
index 0000000..f2246da
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import static androidx.test.ext.truth.location.LocationSubject.assertThat;
+
+import static com.android.server.location.LocationUtils.createLocation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.location.Location;
+import android.platform.test.annotations.Presubmit;
+import android.util.Log;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Clock;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.Random;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LocationFudgerTest {
+
+    private static final String TAG = "LocationFudgerTest";
+
+    private static final double APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR = 111_000;
+    private static final float ACCURACY_M = 2000;
+    private static final float MAX_COARSE_FUDGE_DISTANCE_M =
+            (float) Math.sqrt(2 * ACCURACY_M * ACCURACY_M);
+
+    private Random mRandom;
+
+    private LocationFudger mFudger;
+
+    @Before
+    public void setUp() {
+        long seed = System.currentTimeMillis();
+        Log.i(TAG, "location random seed: " + seed);
+
+        mRandom = new Random(seed);
+        mFudger = new LocationFudger(
+                ACCURACY_M,
+                Clock.fixed(Instant.ofEpochMilli(0), ZoneId.systemDefault()),
+                mRandom);
+    }
+
+    @Test
+    public void testCoarsen() {
+        // test that the coarsened location is not the same as the fine location and no leaks
+        for (int i = 0; i < 100; i++) {
+            Location fine = createLocation("test", mRandom);
+            fine.setBearing(1);
+            fine.setSpeed(1);
+            fine.setAltitude(1);
+
+            Location coarse = mFudger.createCoarse(fine);
+
+            assertThat(coarse).isNotNull();
+            assertThat(coarse).isNotSameAs(fine);
+            assertThat(coarse.hasBearing()).isFalse();
+            assertThat(coarse.hasSpeed()).isFalse();
+            assertThat(coarse.hasAltitude()).isFalse();
+            assertThat(coarse.getAccuracy()).isEqualTo(ACCURACY_M);
+            assertThat(coarse.distanceTo(fine)).isGreaterThan(1F);
+            assertThat(coarse).isNearby(fine, MAX_COARSE_FUDGE_DISTANCE_M);
+        }
+    }
+
+    @Test
+    public void testCoarsen_Consistent() {
+        // test that coarsening the same location will always return the same coarse location
+        // (and thus that averaging to eliminate random noise won't work)
+        for (int i = 0; i < 100; i++) {
+            Location fine = createLocation("test", mRandom);
+            Location coarse = mFudger.createCoarse(fine);
+            assertThat(mFudger.createCoarse(new Location(fine))).isEqualTo(coarse);
+            assertThat(mFudger.createCoarse(new Location(fine))).isEqualTo(coarse);
+        }
+    }
+
+    @Test
+    public void testCoarsen_AvgMany() {
+        // test that a set of locations normally distributed around the user's real location still
+        // cannot be easily average to reveal the user's real location
+
+        int passed = 0;
+        int iterations = 100;
+        for (int j = 0; j < iterations; j++) {
+            Location fine = createLocation("test", mRandom);
+
+            // generate a point cloud around a single location
+            ArrayList<Location> finePoints = new ArrayList<>(100);
+            for (int i = 0; i < 100; i++) {
+                finePoints.add(step(fine, mRandom.nextGaussian() * ACCURACY_M));
+            }
+
+            // generate the coarsened version of that point cloud
+            ArrayList<Location> coarsePoints = new ArrayList<>(100);
+            for (int i = 0; i < 100; i++) {
+                coarsePoints.add(mFudger.createCoarse(finePoints.get(i)));
+            }
+
+            double avgFineLatitude = finePoints.stream().mapToDouble(
+                    Location::getLatitude).average()
+                    .orElseThrow(IllegalStateException::new);
+            double avgFineLongitude = finePoints.stream().mapToDouble(
+                    Location::getLongitude).average()
+                    .orElseThrow(IllegalStateException::new);
+            Location fineAvg = createLocation("test", avgFineLatitude, avgFineLongitude, 0);
+
+            double avgCoarseLatitude = coarsePoints.stream().mapToDouble(
+                    Location::getLatitude).average()
+                    .orElseThrow(IllegalStateException::new);
+            double avgCoarseLongitude = coarsePoints.stream().mapToDouble(
+                    Location::getLongitude).average()
+                    .orElseThrow(IllegalStateException::new);
+            Location coarseAvg = createLocation("test", avgCoarseLatitude, avgCoarseLongitude, 0);
+
+            if (coarseAvg.distanceTo(fine) > fineAvg.distanceTo(fine)) {
+                passed++;
+            }
+        }
+
+        // very generally speaking, the closer the initial fine point is to a grid point, the more
+        // accurate the coarsened average will be. we use 70% as a lower bound by -very- roughly
+        // taking the area within a grid where we expect a reasonable percentage of points generated
+        // by step() to fall in another grid square. this likely doesn't have much mathematical
+        // validity, but it serves as a sanity test as least.
+        assertThat(passed / (double) iterations).isGreaterThan(.70);
+    }
+
+    // step in a random direction by distance - assume cartesian
+    private Location step(Location input, double distanceM) {
+        double radians = mRandom.nextDouble() * 2 * Math.PI;
+        double deltaXM = Math.cos(radians) * distanceM;
+        double deltaYM = Math.sin(radians) * distanceM;
+        return createLocation("test",
+                input.getLatitude() + deltaXM / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR,
+                input.getLongitude() + deltaYM / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR,
+                0);
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationUtils.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationUtils.java
new file mode 100644
index 0000000..decb3a6
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationUtils.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.location.Location;
+import android.os.SystemClock;
+
+import java.util.Random;
+
+public final class LocationUtils {
+
+    private static final double MIN_LATITUDE = -90D;
+    private static final double MAX_LATITUDE = 90D;
+    private static final double MIN_LONGITUDE = -180D;
+    private static final double MAX_LONGITUDE = 180D;
+
+    private static final float MIN_ACCURACY = 1;
+    private static final float MAX_ACCURACY = 100;
+
+    public static Location createLocation(String provider, Random random) {
+        return createLocation(provider,
+                MIN_LATITUDE + random.nextDouble() * (MAX_LATITUDE - MIN_LATITUDE),
+                MIN_LONGITUDE + random.nextDouble() * (MAX_LONGITUDE - MIN_LONGITUDE),
+                MIN_ACCURACY + random.nextFloat() * (MAX_ACCURACY - MIN_ACCURACY));
+    }
+
+    public static Location createLocation(String provider, double latitude, double longitude,
+            float accuracy) {
+        Location location = new Location(provider);
+        location.setLatitude(latitude);
+        location.setLongitude(longitude);
+        location.setAccuracy(accuracy);
+        location.setTime(System.currentTimeMillis());
+        location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
+        return location;
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/UserInfoHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/UserInfoHelperTest.java
index 389fdf9..71e79b3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/UserInfoHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/UserInfoHelperTest.java
@@ -26,6 +26,7 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.app.ActivityManager;
@@ -43,6 +44,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.server.location.UserInfoHelper.UserListener;
 
 import org.junit.After;
 import org.junit.Before;
@@ -117,62 +119,88 @@
         }
     }
 
+    private void startUser(int userId) {
+        Intent intent = new Intent(Intent.ACTION_USER_STARTED).putExtra(Intent.EXTRA_USER_HANDLE,
+                userId);
+        for (BroadcastReceiver broadcastReceiver : mBroadcastReceivers) {
+            broadcastReceiver.onReceive(mContext, intent);
+        }
+    }
+
+    private void stopUser(int userId) {
+        Intent intent = new Intent(Intent.ACTION_USER_STOPPED).putExtra(Intent.EXTRA_USER_HANDLE,
+                userId);
+        for (BroadcastReceiver broadcastReceiver : mBroadcastReceivers) {
+            broadcastReceiver.onReceive(mContext, intent);
+        }
+    }
+
     @Test
-    public void testListeners() {
-        UserInfoHelper.UserChangedListener listener = mock(
-                UserInfoHelper.UserChangedListener.class);
+    public void testListener_SwitchUser() {
+        UserListener listener = mock(UserListener.class);
         mHelper.addListener(listener);
 
         switchUser(USER1_ID);
         verify(listener, never()).onUserChanged(anyInt(), anyInt());
 
         switchUser(USER2_ID);
-        verify(listener).onUserChanged(USER1_ID, USER2_ID);
+        verify(listener, times(1)).onUserChanged(USER1_ID, UserListener.USER_SWITCHED);
+        verify(listener, times(1)).onUserChanged(USER2_ID, UserListener.USER_SWITCHED);
 
         switchUser(USER1_ID);
-        verify(listener).onUserChanged(USER2_ID, USER1_ID);
+        verify(listener, times(2)).onUserChanged(USER1_ID, UserListener.USER_SWITCHED);
+        verify(listener, times(2)).onUserChanged(USER2_ID, UserListener.USER_SWITCHED);
     }
 
     @Test
-    public void testCurrentUser() {
-        assertThat(mHelper.getCurrentUserId()).isEqualTo(USER1_ID);
+    public void testListener_StartUser() {
+        UserListener listener = mock(UserListener.class);
+        mHelper.addListener(listener);
+
+        startUser(USER1_ID);
+        verify(listener).onUserChanged(USER1_ID, UserListener.USER_STARTED);
+
+        startUser(USER2_ID);
+        verify(listener).onUserChanged(USER2_ID, UserListener.USER_STARTED);
+    }
+
+    @Test
+    public void testListener_StopUser() {
+        UserListener listener = mock(UserListener.class);
+        mHelper.addListener(listener);
+
+        stopUser(USER1_ID);
+        verify(listener).onUserChanged(USER1_ID, UserListener.USER_STOPPED);
+
+        stopUser(USER2_ID);
+        verify(listener).onUserChanged(USER2_ID, UserListener.USER_STOPPED);
+    }
+
+    @Test
+    public void testCurrentUserIds() {
+        assertThat(mHelper.getCurrentUserIds()).isEqualTo(USER1_PROFILES);
 
         switchUser(USER2_ID);
 
-        assertThat(mHelper.getCurrentUserId()).isEqualTo(USER2_ID);
+        assertThat(mHelper.getCurrentUserIds()).isEqualTo(USER2_PROFILES);
 
         switchUser(USER1_ID);
 
-        assertThat(mHelper.getCurrentUserId()).isEqualTo(USER1_ID);
+        assertThat(mHelper.getCurrentUserIds()).isEqualTo(USER1_PROFILES);
     }
 
     @Test
-    public void testIsCurrentUserOrProfile() {
-        assertThat(mHelper.isCurrentUserOrProfile(USER1_ID)).isTrue();
-        assertThat(mHelper.isCurrentUserOrProfile(USER1_MANAGED_ID)).isTrue();
-        assertThat(mHelper.isCurrentUserOrProfile(USER2_ID)).isFalse();
-        assertThat(mHelper.isCurrentUserOrProfile(USER2_MANAGED_ID)).isFalse();
+    public void testIsCurrentUserId() {
+        assertThat(mHelper.isCurrentUserId(USER1_ID)).isTrue();
+        assertThat(mHelper.isCurrentUserId(USER1_MANAGED_ID)).isTrue();
+        assertThat(mHelper.isCurrentUserId(USER2_ID)).isFalse();
+        assertThat(mHelper.isCurrentUserId(USER2_MANAGED_ID)).isFalse();
 
         switchUser(USER2_ID);
 
-        assertThat(mHelper.isCurrentUserOrProfile(USER1_ID)).isFalse();
-        assertThat(mHelper.isCurrentUserOrProfile(USER2_ID)).isTrue();
-        assertThat(mHelper.isCurrentUserOrProfile(USER1_MANAGED_ID)).isFalse();
-        assertThat(mHelper.isCurrentUserOrProfile(USER2_MANAGED_ID)).isTrue();
-    }
-
-    @Test
-    public void testGetParentUserId() {
-        assertThat(mHelper.getParentUserId(USER1_ID)).isEqualTo(USER1_ID);
-        assertThat(mHelper.getParentUserId(USER1_MANAGED_ID)).isEqualTo(USER1_ID);
-        assertThat(mHelper.getParentUserId(USER2_ID)).isEqualTo(USER2_ID);
-        assertThat(mHelper.getParentUserId(USER2_MANAGED_ID)).isEqualTo(USER2_ID);
-
-        switchUser(USER2_ID);
-
-        assertThat(mHelper.getParentUserId(USER1_ID)).isEqualTo(USER1_ID);
-        assertThat(mHelper.getParentUserId(USER2_ID)).isEqualTo(USER2_ID);
-        assertThat(mHelper.getParentUserId(USER1_MANAGED_ID)).isEqualTo(USER1_ID);
-        assertThat(mHelper.getParentUserId(USER2_MANAGED_ID)).isEqualTo(USER2_ID);
+        assertThat(mHelper.isCurrentUserId(USER1_ID)).isFalse();
+        assertThat(mHelper.isCurrentUserId(USER2_ID)).isTrue();
+        assertThat(mHelper.isCurrentUserId(USER1_MANAGED_ID)).isFalse();
+        assertThat(mHelper.isCurrentUserId(USER2_MANAGED_ID)).isTrue();
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
index fbb55fd..5a96347 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
@@ -25,7 +25,9 @@
 import static org.hamcrest.Matchers.hasSize;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
+import static org.mockito.AdditionalAnswers.returnsFirstArg;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -133,6 +135,8 @@
         mA11ySecurityPolicy = new AccessibilitySecurityPolicy(mMockContext, mMockA11yUserManager);
         mA11ySecurityPolicy.setAccessibilityWindowManager(mMockA11yWindowManager);
         mA11ySecurityPolicy.setAppWidgetManager(mMockAppWidgetManager);
+
+        when(mMockA11yWindowManager.resolveParentWindowIdLocked(anyInt())).then(returnsFirstArg());
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index 9db5a08..10a86f9 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -88,6 +88,10 @@
     private static final int DEFAULT_FOCUSED_INDEX = 1;
     private static final int SCREEN_WIDTH = 1080;
     private static final int SCREEN_HEIGHT = 1920;
+    private static final int INVALID_ID = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+    private static final int HOST_WINDOW_ID = 10;
+    private static final int EMBEDDED_WINDOW_ID = 11;
+    private static final int OTHER_WINDOW_ID = 12;
 
     private AccessibilityWindowManager mA11yWindowManager;
     // Window manager will support multiple focused window if config_perDisplayFocusEnabled is true,
@@ -117,6 +121,10 @@
     @Mock private AccessibilitySecurityPolicy mMockA11ySecurityPolicy;
     @Mock private AccessibilitySecurityPolicy.AccessibilityUserManager mMockA11yUserManager;
 
+    @Mock private IBinder mMockHostToken;
+    @Mock private IBinder mMockEmbeddedToken;
+    @Mock private IBinder mMockInvalidToken;
+
     @Before
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
@@ -140,6 +148,8 @@
         // AccessibilityEventSender is invoked during onWindowsForAccessibilityChanged.
         // Resets it for mockito verify of further test case.
         Mockito.reset(mMockA11yEventSender);
+
+        registerLeashedTokenAndWindowId();
     }
 
     @After
@@ -407,6 +417,51 @@
     }
 
     @Test
+    public void resolveParentWindowId_windowIsNotEmbedded_shouldReturnGivenId()
+            throws RemoteException {
+        final int windowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, false,
+                Mockito.mock(IBinder.class), USER_SYSTEM_ID);
+        assertEquals(windowId, mA11yWindowManager.resolveParentWindowIdLocked(windowId));
+    }
+
+    @Test
+    public void resolveParentWindowId_windowIsNotRegistered_shouldReturnGivenId() {
+        final int windowId = -1;
+        assertEquals(windowId, mA11yWindowManager.resolveParentWindowIdLocked(windowId));
+    }
+
+    @Test
+    public void resolveParentWindowId_windowIsAssociated_shouldReturnParentWindowId()
+            throws RemoteException {
+        final IBinder mockHostToken = Mockito.mock(IBinder.class);
+        final IBinder mockEmbeddedToken = Mockito.mock(IBinder.class);
+        final int hostWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
+                false, mockHostToken, USER_SYSTEM_ID);
+        final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
+                false, mockEmbeddedToken, USER_SYSTEM_ID);
+        mA11yWindowManager.associateEmbeddedHierarchyLocked(mockHostToken, mockEmbeddedToken);
+        final int resolvedWindowId = mA11yWindowManager.resolveParentWindowIdLocked(
+                embeddedWindowId);
+        assertEquals(hostWindowId, resolvedWindowId);
+    }
+
+    @Test
+    public void resolveParentWindowId_windowIsDisassociated_shouldReturnGivenId()
+            throws RemoteException {
+        final IBinder mockHostToken = Mockito.mock(IBinder.class);
+        final IBinder mockEmbeddedToken = Mockito.mock(IBinder.class);
+        final int hostWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
+                false, mockHostToken, USER_SYSTEM_ID);
+        final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
+                false, mockEmbeddedToken, USER_SYSTEM_ID);
+        mA11yWindowManager.associateEmbeddedHierarchyLocked(mockHostToken, mockEmbeddedToken);
+        mA11yWindowManager.disassociateEmbeddedHierarchyLocked(mockEmbeddedToken);
+        final int resolvedWindowId = mA11yWindowManager.resolveParentWindowIdLocked(
+                embeddedWindowId);
+        assertEquals(embeddedWindowId, resolvedWindowId);
+    }
+
+    @Test
     public void computePartialInteractiveRegionForWindow_wholeVisible_returnWholeRegion() {
         // Updates top 2 z-order WindowInfo are whole visible.
         WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
@@ -726,6 +781,64 @@
                 token.asBinder(), -1);
     }
 
+    @Test
+    public void getHostTokenLocked_hierarchiesAreAssociated_shouldReturnHostToken() {
+        mA11yWindowManager.associateLocked(mMockEmbeddedToken, mMockHostToken);
+        final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockEmbeddedToken);
+        assertEquals(hostToken, mMockHostToken);
+    }
+
+    @Test
+    public void getHostTokenLocked_hierarchiesAreNotAssociated_shouldReturnNull() {
+        final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockEmbeddedToken);
+        assertNull(hostToken);
+    }
+
+    @Test
+    public void getHostTokenLocked_embeddedHierarchiesAreDisassociated_shouldReturnNull() {
+        mA11yWindowManager.associateLocked(mMockEmbeddedToken, mMockHostToken);
+        mA11yWindowManager.disassociateLocked(mMockEmbeddedToken);
+        final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockEmbeddedToken);
+        assertNull(hostToken);
+    }
+
+    @Test
+    public void getHostTokenLocked_hostHierarchiesAreDisassociated_shouldReturnNull() {
+        mA11yWindowManager.associateLocked(mMockEmbeddedToken, mMockHostToken);
+        mA11yWindowManager.disassociateLocked(mMockHostToken);
+        final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockHostToken);
+        assertNull(hostToken);
+    }
+
+    @Test
+    public void getWindowIdLocked_windowIsRegistered_shouldReturnWindowId() {
+        final int windowId = mA11yWindowManager.getWindowIdLocked(mMockHostToken);
+        assertEquals(windowId, HOST_WINDOW_ID);
+    }
+
+    @Test
+    public void getWindowIdLocked_windowIsNotRegistered_shouldReturnInvalidWindowId() {
+        final int windowId = mA11yWindowManager.getWindowIdLocked(mMockInvalidToken);
+        assertEquals(windowId, INVALID_ID);
+    }
+
+    @Test
+    public void getTokenLocked_windowIsRegistered_shouldReturnToken() {
+        final IBinder token = mA11yWindowManager.getTokenLocked(HOST_WINDOW_ID);
+        assertEquals(token, mMockHostToken);
+    }
+
+    @Test
+    public void getTokenLocked_windowIsNotRegistered_shouldReturnNull() {
+        final IBinder token = mA11yWindowManager.getTokenLocked(OTHER_WINDOW_ID);
+        assertNull(token);
+    }
+
+    private void registerLeashedTokenAndWindowId() {
+        mA11yWindowManager.registerIdLocked(mMockHostToken, HOST_WINDOW_ID);
+        mA11yWindowManager.registerIdLocked(mMockEmbeddedToken, EMBEDDED_WINDOW_ID);
+    }
+
     private void startTrackingPerDisplay(int displayId) throws RemoteException {
         ArrayList<WindowInfo> windowInfosForDisplay = new ArrayList<>();
         // Adds RemoteAccessibilityConnection into AccessibilityWindowManager, and copy
@@ -784,6 +897,7 @@
                 IAccessibilityInteractionConnection.class);
         final IBinder mockConnectionBinder = Mockito.mock(IBinder.class);
         final IBinder mockWindowBinder = Mockito.mock(IBinder.class);
+        final IBinder mockLeashToken = Mockito.mock(IBinder.class);
         when(mockA11yConnection.asBinder()).thenReturn(mockConnectionBinder);
         when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder);
         when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(userId))
@@ -792,11 +906,31 @@
                 .thenReturn(displayId);
 
         int windowId = mA11yWindowManager.addAccessibilityInteractionConnection(
-                mockWindowToken, mockA11yConnection, PACKAGE_NAME, userId);
+                mockWindowToken, mockLeashToken, mockA11yConnection, PACKAGE_NAME, userId);
         mA11yWindowTokens.put(windowId, mockWindowToken);
         return mockWindowToken;
     }
 
+    private int addAccessibilityInteractionConnection(int displayId, boolean bGlobal,
+            IBinder leashToken, int userId) throws RemoteException {
+        final IWindow mockWindowToken = Mockito.mock(IWindow.class);
+        final IAccessibilityInteractionConnection mockA11yConnection = Mockito.mock(
+                IAccessibilityInteractionConnection.class);
+        final IBinder mockConnectionBinder = Mockito.mock(IBinder.class);
+        final IBinder mockWindowBinder = Mockito.mock(IBinder.class);
+        when(mockA11yConnection.asBinder()).thenReturn(mockConnectionBinder);
+        when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder);
+        when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(userId))
+                .thenReturn(bGlobal);
+        when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowToken.asBinder()))
+                .thenReturn(displayId);
+
+        int windowId = mA11yWindowManager.addAccessibilityInteractionConnection(
+                mockWindowToken, leashToken, mockA11yConnection, PACKAGE_NAME, userId);
+        mA11yWindowTokens.put(windowId, mockWindowToken);
+        return windowId;
+    }
+
     private void addWindowInfo(ArrayList<WindowInfo> windowInfos, IWindow windowToken, int layer) {
         final WindowInfo windowInfo = WindowInfo.obtain();
         windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 156cd6e..e5adb80 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -283,7 +283,7 @@
                 null /* authenticators */);
         waitForIdle();
         verify(mReceiver1).onError(
-                eq(BiometricAuthenticator.TYPE_NONE),
+                eq(BiometricAuthenticator.TYPE_FINGERPRINT),
                 eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE),
                 eq(0 /* vendorCode */));
     }
@@ -1117,14 +1117,14 @@
 
         // STRONG-only auth is not available
         int authenticators = Authenticators.BIOMETRIC_STRONG;
-        assertEquals(BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE,
+        assertEquals(BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
                 invokeCanAuthenticate(mBiometricService, authenticators));
         invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
                 authenticators);
         waitForIdle();
         verify(mReceiver1).onError(
-                eq(BiometricAuthenticator.TYPE_NONE),
-                eq(BiometricPrompt.BIOMETRIC_ERROR_HW_NOT_PRESENT),
+                eq(BiometricAuthenticator.TYPE_FINGERPRINT),
+                eq(BiometricPrompt.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED),
                 eq(0) /* vendorCode */);
 
         // Request for weak auth works
@@ -1154,7 +1154,7 @@
                 false /* requireConfirmation */,
                 authenticators);
         waitForIdle();
-        assertTrue(Utils.isDeviceCredentialAllowed(mBiometricService.mCurrentAuthSession.mBundle));
+        assertTrue(Utils.isCredentialRequested(mBiometricService.mCurrentAuthSession.mBundle));
         verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
                 eq(mBiometricService.mCurrentAuthSession.mBundle),
                 any(IBiometricServiceReceiverInternal.class),
@@ -1162,6 +1162,28 @@
                 anyBoolean() /* requireConfirmation */,
                 anyInt() /* userId */,
                 eq(TEST_PACKAGE_NAME));
+
+        // Un-downgrading the authenticator allows successful strong auth
+        for (BiometricService.AuthenticatorWrapper wrapper : mBiometricService.mAuthenticators) {
+            if (wrapper.id == testId) {
+                wrapper.updateStrength(Authenticators.BIOMETRIC_STRONG);
+            }
+        }
+
+        resetReceiver();
+        authenticators = Authenticators.BIOMETRIC_STRONG;
+        assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
+                invokeCanAuthenticate(mBiometricService, authenticators));
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                false /* requireConfirmation */, authenticators);
+        waitForIdle();
+        verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
+                eq(mBiometricService.mCurrentAuthSession.mBundle),
+                any(IBiometricServiceReceiverInternal.class),
+                eq(BiometricAuthenticator.TYPE_FINGERPRINT /* biometricModality */),
+                anyBoolean() /* requireConfirmation */,
+                anyInt() /* userId */,
+                eq(TEST_PACKAGE_NAME));
     }
 
     @Test(expected = IllegalStateException.class)
@@ -1245,6 +1267,19 @@
     }
 
     @Test
+    public void testAuthentication_normalAppIgnoresDevicePolicy() throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+        when(mDevicePolicyManager
+                .getKeyguardDisabledFeatures(any() /* admin */, anyInt() /* userHandle */))
+                .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                false /* requireConfirmation */, Authenticators.BIOMETRIC_STRONG);
+        waitForIdle();
+        assertEquals(mBiometricService.mCurrentAuthSession.mState,
+                BiometricService.STATE_AUTH_STARTED);
+    }
+
+    @Test
     public void testWorkAuthentication_faceWorksIfNotDisabledByDevicePolicyManager()
             throws Exception {
         setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
index 312ff2c..df242f3 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
@@ -91,31 +91,31 @@
     @Test
     public void testIsDeviceCredentialAllowed_withIntegerFlags() {
         int authenticators = 0;
-        assertFalse(Utils.isDeviceCredentialAllowed(authenticators));
+        assertFalse(Utils.isCredentialRequested(authenticators));
 
         authenticators |= Authenticators.DEVICE_CREDENTIAL;
-        assertTrue(Utils.isDeviceCredentialAllowed(authenticators));
+        assertTrue(Utils.isCredentialRequested(authenticators));
 
         authenticators |= Authenticators.BIOMETRIC_WEAK;
-        assertTrue(Utils.isDeviceCredentialAllowed(authenticators));
+        assertTrue(Utils.isCredentialRequested(authenticators));
     }
 
     @Test
     public void testIsDeviceCredentialAllowed_withBundle() {
         Bundle bundle = new Bundle();
-        assertFalse(Utils.isDeviceCredentialAllowed(bundle));
+        assertFalse(Utils.isCredentialRequested(bundle));
 
         int authenticators = 0;
         bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
-        assertFalse(Utils.isDeviceCredentialAllowed(bundle));
+        assertFalse(Utils.isCredentialRequested(bundle));
 
         authenticators |= Authenticators.DEVICE_CREDENTIAL;
         bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
-        assertTrue(Utils.isDeviceCredentialAllowed(bundle));
+        assertTrue(Utils.isCredentialRequested(bundle));
 
         authenticators |= Authenticators.BIOMETRIC_WEAK;
         bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
-        assertTrue(Utils.isDeviceCredentialAllowed(bundle));
+        assertTrue(Utils.isCredentialRequested(bundle));
     }
 
     @Test
@@ -140,14 +140,14 @@
         for (int i = 0; i <= 7; i++) {
             int authenticators = 1 << i;
             bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
-            assertTrue(Utils.isBiometricAllowed(bundle));
+            assertTrue(Utils.isBiometricRequested(bundle));
         }
 
         // The rest of the bits are not allowed to integrate with the public APIs
         for (int i = 8; i < 32; i++) {
             int authenticators = 1 << i;
             bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
-            assertFalse(Utils.isBiometricAllowed(bundle));
+            assertFalse(Utils.isBiometricRequested(bundle));
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 39a749f..3f74681 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -5689,6 +5689,9 @@
     }
 
     public void testGetPasswordComplexity_securityExceptionNotThrownForParentInstance() {
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        when(getServices().packageManager.getPackagesForUid(DpmMockContext.CALLER_UID)).thenReturn(
+                new String[0]);
         mServiceContext.permissions.add(permission.REQUEST_PASSWORD_COMPLEXITY);
         setAsProfileOwner(admin1);
 
@@ -5698,12 +5701,18 @@
     }
 
     public void testGetPasswordComplexity_illegalStateExceptionIfLocked() {
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        when(getServices().packageManager.getPackagesForUid(DpmMockContext.CALLER_UID)).thenReturn(
+                new String[0]);
         when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE))
                 .thenReturn(false);
         assertThrows(IllegalStateException.class, () -> dpm.getPasswordComplexity());
     }
 
     public void testGetPasswordComplexity_securityExceptionWithoutPermissions() {
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        when(getServices().packageManager.getPackagesForUid(DpmMockContext.CALLER_UID)).thenReturn(
+                new String[0]);
         when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE))
                 .thenReturn(true);
         assertThrows(SecurityException.class, () -> dpm.getPasswordComplexity());
@@ -5711,6 +5720,9 @@
 
 
     public void testGetPasswordComplexity_currentUserNoPassword() {
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        when(getServices().packageManager.getPackagesForUid(DpmMockContext.CALLER_UID)).thenReturn(
+                new String[0]);
         when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE))
                 .thenReturn(true);
         mServiceContext.permissions.add(permission.REQUEST_PASSWORD_COMPLEXITY);
@@ -5721,6 +5733,9 @@
     }
 
     public void testGetPasswordComplexity_currentUserHasPassword() {
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        when(getServices().packageManager.getPackagesForUid(DpmMockContext.CALLER_UID)).thenReturn(
+                new String[0]);
         when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE))
                 .thenReturn(true);
         mServiceContext.permissions.add(permission.REQUEST_PASSWORD_COMPLEXITY);
@@ -5734,6 +5749,9 @@
     }
 
     public void testGetPasswordComplexity_unifiedChallengeReturnsParentUserPassword() {
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        when(getServices().packageManager.getPackagesForUid(DpmMockContext.CALLER_UID)).thenReturn(
+                new String[0]);
         when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE))
                 .thenReturn(true);
         mServiceContext.permissions.add(permission.REQUEST_PASSWORD_COMPLEXITY);
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index ca00116..234c987 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -25,7 +25,6 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
-import android.content.pm.PackageManager;
 import android.hardware.Sensor;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
@@ -46,9 +45,8 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class AutomaticBrightnessControllerTest {
-
-    private static final int BRIGHTNESS_MIN = 1;
-    private static final int BRIGHTNESS_MAX = 255;
+    private static final float BRIGHTNESS_MIN_FLOAT = 0.0f;
+    private static final float BRIGHTNESS_MAX_FLOAT = 1.0f;
     private static final int LIGHT_SENSOR_RATE = 20;
     private static final int INITIAL_LIGHT_SENSOR_RATE = 20;
     private static final int BRIGHTENING_LIGHT_DEBOUNCE_CONFIG = 0;
@@ -61,7 +59,6 @@
     @Mock BrightnessMappingStrategy mBrightnessMappingStrategy;
     @Mock HysteresisLevels mAmbientBrightnessThresholds;
     @Mock HysteresisLevels mScreenBrightnessThresholds;
-    @Mock PackageManager mPackageManager;
     @Mock Handler mNoopHandler;
 
     private static final int LIGHT_SENSOR_WARMUP_TIME = 0;
@@ -81,11 +78,11 @@
                     }
                 },
                 () -> { }, mContext.getMainLooper(), mSensorManager, lightSensor,
-                mBrightnessMappingStrategy, LIGHT_SENSOR_WARMUP_TIME, BRIGHTNESS_MIN,
-                BRIGHTNESS_MAX, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE, INITIAL_LIGHT_SENSOR_RATE,
-                BRIGHTENING_LIGHT_DEBOUNCE_CONFIG, DARKENING_LIGHT_DEBOUNCE_CONFIG,
-                RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG, mAmbientBrightnessThresholds,
-                mScreenBrightnessThresholds, mPackageManager);
+                mBrightnessMappingStrategy, LIGHT_SENSOR_WARMUP_TIME, BRIGHTNESS_MIN_FLOAT,
+                BRIGHTNESS_MAX_FLOAT, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE,
+                INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG,
+                DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
+                mAmbientBrightnessThresholds, mScreenBrightnessThresholds, mContext);
         controller.setLoggingEnabled(true);
 
         // Configure the brightness controller and grab an instance of the sensor listener,
@@ -110,7 +107,7 @@
 
         // Set up system to return 5 as a brightness value
         float lux1 = 100.0f;
-        float normalizedBrightness1 = 0.02f;
+        float normalizedBrightness1 = 0.0158f; //float equivalent of 5 out of 255
         when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux1))
                 .thenReturn(lux1);
         when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux1))
@@ -156,7 +153,7 @@
 
         // Set up system to return 250 as a brightness value
         float lux1 = 100.0f;
-        float normalizedBrightness1 = 0.98f;
+        float normalizedBrightness1 = 0.981f; //float equivalent of 250 out of 255
         when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux1))
                 .thenReturn(lux1);
         when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux1))
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index d40130a..8dae48c 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -211,7 +211,8 @@
         IntentSender mockReceiver = mock(IntentSender.class);
         List<Rule> rules =
                 Arrays.asList(
-                        new Rule(IntegrityFormula.PACKAGE_NAME.equalTo(PACKAGE_NAME), Rule.DENY));
+                        new Rule(IntegrityFormula.Application.packageNameEquals(PACKAGE_NAME),
+                                Rule.DENY));
 
         mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver);
         runJobInHandler();
@@ -230,7 +231,8 @@
         IntentSender mockReceiver = mock(IntentSender.class);
         List<Rule> rules =
                 Arrays.asList(
-                        new Rule(IntegrityFormula.PACKAGE_NAME.equalTo(PACKAGE_NAME), Rule.DENY));
+                        new Rule(IntegrityFormula.Application.packageNameEquals(PACKAGE_NAME),
+                                Rule.DENY));
 
         mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver);
         runJobInHandler();
@@ -390,7 +392,7 @@
     public void getCurrentRules() throws Exception {
         whitelistUsAsRuleProvider();
         makeUsSystemApp();
-        Rule rule = new Rule(IntegrityFormula.PACKAGE_NAME.equalTo("package"), Rule.DENY);
+        Rule rule = new Rule(IntegrityFormula.Application.packageNameEquals("package"), Rule.DENY);
         when(mIntegrityFileManager.readRules(any())).thenReturn(Arrays.asList(rule));
 
         assertThat(mService.getCurrentRules().getList()).containsExactly(rule);
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
index 38cf562..3dc26af 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
@@ -58,7 +58,7 @@
             getBits(COMPOUND_FORMULA_END, SEPARATOR_BITS);
     private static final String ATOMIC_FORMULA_START_BITS =
             getBits(ATOMIC_FORMULA_START, SEPARATOR_BITS);
-    private static final int INVALID_FORMULA_SEPARATOR_VALUE = 3;
+    private static final int INVALID_FORMULA_SEPARATOR_VALUE = (1 << SEPARATOR_BITS) - 1;
     private static final String INVALID_FORMULA_SEPARATOR_BITS =
             getBits(INVALID_FORMULA_SEPARATOR_VALUE, SEPARATOR_BITS);
 
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java
index 913aff7..ea9e6ff 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java
@@ -112,6 +112,7 @@
                                     ATOMIC_FORMULA_WITH_VERSION_CODE,
                                     ATOMIC_FORMULA_WITH_ISPREINSTALLED)),
                     Rule.DENY);
+    public static final int INVALID_FORMULA_TAG = -1;
 
     @Test
     public void getIndexType_nullRule() {
@@ -290,7 +291,7 @@
         return new AtomicFormula(0) {
             @Override
             public int getTag() {
-                return 4;
+                return INVALID_FORMULA_TAG;
             }
 
             @Override
diff --git a/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java
index f16cf35..4d0ad96 100644
--- a/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java
@@ -35,12 +35,15 @@
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.location.GnssAntennaInfo;
 import android.location.GnssClock;
 import android.location.GnssMeasurementCorrections;
 import android.location.GnssMeasurementsEvent;
 import android.location.GnssNavigationMessage;
+import android.location.GnssRequest;
 import android.location.GnssSingleSatCorrection;
 import android.location.IBatchedLocationCallback;
+import android.location.IGnssAntennaInfoListener;
 import android.location.IGnssMeasurementsListener;
 import android.location.IGnssNavigationMessageListener;
 import android.location.IGnssStatusListener;
@@ -55,6 +58,8 @@
 
 import com.android.server.LocalServices;
 import com.android.server.location.AppForegroundHelper;
+import com.android.server.location.GnssAntennaInfoProvider;
+import com.android.server.location.GnssAntennaInfoProvider.GnssAntennaInfoProviderNative;
 import com.android.server.location.GnssBatchingProvider;
 import com.android.server.location.GnssCapabilitiesProvider;
 import com.android.server.location.GnssLocationProvider;
@@ -101,6 +106,7 @@
     private GnssMeasurementsProvider mTestGnssMeasurementsProvider;
     private GnssStatusListenerHelper mTestGnssStatusProvider;
     private GnssNavigationMessageProvider mTestGnssNavigationMessageProvider;
+    private GnssAntennaInfoProvider mTestGnssAntennaInfoProvider;
 
     // Managers and services
     @Mock
@@ -151,6 +157,8 @@
                 mMockContext, mMockHandler);
         mTestGnssNavigationMessageProvider = createGnssNavigationMessageProvider(
                 mMockContext, mMockHandler);
+        mTestGnssAntennaInfoProvider = createGnssAntennaInfoProvider(
+                mMockContext, mMockHandler);
 
         // Setup GnssLocationProvider to return providers
         when(mMockGnssLocationProvider.getGnssStatusProvider()).thenReturn(
@@ -169,6 +177,8 @@
                 mTestGnssNavigationMessageProvider);
         when(mMockGnssLocationProvider.getNetInitiatedListener()).thenReturn(
                 mNetInitiatedListener);
+        when(mMockGnssLocationProvider.getGnssAntennaInfoProvider()).thenReturn(
+                mTestGnssAntennaInfoProvider);
 
         // Setup GnssBatching provider
         when(mMockGnssBatchingProvider.start(anyLong(), anyBoolean())).thenReturn(true);
@@ -204,6 +214,12 @@
         return mockListener;
     }
 
+    private IGnssAntennaInfoListener createMockGnssAntennaInfoListener() {
+        IGnssAntennaInfoListener mockListener = mock(IGnssAntennaInfoListener.class);
+        overrideAsBinder(mockListener);
+        return mockListener;
+    }
+
     private IBatchedLocationCallback createMockBatchedLocationCallback() {
         IBatchedLocationCallback mockedCallback = mock(IBatchedLocationCallback.class);
         overrideAsBinder(mockedCallback);
@@ -224,6 +240,39 @@
                         Arrays.asList(gnssSingleSatCorrection)).build();
     }
 
+    private static List<GnssAntennaInfo> createDummyGnssAntennaInfos() {
+        double carrierFrequencyMHz = 13758.0;
+
+        GnssAntennaInfo.PhaseCenterOffsetCoordinates phaseCenterOffsetCoordinates = new
+                GnssAntennaInfo.PhaseCenterOffsetCoordinates(
+                4.3d,
+                1.4d,
+                2.10d,
+                2.1d,
+                3.12d,
+                0.5d);
+
+        double[][] phaseCenterVariationCorrectionsMillimeters = new double[10][10];
+        double[][] phaseCenterVariationCorrectionsUncertaintyMillimeters = new double[10][10];
+        GnssAntennaInfo.PhaseCenterVariationCorrections
+                phaseCenterVariationCorrections =
+                new GnssAntennaInfo.PhaseCenterVariationCorrections(
+                        phaseCenterVariationCorrectionsMillimeters,
+                        phaseCenterVariationCorrectionsUncertaintyMillimeters);
+
+        double[][] signalGainCorrectionsDbi = new double[10][10];
+        double[][] signalGainCorrectionsUncertaintyDbi = new double[10][10];
+        GnssAntennaInfo.SignalGainCorrections signalGainCorrections = new
+                GnssAntennaInfo.SignalGainCorrections(
+                signalGainCorrectionsDbi,
+                signalGainCorrectionsUncertaintyDbi);
+
+        List<GnssAntennaInfo> gnssAntennaInfos = new ArrayList();
+        gnssAntennaInfos.add(new GnssAntennaInfo(carrierFrequencyMHz, phaseCenterOffsetCoordinates,
+                phaseCenterVariationCorrections, signalGainCorrections));
+        return gnssAntennaInfos;
+    }
+
     private void enableLocationPermissions() {
         Mockito.doThrow(new SecurityException()).when(
                 mMockContext).enforceCallingPermission(
@@ -298,6 +347,18 @@
         };
     }
 
+    private GnssAntennaInfoProvider createGnssAntennaInfoProvider(Context context,
+            Handler handler) {
+        GnssAntennaInfoProviderNative mockGnssAntenaInfoProviderNative = mock(
+                GnssAntennaInfoProviderNative.class);
+        return new GnssAntennaInfoProvider(context, handler, mockGnssAntenaInfoProviderNative) {
+            @Override
+            protected boolean isGpsEnabled() {
+                return true;
+            }
+        };
+    }
+
     @Test
     public void getGnssYearOfHardwareTest() {
         final int gnssYearOfHardware = 2012;
@@ -563,7 +624,7 @@
 
         assertThrows(SecurityException.class,
                 () -> mGnssManagerService.addGnssMeasurementsListener(
-                        mockGnssMeasurementsListener,
+                        new GnssRequest.Builder().build(), mockGnssMeasurementsListener,
                         "com.android.server", "abcd123", "TestGnssMeasurementsListener"));
 
         mTestGnssMeasurementsProvider.onMeasurementsAvailable(gnssMeasurementsEvent);
@@ -580,8 +641,11 @@
 
         enableLocationPermissions();
 
-        assertThat(mGnssManagerService.addGnssMeasurementsListener(mockGnssMeasurementsListener,
-                "com.android.server", "abcd123", "TestGnssMeasurementsListener")).isEqualTo(true);
+        assertThat(mGnssManagerService.addGnssMeasurementsListener(
+                new GnssRequest.Builder().build(),
+                mockGnssMeasurementsListener,
+                "com.android.server", "abcd123",
+                "TestGnssMeasurementsListener")).isEqualTo(true);
 
         mTestGnssMeasurementsProvider.onMeasurementsAvailable(gnssMeasurementsEvent);
         verify(mockGnssMeasurementsListener, times(1)).onGnssMeasurementsReceived(
@@ -626,8 +690,10 @@
 
         enableLocationPermissions();
 
-        mGnssManagerService.addGnssMeasurementsListener(mockGnssMeasurementsListener,
-                "com.android.server", "abcd123", "TestGnssMeasurementsListener");
+        mGnssManagerService.addGnssMeasurementsListener(new GnssRequest.Builder().build(),
+                mockGnssMeasurementsListener,
+                "com.android.server", "abcd123",
+                "TestGnssMeasurementsListener");
 
         disableLocationPermissions();
 
@@ -648,8 +714,10 @@
 
         enableLocationPermissions();
 
-        mGnssManagerService.addGnssMeasurementsListener(mockGnssMeasurementsListener,
-                "com.android.server", "abcd123", "TestGnssMeasurementsListener");
+        mGnssManagerService.addGnssMeasurementsListener(new GnssRequest.Builder().build(),
+                mockGnssMeasurementsListener,
+                "com.android.server", "abcd123",
+                "TestGnssMeasurementsListener");
 
         disableLocationPermissions();
 
@@ -662,6 +730,82 @@
     }
 
     @Test
+    public void addGnssAntennaInfoListenerWithoutPermissionsTest() throws RemoteException {
+        IGnssAntennaInfoListener mockGnssAntennaInfoListener =
+                createMockGnssAntennaInfoListener();
+        List<GnssAntennaInfo> gnssAntennaInfos = createDummyGnssAntennaInfos();
+
+        disableLocationPermissions();
+
+        assertThrows(SecurityException.class,
+                () -> mGnssManagerService.addGnssAntennaInfoListener(
+                        mockGnssAntennaInfoListener,
+                        "com.android.server", "abcd123", "TestGnssAntennaInfoListener"));
+
+        mTestGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(gnssAntennaInfos);
+        verify(mockGnssAntennaInfoListener, times(0))
+                .onGnssAntennaInfoReceived(gnssAntennaInfos);
+    }
+
+    @Test
+    public void addGnssAntennaInfoListenerWithPermissionsTest() throws RemoteException {
+        IGnssAntennaInfoListener mockGnssAntennaInfoListener =
+                createMockGnssAntennaInfoListener();
+        List<GnssAntennaInfo> gnssAntennaInfos = createDummyGnssAntennaInfos();
+
+        enableLocationPermissions();
+
+        assertThat(mGnssManagerService.addGnssAntennaInfoListener(mockGnssAntennaInfoListener,
+                "com.android.server", "abcd123", "TestGnssAntennaInfoListener")).isEqualTo(true);
+
+        mTestGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(gnssAntennaInfos);
+        verify(mockGnssAntennaInfoListener, times(1))
+                .onGnssAntennaInfoReceived(gnssAntennaInfos);
+    }
+
+    @Test
+    public void removeGnssAntennaInfoListenerWithoutPermissionsTest() throws RemoteException {
+        IGnssAntennaInfoListener mockGnssAntennaInfoListener =
+                createMockGnssAntennaInfoListener();
+        List<GnssAntennaInfo> gnssAntennaInfos = createDummyGnssAntennaInfos();
+
+        enableLocationPermissions();
+
+        mGnssManagerService.addGnssAntennaInfoListener(
+                mockGnssAntennaInfoListener,
+                "com.android.server", "abcd123", "TestGnssAntennaInfoListener");
+
+        disableLocationPermissions();
+
+        mGnssManagerService.removeGnssAntennaInfoListener(
+                mockGnssAntennaInfoListener);
+
+        mTestGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(gnssAntennaInfos);
+        verify(mockGnssAntennaInfoListener, times(0)).onGnssAntennaInfoReceived(
+                gnssAntennaInfos);
+    }
+
+    @Test
+    public void removeGnssAntennaInfoListenerWithPermissionsTest() throws RemoteException {
+        IGnssAntennaInfoListener mockGnssAntennaInfoListener =
+                createMockGnssAntennaInfoListener();
+        List<GnssAntennaInfo> gnssAntennaInfos = createDummyGnssAntennaInfos();
+
+        enableLocationPermissions();
+
+        mGnssManagerService.addGnssAntennaInfoListener(
+                mockGnssAntennaInfoListener,
+                "com.android.server", "abcd123", "TestGnssAntennaInfoListener");
+
+        mGnssManagerService.removeGnssAntennaInfoListener(
+                mockGnssAntennaInfoListener);
+
+        mTestGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(gnssAntennaInfos);
+        verify(mockGnssAntennaInfoListener, times(0)).onGnssAntennaInfoReceived(
+                gnssAntennaInfos);
+    }
+
+    @Test
     public void addGnssNavigationMessageListenerWithoutPermissionsTest() throws RemoteException {
         IGnssNavigationMessageListener mockGnssNavigationMessageListener =
                 createMockGnssNavigationMessageListener();
diff --git a/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
index 4ae374a..9213e1f 100644
--- a/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
@@ -51,6 +51,7 @@
     private static final String APP_PREDICTION_SHARE_UI_SURFACE = "share";
     private static final int APP_PREDICTION_TARGET_COUNT = 4;
     private static final String TEST_PACKAGE_NAME = "com.example";
+    private static final int USER_ID = 0;
 
     private PeopleServiceInternal mServiceInternal;
     private PeopleService.LocalService mLocalService;
@@ -73,7 +74,7 @@
         mServiceInternal = LocalServices.getService(PeopleServiceInternal.class);
         mLocalService = (PeopleService.LocalService) mServiceInternal;
 
-        mSessionId = new AppPredictionSessionId("abc");
+        mSessionId = new AppPredictionSessionId("abc", USER_ID);
         mPredictionContext = new AppPredictionContext.Builder(mContext)
                 .setUiSurface(APP_PREDICTION_SHARE_UI_SURFACE)
                 .setPredictedTargetCount(APP_PREDICTION_TARGET_COUNT)
diff --git a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
index fea1b82..3708571 100644
--- a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
@@ -23,6 +23,7 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.CrossProfileAppsInternal;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -35,6 +36,9 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.SparseArray;
 
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
+import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
+import com.android.server.LocalServices;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
 import org.junit.Before;
@@ -96,6 +100,7 @@
     @Before
     public void initCrossProfileAppsServiceImpl() {
         mTestInjector = new TestInjector();
+        LocalServices.removeServiceForTest(CrossProfileAppsInternal.class);
         mCrossProfileAppsServiceImpl = new CrossProfileAppsServiceImpl(mContext, mTestInjector);
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
     }
@@ -601,6 +606,16 @@
         }
 
         @Override
+        public void withCleanCallingIdentity(ThrowingRunnable action) {
+            action.run();
+        }
+
+        @Override
+        public <T> T withCleanCallingIdentity(ThrowingSupplier<T> action) {
+            return action.get();
+        }
+
+        @Override
         public UserManager getUserManager() {
             return mUserManager;
         }
diff --git a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
index 642cedb..c4289ef 100644
--- a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
@@ -92,18 +92,18 @@
         mNextDimming = SystemClock.uptimeMillis() + 3000L;
 
         // Save the existing state.
-        mIsSettingEnabled = Settings.System.getIntForUser(getContext().getContentResolver(),
-                Settings.System.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT);
+        mIsSettingEnabled = Settings.Secure.getIntForUser(getContext().getContentResolver(),
+                Settings.Secure.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT);
 
-        Settings.System.putIntForUser(getContext().getContentResolver(),
-                Settings.System.ADAPTIVE_SLEEP, 1, UserHandle.USER_CURRENT);
+        Settings.Secure.putIntForUser(getContext().getContentResolver(),
+                Settings.Secure.ADAPTIVE_SLEEP, 1, UserHandle.USER_CURRENT);
         mAttentionDetector.updateEnabledFromSettings(getContext());
     }
 
     @After
     public void tearDown() {
-        Settings.System.putIntForUser(getContext().getContentResolver(),
-                Settings.System.ADAPTIVE_SLEEP, mIsSettingEnabled, UserHandle.USER_CURRENT);
+        Settings.Secure.putIntForUser(getContext().getContentResolver(),
+                Settings.Secure.ADAPTIVE_SLEEP, mIsSettingEnabled, UserHandle.USER_CURRENT);
 
         DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
                 KEY_PRE_DIM_CHECK_DURATION_MILLIS,
@@ -122,8 +122,8 @@
 
     @Test
     public void testOnUserActivity_doesntCheckIfNotEnabled() {
-        Settings.System.putIntForUser(getContext().getContentResolver(),
-                Settings.System.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT);
+        Settings.Secure.putIntForUser(getContext().getContentResolver(),
+                Settings.Secure.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT);
         mAttentionDetector.updateEnabledFromSettings(getContext());
         long when = registerAttention();
         verify(mAttentionManagerInternal, never()).checkAttention(anyLong(), any());
@@ -163,8 +163,8 @@
                 PackageManager.PERMISSION_DENIED);
 
         registerAttention();
-        boolean enabled = Settings.System.getIntForUser(getContext().getContentResolver(),
-                Settings.System.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT) == 1;
+        boolean enabled = Settings.Secure.getIntForUser(getContext().getContentResolver(),
+                Settings.Secure.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT) == 1;
         assertFalse(enabled);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 6eef41a..811089a 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -65,6 +65,7 @@
 import android.os.UserHandle;
 import android.platform.test.annotations.FlakyTest;
 import android.provider.Settings;
+import android.service.dreams.DreamManagerInternal;
 import android.test.mock.MockContentResolver;
 import android.view.Display;
 
@@ -111,6 +112,7 @@
     @Mock private BatteryManagerInternal mBatteryManagerInternalMock;
     @Mock private ActivityManagerInternal mActivityManagerInternalMock;
     @Mock private AttentionManagerInternal mAttentionManagerInternalMock;
+    @Mock private DreamManagerInternal mDreamManagerInternalMock;
     @Mock private PowerManagerService.NativeWrapper mNativeWrapperMock;
     @Mock private Notifier mNotifierMock;
     @Mock private WirelessChargerDetector mWirelessChargerDetectorMock;
@@ -171,6 +173,7 @@
         addLocalServiceMock(BatteryManagerInternal.class, mBatteryManagerInternalMock);
         addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock);
         addLocalServiceMock(AttentionManagerInternal.class, mAttentionManagerInternalMock);
+        addLocalServiceMock(DreamManagerInternal.class, mDreamManagerInternalMock);
 
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
         mResourcesSpy = spy(mContextSpy.getResources());
@@ -651,6 +654,7 @@
         int flags = PowerManager.DOZE_WAKE_LOCK;
         mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
                 null /* workSource */, null /* historyTag */);
+        when(mDreamManagerInternalMock.isDreaming()).thenReturn(true);
         mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(),
                 PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0);
         assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_DOZING);
@@ -732,7 +736,7 @@
         setAttentiveTimeout(5);
         createService();
         startSystem();
-        SystemClock.sleep(8);
+        SystemClock.sleep(20);
         assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/power/PreRebootLoggerTest.java b/services/tests/servicestests/src/com/android/server/power/PreRebootLoggerTest.java
new file mode 100644
index 0000000..a138234
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/PreRebootLoggerTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+
+import com.google.common.io.Files;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+
+/**
+ * Tests for {@link PreRebootLogger}
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PreRebootLoggerTest {
+    @Mock Context mContext;
+    private MockContentResolver mContentResolver;
+    private File mDumpDir;
+
+    @BeforeClass
+    public static void setupOnce() {
+        FakeSettingsProvider.clearSettingsProvider();
+    }
+
+    @AfterClass
+    public static void tearDownOnce() {
+        FakeSettingsProvider.clearSettingsProvider();
+    }
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContentResolver = new MockContentResolver(getInstrumentation().getTargetContext());
+        when(mContext.getContentResolver()).thenReturn(mContentResolver);
+        mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+
+        mDumpDir = Files.createTempDir();
+        mDumpDir.mkdir();
+        mDumpDir.deleteOnExit();
+    }
+
+    @Test
+    public void log_adbEnabled_dumpsInformationProperly() {
+        Settings.Global.putInt(mContentResolver, Settings.Global.ADB_ENABLED, 1);
+
+        PreRebootLogger.log(mContext, mDumpDir);
+
+        assertThat(mDumpDir.list()).asList().containsExactly("system", "package", "rollback");
+    }
+
+    @Test
+    public void log_adbDisabled_wipesDumpedInformation() {
+        Settings.Global.putInt(mContentResolver, Settings.Global.ADB_ENABLED, 1);
+        PreRebootLogger.log(mContext, mDumpDir);
+        Settings.Global.putInt(mContentResolver, Settings.Global.ADB_ENABLED, 0);
+
+        PreRebootLogger.log(mContext, mDumpDir);
+
+        assertThat(mDumpDir.listFiles()).isEmpty();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 03dc213..e768205 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -115,6 +115,7 @@
     private static final long WORKING_SET_THRESHOLD = 12 * HOUR_MS;
     private static final long FREQUENT_THRESHOLD = 24 * HOUR_MS;
     private static final long RARE_THRESHOLD = 48 * HOUR_MS;
+    private static final long RESTRICTED_THRESHOLD = 96 * HOUR_MS;
 
     /** Mock variable used in {@link MyInjector#isPackageInstalled(String, int, int)} */
     private static boolean isPackageInstalled = true;
@@ -232,9 +233,8 @@
         @Override
         String getAppIdleSettings() {
             return "screen_thresholds=0/0/0/" + HOUR_MS + ",elapsed_thresholds=0/"
-                    + WORKING_SET_THRESHOLD + "/"
-                    + FREQUENT_THRESHOLD + "/"
-                    + RARE_THRESHOLD;
+                    + WORKING_SET_THRESHOLD + "/" + FREQUENT_THRESHOLD + "/" + RARE_THRESHOLD
+                    + "/" + RESTRICTED_THRESHOLD;
         }
 
         @Override
@@ -372,12 +372,15 @@
         // RARE bucket
         assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_RARE);
 
+        // RESTRICTED bucket
+        assertTimeout(mController, RESTRICTED_THRESHOLD + 1, STANDBY_BUCKET_RESTRICTED);
+
         reportEvent(mController, USER_INTERACTION, RARE_THRESHOLD + 1, PACKAGE_1);
 
         assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_ACTIVE);
 
-        // RARE bucket
-        assertTimeout(mController, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
+        // RESTRICTED bucket
+        assertTimeout(mController, RESTRICTED_THRESHOLD * 2 + 2, STANDBY_BUCKET_RESTRICTED);
     }
 
     @Test
@@ -437,7 +440,7 @@
         assertNotEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
 
         mInjector.setDisplayOn(true);
-        assertTimeout(mController, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
+        assertTimeout(mController, RARE_THRESHOLD + 2 * HOUR_MS + 1, STANDBY_BUCKET_RARE);
     }
 
     @Test
@@ -642,7 +645,7 @@
         assertBucket(STANDBY_BUCKET_FREQUENT);
 
         // Way past prediction timeout, use system thresholds
-        mInjector.mElapsedRealtime = RARE_THRESHOLD * 4;
+        mInjector.mElapsedRealtime = RARE_THRESHOLD;
         mController.checkIdleStates(USER_ID);
         assertBucket(STANDBY_BUCKET_RARE);
     }
@@ -669,7 +672,7 @@
         assertBucket(STANDBY_BUCKET_RESTRICTED);
 
         // Way past all timeouts. Make sure timeout processing doesn't raise bucket.
-        mInjector.mElapsedRealtime += RARE_THRESHOLD * 4;
+        mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
         mController.checkIdleStates(USER_ID);
         assertBucket(STANDBY_BUCKET_RESTRICTED);
     }
@@ -682,7 +685,7 @@
         reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
         assertBucket(STANDBY_BUCKET_ACTIVE);
 
-        mInjector.mElapsedRealtime += mInjector.getRestrictedBucketDelayMs() - 5000;
+        mInjector.mElapsedRealtime += mInjector.getAutoRestrictedBucketDelayMs() - 5000;
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
                 REASON_MAIN_FORCED_BY_SYSTEM);
         // Bucket shouldn't change
@@ -696,6 +699,45 @@
         assertBucket(STANDBY_BUCKET_RESTRICTED);
     }
 
+    /**
+     * Test that an app is put into the RESTRICTED bucket after enough time has passed.
+     */
+    @Test
+    public void testRestrictedDelay_DelayChange() throws Exception {
+        reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        mInjector.mAutoRestrictedBucketDelayMs = 2 * HOUR_MS;
+        mInjector.mElapsedRealtime += 2 * HOUR_MS - 5000;
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+                REASON_MAIN_FORCED_BY_SYSTEM);
+        // Bucket shouldn't change
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        // bucketing works after timeout
+        mInjector.mElapsedRealtime += 6000;
+
+        Thread.sleep(6000);
+        // Enough time has passed. The app should automatically be put into the RESTRICTED bucket.
+        assertBucket(STANDBY_BUCKET_RESTRICTED);
+    }
+
+    @Test
+    public void testPredictionRaiseFromRestrictedTimeout() {
+        reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
+
+        // Way past all timeouts. App times out into RESTRICTED bucket.
+        mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
+        mController.checkIdleStates(USER_ID);
+        assertBucket(STANDBY_BUCKET_RESTRICTED);
+
+        // Since the app timed out into RESTRICTED, prediction should be able to remove from the
+        // bucket.
+        mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD;
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+                REASON_MAIN_PREDICTED);
+    }
+
     @Test
     public void testCascadingTimeouts() throws Exception {
         reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 57428dc..a0f7f5b 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -98,6 +98,7 @@
 import android.app.PendingIntent;
 import android.app.Person;
 import android.app.RemoteInput;
+import android.app.StatsManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.usage.UsageStatsManagerInternal;
 import android.companion.ICompanionDeviceManager;
@@ -267,6 +268,8 @@
     UserManager mUm;
     @Mock
     NotificationHistoryManager mHistoryManager;
+    @Mock
+    StatsManager mStatsManager;
     NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
     private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake(
             1 << 30);
@@ -440,7 +443,7 @@
                 mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
                 mGroupHelper, mAm, mAppUsageStats,
                 mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
-                mAppOpsManager, mUm, mHistoryManager);
+                mAppOpsManager, mUm, mHistoryManager, mStatsManager);
         mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
 
         mService.setAudioManager(mAudioManager);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
new file mode 100644
index 0000000..f051fa4
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NotificationRecordLoggerTest extends UiServiceTestCase {
+    private static final int UID = 9999;
+    private static final String CHANNEL_ID = "NotificationRecordLoggerTestChannelId";
+
+    private NotificationRecord getNotification(int id, String tag) {
+        final String packageName = mContext.getPackageName();
+        NotificationChannel channel = new NotificationChannel(
+                CHANNEL_ID, CHANNEL_ID, IMPORTANCE_DEFAULT);
+        Notification.Builder nb = new Notification.Builder(mContext, channel.getId());
+        StatusBarNotification sbn = new StatusBarNotification(packageName, packageName, id, tag,
+                UID, 0, nb.build(), new UserHandle(UID), null,
+                0);
+        return new NotificationRecord(mContext, sbn, channel);
+    }
+
+    private NotificationRecordLogger.NotificationRecordPair getNotificationRecordPair(int id,
+            String tag) {
+        return new NotificationRecordLogger.NotificationRecordPair(getNotification(id, tag),
+                null);
+    }
+
+    @Test
+    public void testSmallHash() {
+        assertEquals(0, NotificationRecordLogger.NotificationRecordPair.smallHash(0));
+        final int maxHash = NotificationRecordLogger.NotificationRecordPair.MAX_HASH;
+        assertEquals(0,
+                NotificationRecordLogger.NotificationRecordPair.smallHash(maxHash));
+        assertEquals(0,
+                NotificationRecordLogger.NotificationRecordPair.smallHash(17 * maxHash));
+        assertEquals(maxHash - 1,
+                NotificationRecordLogger.NotificationRecordPair.smallHash(maxHash - 1));
+        assertEquals(maxHash - 1,
+                NotificationRecordLogger.NotificationRecordPair.smallHash(-1));
+    }
+
+    @Test
+    public void testGetNotificationIdHash() {
+        assertEquals(0,
+                getNotificationRecordPair(0, null).getNotificationIdHash());
+        assertEquals(1,
+                getNotificationRecordPair(1, null).getNotificationIdHash());
+        assertEquals(NotificationRecordLogger.NotificationRecordPair.MAX_HASH - 1,
+                getNotificationRecordPair(-1, null).getNotificationIdHash());
+        final String tag = "someTag";
+        final int hash = NotificationRecordLogger.NotificationRecordPair.smallHash(tag.hashCode());
+        assertEquals(hash, getNotificationRecordPair(0, tag).getNotificationIdHash());
+        // We xor the tag and hashcode together before compressing the range. The order of
+        // operations doesn't matter if id is small.
+        assertEquals(1 ^ hash,
+                getNotificationRecordPair(1, tag).getNotificationIdHash());
+        // But it does matter for an id with more 1 bits than fit in the small hash.
+        assertEquals(
+                NotificationRecordLogger.NotificationRecordPair.smallHash(-1 ^ tag.hashCode()),
+                getNotificationRecordPair(-1, tag).getNotificationIdHash());
+        assertNotEquals(-1 ^ hash,
+                NotificationRecordLogger.NotificationRecordPair.smallHash(-1 ^ tag.hashCode()));
+    }
+
+    @Test
+    public void testGetChannelIdHash() {
+        assertEquals(
+                NotificationRecordLogger.NotificationRecordPair.smallHash(CHANNEL_ID.hashCode()),
+                getNotificationRecordPair(0, null).getChannelIdHash());
+        assertNotEquals(
+                NotificationRecordLogger.NotificationRecordPair.smallHash(CHANNEL_ID.hashCode()),
+                CHANNEL_ID.hashCode());
+    }
+
+    @Test
+    public void testGetGroupIdHash() {
+        NotificationRecordLogger.NotificationRecordPair p = getNotificationRecordPair(
+                0, null);
+        assertEquals(0, p.getGroupIdHash());
+        final String group = "someGroup";
+        p.r.setOverrideGroupKey(group);
+        assertEquals(
+                NotificationRecordLogger.NotificationRecordPair.smallHash(group.hashCode()),
+                p.getGroupIdHash());
+    }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
index 64a9804..27f72a1 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
@@ -35,6 +35,7 @@
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
 import android.app.IUriGrantsManager;
+import android.app.StatsManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.role.RoleManager;
 import android.app.usage.UsageStatsManagerInternal;
@@ -142,7 +143,8 @@
                     mock(UsageStatsManagerInternal.class),
                     mock(DevicePolicyManagerInternal.class), mock(IUriGrantsManager.class),
                     mock(UriGrantsManagerInternal.class),
-                    mock(AppOpsManager.class), mUm, mock(NotificationHistoryManager.class));
+                    mock(AppOpsManager.class), mUm, mock(NotificationHistoryManager.class),
+                    mock(StatsManager.class));
         } catch (SecurityException e) {
             if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
                 throw e;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
new file mode 100644
index 0000000..c1a1d5e
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.server.wm.testing.Assert.assertThrows;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.res.Resources;
+import android.platform.test.annotations.Presubmit;
+
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Test;
+
+@Presubmit
+public class DisplayAreaProviderTest {
+
+    @Test
+    public void testFromResources_emptyProvider() {
+        Assert.assertThat(DisplayAreaPolicy.Provider.fromResources(resourcesWithProvider("")),
+                Matchers.instanceOf(DisplayAreaPolicy.Default.Provider.class));
+    }
+
+    @Test
+    public void testFromResources_nullProvider() {
+        Assert.assertThat(DisplayAreaPolicy.Provider.fromResources(resourcesWithProvider(null)),
+                Matchers.instanceOf(DisplayAreaPolicy.Default.Provider.class));
+    }
+
+    @Test
+    public void testFromResources_customProvider() {
+        Assert.assertThat(DisplayAreaPolicy.Provider.fromResources(resourcesWithProvider(
+                TestProvider.class.getName())), Matchers.instanceOf(TestProvider.class));
+    }
+
+    @Test
+    public void testFromResources_badProvider_notImplementingProviderInterface() {
+        assertThrows(IllegalStateException.class, () -> {
+            DisplayAreaPolicy.Provider.fromResources(resourcesWithProvider(
+                    Object.class.getName()));
+        });
+    }
+
+    @Test
+    public void testFromResources_badProvider_doesntExist() {
+        assertThrows(IllegalStateException.class, () -> {
+            DisplayAreaPolicy.Provider.fromResources(resourcesWithProvider(
+                    "com.android.wmtests.nonexistent.Provider"));
+        });
+    }
+
+    private static Resources resourcesWithProvider(String provider) {
+        Resources mock = mock(Resources.class);
+        when(mock.getString(
+                com.android.internal.R.string.config_deviceSpecificDisplayAreaPolicyProvider))
+                .thenReturn(provider);
+        return mock;
+    }
+
+    static class TestProvider implements DisplayAreaPolicy.Provider {
+
+        @Override
+        public DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content,
+                DisplayArea.Root root, DisplayArea<? extends WindowContainer> imeContainer,
+                DisplayContent.TaskContainers taskContainers) {
+            throw new RuntimeException("test stub");
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
index f754c59..39cd76a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
@@ -174,6 +174,6 @@
     }
 
     private static DisplayInfo displayInfoForRotation(int rotation, boolean withDisplayCutout) {
-        return displayInfoAndCutoutForRotation(rotation, withDisplayCutout).first;
+        return displayInfoAndCutoutForRotation(rotation, withDisplayCutout, false).first;
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index e6291a4..c19312d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -34,6 +34,7 @@
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
@@ -91,6 +92,7 @@
     private WindowState mWindow;
     private int mRotation = ROTATION_0;
     private boolean mHasDisplayCutout;
+    private boolean mIsLongEdgeDisplayCutout;
     private static final int DECOR_WINDOW_INSET = 50;
 
     @Before
@@ -124,6 +126,12 @@
         updateDisplayFrames();
     }
 
+    public void addLongEdgeDisplayCutout() {
+        mHasDisplayCutout = true;
+        mIsLongEdgeDisplayCutout = true;
+        updateDisplayFrames();
+    }
+
     private void updateDisplayFrames() {
         mFrames = createDisplayFrames();
         mDisplayContent.mDisplayFrames = mFrames;
@@ -131,7 +139,7 @@
 
     private DisplayFrames createDisplayFrames() {
         final Pair<DisplayInfo, WmDisplayCutout> info = displayInfoAndCutoutForRotation(mRotation,
-                mHasDisplayCutout);
+                mHasDisplayCutout, mIsLongEdgeDisplayCutout);
         return new DisplayFrames(mDisplayContent.getDisplayId(), info.first, info.second);
     }
 
@@ -376,6 +384,46 @@
     }
 
     @Test
+    public void layoutWindowLw_withDisplayCutout_shortEdges() {
+        addDisplayCutout();
+
+        mWindow.mAttrs.flags =
+                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
+        mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
+        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getContentFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+        assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_always() {
+        addDisplayCutout();
+
+        mWindow.mAttrs.flags =
+                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
+        mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
+        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getContentFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+        assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
+    }
+
+    @Test
     public void layoutWindowLw_withDisplayCutout_layoutFullscreen() {
         addDisplayCutout();
 
@@ -551,6 +599,88 @@
     }
 
     @Test
+    public void layoutWindowLw_withLongEdgeDisplayCutout() {
+        addLongEdgeDisplayCutout();
+
+        mWindow.mAttrs.flags =
+                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getContentFrameLw(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0,
+                NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+        assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+    }
+
+    @Test
+    public void layoutWindowLw_withLongEdgeDisplayCutout_never() {
+        addLongEdgeDisplayCutout();
+
+        mWindow.mAttrs.flags =
+                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0,
+                NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getContentFrameLw(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0,
+                NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+        assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+    }
+
+    @Test
+    public void layoutWindowLw_withLongEdgeDisplayCutout_shortEdges() {
+        addLongEdgeDisplayCutout();
+
+        mWindow.mAttrs.flags =
+                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
+        mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getContentFrameLw(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0,
+                NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+        assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+    }
+
+    @Test
+    public void layoutWindowLw_withLongEdgeDisplayCutout_always() {
+        addLongEdgeDisplayCutout();
+
+        mWindow.mAttrs.flags =
+                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
+        mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
+        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getContentFrameLw(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0,
+                NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+        assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
+    }
+
+    @Test
     public void layoutWindowLw_withForwardInset_SoftInputAdjustResize() {
         assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_NONE);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
index d0b3350..2a20c2e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
@@ -111,7 +111,7 @@
     }
 
     static Pair<DisplayInfo, WmDisplayCutout> displayInfoAndCutoutForRotation(int rotation,
-            boolean withDisplayCutout) {
+            boolean withDisplayCutout, boolean isLongEdgeCutout) {
         final DisplayInfo info = new DisplayInfo();
         WmDisplayCutout cutout = null;
 
@@ -121,7 +121,7 @@
         info.rotation = rotation;
         if (withDisplayCutout) {
             cutout = WmDisplayCutout.computeSafeInsets(
-                    displayCutoutForRotation(rotation), info.logicalWidth,
+                    displayCutoutForRotation(rotation, isLongEdgeCutout), info.logicalWidth,
                     info.logicalHeight);
             info.displayCutout = cutout.getDisplayCutout();
         } else {
@@ -130,9 +130,13 @@
         return Pair.create(info, cutout);
     }
 
-    private static DisplayCutout displayCutoutForRotation(int rotation) {
-        final RectF rectF =
-                new RectF(DISPLAY_WIDTH / 4, 0, DISPLAY_WIDTH * 3 / 4, DISPLAY_CUTOUT_HEIGHT);
+    private static DisplayCutout displayCutoutForRotation(int rotation, boolean isLongEdgeCutout) {
+        RectF rectF = new RectF();
+        if (isLongEdgeCutout) {
+            rectF.set(0, DISPLAY_HEIGHT / 4, DISPLAY_CUTOUT_HEIGHT, DISPLAY_HEIGHT * 3 / 4);
+        } else {
+            rectF.set(DISPLAY_WIDTH / 4, 0, DISPLAY_WIDTH * 3 / 4, DISPLAY_CUTOUT_HEIGHT);
+        }
 
         final Matrix m = new Matrix();
         transformPhysicalToLogicalCoordinates(rotation, DISPLAY_WIDTH, DISPLAY_HEIGHT, m);
@@ -141,16 +145,16 @@
         int pos = -1;
         switch (rotation) {
             case ROTATION_0:
-                pos = BOUNDS_POSITION_TOP;
+                pos = isLongEdgeCutout ? BOUNDS_POSITION_LEFT : BOUNDS_POSITION_TOP;
                 break;
             case ROTATION_90:
-                pos = BOUNDS_POSITION_LEFT;
+                pos = isLongEdgeCutout ? BOUNDS_POSITION_BOTTOM : BOUNDS_POSITION_LEFT;
                 break;
             case ROTATION_180:
-                pos = BOUNDS_POSITION_BOTTOM;
+                pos = isLongEdgeCutout ? BOUNDS_POSITION_RIGHT : BOUNDS_POSITION_BOTTOM;
                 break;
             case ROTATION_270:
-                pos = BOUNDS_POSITION_RIGHT;
+                pos = isLongEdgeCutout ? BOUNDS_POSITION_TOP : BOUNDS_POSITION_RIGHT;
                 break;
         }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index e712255..eae007d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -29,12 +29,13 @@
 import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.spy;
 
 import android.platform.test.annotations.Presubmit;
 import android.util.IntArray;
@@ -122,13 +123,13 @@
     // TODO: adjust this test if we pretend to the app that it's still able to control it.
     @Test
     public void testControlsForDispatch_forceStatusBarVisible() {
-        addWindow(TYPE_STATUS_BAR, "topBar").mAttrs.privateFlags |=
+        addWindow(TYPE_STATUS_BAR, "statusBar").mAttrs.privateFlags |=
                 PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
         addWindow(TYPE_NAVIGATION_BAR, "navBar");
 
         final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch();
 
-        // The app must not control the top bar.
+        // The app must not control the status bar.
         assertNotNull(controls);
         assertEquals(1, controls.length);
     }
@@ -137,6 +138,7 @@
     public void testControlsForDispatch_statusBarForceShowNavigation() {
         addWindow(TYPE_NOTIFICATION_SHADE, "notificationShade").mAttrs.privateFlags |=
                 PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
+        addWindow(TYPE_STATUS_BAR, "statusBar");
         addWindow(TYPE_NAVIGATION_BAR, "navBar");
 
         final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch();
@@ -169,7 +171,8 @@
                 .getControllableInsetProvider().getSource().setVisible(false);
         final WindowState app = addWindow(TYPE_APPLICATION, "app");
 
-        final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
+        final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
+        doNothing().when(policy).startAnimation(anyBoolean(), any());
         policy.updateBarControlTarget(app);
         policy.showTransient(
                 IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}));
@@ -184,7 +187,7 @@
     }
 
     @Test
-    public void testShowTransientBars_topCanBeTransient_appGetsTopFakeControl() {
+    public void testShowTransientBars_statusBarCanBeTransient_appGetsStatusBarFakeControl() {
         // Adding app window before setting source visibility is to prevent the visibility from
         // being cleared by InsetsSourceProvider.updateVisibility.
         final WindowState app = addWindow(TYPE_APPLICATION, "app");
@@ -194,14 +197,15 @@
         addWindow(TYPE_NAVIGATION_BAR, "navBar")
                 .getControllableInsetProvider().getSource().setVisible(true);
 
-        final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
+        final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
+        doNothing().when(policy).startAnimation(anyBoolean(), any());
         policy.updateBarControlTarget(app);
         policy.showTransient(
                 IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}));
         final InsetsSourceControl[] controls =
                 mDisplayContent.getInsetsStateController().getControlsForDispatch(app);
 
-        // The app must get the fake control of the top bar, and must get the real control of the
+        // The app must get the fake control of the status bar, and must get the real control of the
         // navigation bar.
         assertEquals(2, controls.length);
         for (int i = controls.length - 1; i >= 0; i--) {
@@ -222,7 +226,8 @@
                 .getControllableInsetProvider().getSource().setVisible(false);
         final WindowState app = addWindow(TYPE_APPLICATION, "app");
 
-        final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
+        final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
+        doNothing().when(policy).startAnimation(anyBoolean(), any());
         policy.updateBarControlTarget(app);
         policy.showTransient(
                 IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}));
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index 7ffdd7c..f811757 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -112,11 +112,44 @@
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
         statusBar.getFrameLw().set(0, 0, 500, 100);
+
+        // We must not have control or control target before we have the insets source window.
+        mProvider.updateControlForTarget(target, true /* force */);
+        assertNull(mProvider.getControl(target));
+        assertNull(mProvider.getControlTarget());
+
+        // We can have the control or the control target after we have the insets source window.
         mProvider.setWindow(statusBar, null, null);
         mProvider.updateControlForTarget(target, false /* force */);
         assertNotNull(mProvider.getControl(target));
+        assertNotNull(mProvider.getControlTarget());
+
+        // We must not have control or control target while we are performing seamless rotation.
+        // And the control and the control target must not be updated during that.
+        mProvider.startSeamlessRotation();
+        assertNull(mProvider.getControl(target));
+        assertNull(mProvider.getControlTarget());
+        mProvider.updateControlForTarget(target, true /* force */);
+        assertNull(mProvider.getControl(target));
+        assertNull(mProvider.getControlTarget());
+
+        // We can have the control and the control target after seamless rotation.
+        mProvider.finishSeamlessRotation(false /* timeout */);
+        mProvider.updateControlForTarget(target, false /* force */);
+        assertNotNull(mProvider.getControl(target));
+        assertNotNull(mProvider.getControlTarget());
+
+        // We can clear the control and the control target.
         mProvider.updateControlForTarget(null, false /* force */);
         assertNull(mProvider.getControl(target));
+        assertNull(mProvider.getControlTarget());
+
+        // We must not have control or control target if the insets source window doesn't have a
+        // surface.
+        statusBar.setSurfaceControl(null);
+        mProvider.updateControlForTarget(target, true /* force */);
+        assertNull(mProvider.getControl(target));
+        assertNull(mProvider.getControlTarget());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 39cdd2c..5cf1fbb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -20,7 +20,9 @@
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
 import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
@@ -33,14 +35,14 @@
 import android.view.InsetsState;
 import android.view.test.InsetsModeSession;
 
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
-
 @SmallTest
 @FlakyTest(detail = "Promote to pre-submit once confirmed stable.")
 @Presubmit
@@ -88,6 +90,10 @@
         final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+
+        // IME cannot be the IME target.
+        ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
+
         getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
         getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
         getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
@@ -98,6 +104,10 @@
     public void testImeForDispatch() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+
+        // IME cannot be the IME target.
+        ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
+
         InsetsSourceProvider statusBarProvider =
                 getController().getSourceProvider(ITYPE_STATUS_BAR);
         statusBarProvider.setWindow(statusBar, null, ((displayFrames, windowState, rect) ->
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index f7aa3cc..0312df6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -34,14 +34,21 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 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;
 
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManager.StackInfo;
+import android.app.PictureInPictureParams;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Binder;
@@ -49,6 +56,8 @@
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.util.ArrayMap;
+import android.content.pm.ActivityInfo;
+import android.util.Rational;
 import android.view.Display;
 import android.view.ITaskOrganizer;
 import android.view.IWindowContainer;
@@ -384,11 +393,19 @@
         RunningTaskInfo info2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
                 mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
 
+        final int initialRootTaskCount = mWm.mAtmService.mTaskOrganizerController.getRootTasks(
+                mDisplayContent.mDisplayId, null /* activityTypes */).size();
+
         final ActivityStack stack = createTaskStackOnDisplay(
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
         final ActivityStack stack2 = createTaskStackOnDisplay(
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mDisplayContent);
 
+        // Check getRootTasks works
+        List<RunningTaskInfo> roots = mWm.mAtmService.mTaskOrganizerController.getRootTasks(
+                mDisplayContent.mDisplayId, null /* activityTypes */);
+        assertEquals(initialRootTaskCount + 2, roots.size());
+
         lastReportedTiles.clear();
         WindowContainerTransaction wct = new WindowContainerTransaction();
         wct.reparent(stack.mRemoteToken, info1.token, true /* onTop */);
@@ -415,11 +432,18 @@
 
         // Check the getChildren call
         List<RunningTaskInfo> children =
-                mWm.mAtmService.mTaskOrganizerController.getChildTasks(info1.token);
+                mWm.mAtmService.mTaskOrganizerController.getChildTasks(info1.token,
+                        null /* activityTypes */);
         assertEquals(2, children.size());
-        children = mWm.mAtmService.mTaskOrganizerController.getChildTasks(info2.token);
+        children = mWm.mAtmService.mTaskOrganizerController.getChildTasks(info2.token,
+                null /* activityTypes */);
         assertEquals(0, children.size());
 
+        // Check that getRootTasks doesn't include children of tiles
+        roots = mWm.mAtmService.mTaskOrganizerController.getRootTasks(mDisplayContent.mDisplayId,
+                null /* activityTypes */);
+        assertEquals(initialRootTaskCount, roots.size());
+
         lastReportedTiles.clear();
         wct = new WindowContainerTransaction();
         wct.reorder(stack2.mRemoteToken, true /* onTop */);
@@ -483,4 +507,76 @@
         verify(transactionListener)
             .transactionReady(anyInt(), any());
     }
+
+    class StubOrganizer extends ITaskOrganizer.Stub {
+        RunningTaskInfo mInfo;
+
+        @Override
+        public void taskAppeared(RunningTaskInfo info) {
+            mInfo = info;
+        }
+        @Override
+        public void taskVanished(IWindowContainer wc) {
+        }
+        @Override
+        public void transactionReady(int id, SurfaceControl.Transaction t) {
+        }
+        @Override
+        public void onTaskInfoChanged(RunningTaskInfo info) {
+        }
+    };
+
+    private ActivityRecord makePipableActivity() {
+        final ActivityRecord record = createActivityRecord(mDisplayContent,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        record.info.flags |= ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
+        spyOn(record);
+        doReturn(true).when(record).checkEnterPictureInPictureState(any(), anyBoolean());
+        return record;
+    }
+
+    @Test
+    public void testEnterPipParams() {
+        final StubOrganizer o = new StubOrganizer();
+        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o, WINDOWING_MODE_PINNED);
+        final ActivityRecord record = makePipableActivity();
+
+        final PictureInPictureParams p =
+            new PictureInPictureParams.Builder().setAspectRatio(new Rational(1, 2)).build();
+        assertTrue(mWm.mAtmService.enterPictureInPictureMode(record.token, p));
+        waitUntilHandlersIdle();
+        assertNotNull(o.mInfo);
+        assertNotNull(o.mInfo.pictureInPictureParams);
+    }
+
+    @Test
+    public void testChangePipParams() {
+        class ChangeSavingOrganizer extends StubOrganizer {
+            RunningTaskInfo mChangedInfo;
+            @Override
+            public void onTaskInfoChanged(RunningTaskInfo info) {
+                mChangedInfo = info;
+            }
+        }
+        ChangeSavingOrganizer o = new ChangeSavingOrganizer();
+        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o, WINDOWING_MODE_PINNED);
+
+        final ActivityRecord record = makePipableActivity();
+        final PictureInPictureParams p =
+            new PictureInPictureParams.Builder().setAspectRatio(new Rational(1, 2)).build();
+        assertTrue(mWm.mAtmService.enterPictureInPictureMode(record.token, p));
+        waitUntilHandlersIdle();
+        assertNotNull(o.mInfo);
+        assertNotNull(o.mInfo.pictureInPictureParams);
+
+        final PictureInPictureParams p2 =
+            new PictureInPictureParams.Builder().setAspectRatio(new Rational(3, 4)).build();
+        mWm.mAtmService.setPictureInPictureParams(record.token, p2);
+        waitUntilHandlersIdle();
+        assertNotNull(o.mChangedInfo);
+        assertNotNull(o.mChangedInfo.pictureInPictureParams);
+        final Rational ratio = o.mChangedInfo.pictureInPictureParams.getAspectRatioRational();
+        assertEquals(3, ratio.getNumerator());
+        assertEquals(4, ratio.getDenominator());
+    }
 }
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 48b6e2a..1371481 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1132,7 +1132,11 @@
                             // dump everything for all users
                             final int numUsers = mUserState.size();
                             for (int user = 0; user < numUsers; user++) {
-                                ipw.println("user=" + mUserState.keyAt(user));
+                                final int userId = mUserState.keyAt(user);
+                                if (!mUserUnlockedStates.get(userId)) {
+                                    continue;
+                                }
+                                ipw.println("user=" + userId);
                                 ipw.increaseIndent();
                                 mUserState.valueAt(user).dumpFile(ipw, null);
                                 ipw.decreaseIndent();
@@ -1153,7 +1157,11 @@
                             // dump info for all users
                             final int numUsers = mUserState.size();
                             for (int user = 0; user < numUsers; user++) {
-                                ipw.println("user=" + mUserState.keyAt(user));
+                                final int userId = mUserState.keyAt(user);
+                                if (!mUserUnlockedStates.get(userId)) {
+                                    continue;
+                                }
+                                ipw.println("user=" + userId);
                                 ipw.increaseIndent();
                                 mUserState.valueAt(user).dumpDatabaseInfo(ipw);
                                 ipw.decreaseIndent();
@@ -1198,11 +1206,13 @@
                 idpw.printPair("user", userId);
                 idpw.println();
                 idpw.increaseIndent();
-                if (checkin) {
-                    mUserState.valueAt(i).checkin(idpw);
-                } else {
-                    mUserState.valueAt(i).dump(idpw, pkg, compact);
-                    idpw.println();
+                if (mUserUnlockedStates.get(userId)) {
+                    if (checkin) {
+                        mUserState.valueAt(i).checkin(idpw);
+                    } else {
+                        mUserState.valueAt(i).dump(idpw, pkg, compact);
+                        idpw.println();
+                    }
                 }
                 mAppStandby.dumpUser(idpw, userId, pkg);
                 idpw.decreaseIndent();
@@ -1224,7 +1234,7 @@
     private int parseUserIdFromArgs(String[] args, int index, IndentingPrintWriter ipw) {
         final int userId;
         try {
-            userId = Integer.valueOf(args[index + 1]);
+            userId = Integer.parseInt(args[index + 1]);
         } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
             ipw.println("invalid user specified.");
             return UserHandle.USER_NULL;
@@ -1233,6 +1243,10 @@
             ipw.println("the specified user does not exist.");
             return UserHandle.USER_NULL;
         }
+        if (!mUserUnlockedStates.get(userId)) {
+            ipw.println("the specified user is currently in a locked state.");
+            return UserHandle.USER_NULL;
+        }
         return userId;
     }
 
diff --git a/startop/iorap/stress/Android.bp b/startop/iorap/stress/Android.bp
new file mode 100644
index 0000000..f9f251bd
--- /dev/null
+++ b/startop/iorap/stress/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_binary {
+  name: "iorap.stress.memory",
+  srcs: ["main_memory.cc"],
+
+  cflags: [
+      "-Wall",
+      "-Wextra",
+      "-Werror",
+      "-Wno-unused-parameter"
+  ],
+
+  shared_libs: [
+      "libbase"
+  ],
+
+  host_supported: true,
+}
diff --git a/startop/iorap/stress/main_memory.cc b/startop/iorap/stress/main_memory.cc
new file mode 100644
index 0000000..1f26861
--- /dev/null
+++ b/startop/iorap/stress/main_memory.cc
@@ -0,0 +1,126 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <chrono>
+#include <fstream>
+#include <iostream>
+#include <random>
+#include <string>
+
+#include <string.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+
+#include <android-base/parseint.h>
+
+static constexpr size_t kBytesPerMb = 1048576;
+const size_t kMemoryAllocationSize = 2 * 1024 * kBytesPerMb;
+
+#define USE_MLOCKALL 0
+
+std::string GetProcessStatus(const char* key) {
+  // Build search pattern of key and separator.
+  std::string pattern(key);
+  pattern.push_back(':');
+
+  // Search for status lines starting with pattern.
+  std::ifstream fs("/proc/self/status");
+  std::string line;
+  while (std::getline(fs, line)) {
+    if (strncmp(pattern.c_str(), line.c_str(), pattern.size()) == 0) {
+      // Skip whitespace in matching line (if any).
+      size_t pos = line.find_first_not_of(" \t", pattern.size());
+      if (pos == std::string::npos) {
+        break;
+      }
+      return std::string(line, pos);
+    }
+  }
+  return "<unknown>";
+}
+
+int main(int argc, char** argv) {
+  size_t allocationSize = 0;
+  if (argc >= 2) {
+    if (!android::base::ParseUint(argv[1], /*out*/&allocationSize)) {
+      std::cerr << "Failed to parse the allocation size (must be 0,MAX_SIZE_T)" << std::endl;
+      return 1;
+    }
+  } else {
+    allocationSize = kMemoryAllocationSize;
+  }
+
+  void* mem = malloc(allocationSize);
+  if (mem == nullptr) {
+    std::cerr << "Malloc failed" << std::endl;
+    return 1;
+  }
+
+  volatile int* imem = static_cast<int *>(mem);  // don't optimize out memory usage
+
+  size_t imemCount = allocationSize / sizeof(int);
+
+  std::cout << "Allocated " << allocationSize << " bytes" << std::endl;
+
+  auto seed = std::chrono::high_resolution_clock::now().time_since_epoch().count();
+  std::mt19937 mt_rand(seed);
+
+  size_t randPrintCount = 10;
+
+  // Write random numbers:
+  // * Ensures each page is resident
+  // * Avoids zeroed out pages (zRAM)
+  // * Avoids same-page merging
+  for (size_t i = 0; i < imemCount; ++i) {
+    imem[i] = mt_rand();
+
+    if (i < randPrintCount) {
+      std::cout << "Generated random value: " << imem[i] << std::endl;
+    }
+  }
+
+#if USE_MLOCKALL
+  /*
+   * Lock all pages from the address space of this process.
+   */
+  if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0) {
+    std::cerr << "Mlockall failed" << std::endl;
+    return 1;
+  }
+#else
+  // Use mlock because of the predictable VmLck size.
+  // Using mlockall tends to bring in anywhere from 2-2.5GB depending on the device.
+  if (mlock(mem, allocationSize) != 0) {
+    std::cerr << "Mlock failed" << std::endl;
+    return 1;
+  }
+#endif
+
+  // Validate memory is actually resident and locked with:
+  // $> cat /proc/$(pidof iorap.stress.memory)/status | grep VmLck
+  std::cout << "Locked memory (VmLck) = " << GetProcessStatus("VmLck") << std::endl;
+
+  std::cout << "Press any key to terminate" << std::endl;
+  int any_input;
+  std::cin >> any_input;
+
+  std::cout << "Terminating..." << std::endl;
+
+  munlockall();
+  free(mem);
+
+  return 0;
+}
diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt
index 460add8..18c2491 100644
--- a/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt
+++ b/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt
@@ -16,6 +16,7 @@
 
 import android.net.Uri
 import android.os.ServiceManager
+import androidx.test.filters.FlakyTest
 import androidx.test.filters.MediumTest
 import org.junit.Test
 import org.mockito.Mockito.argThat
@@ -26,6 +27,7 @@
 
 // @Ignore("Test is disabled until iorapd is added to init and there's selinux policies for it")
 @MediumTest
+@FlakyTest(bugId = 149098310) // Failing on cuttlefish with SecurityException.
 class IIorapIntegrationTest {
     /**
      * @throws ServiceManager.ServiceNotFoundException if iorapd service could not be found
@@ -55,6 +57,9 @@
     private fun testAnyMethod(func: (RequestId) -> Unit) {
         val taskListener = spy(DummyTaskListener())!!
 
+        // FIXME: b/149098310
+        return
+
         try {
             iorapService.setTaskListener(taskListener)
             // Note: Binder guarantees total order for oneway messages sent to the same binder
diff --git a/startop/view_compiler/dex_layout_compiler.cc b/startop/view_compiler/dex_layout_compiler.cc
index cb820f8..bddb8aa 100644
--- a/startop/view_compiler/dex_layout_compiler.cc
+++ b/startop/view_compiler/dex_layout_compiler.cc
@@ -118,7 +118,7 @@
 std::string ResolveName(const std::string& name) {
   if (name == "View") return "android.view.View";
   if (name == "ViewGroup") return "android.view.ViewGroup";
-  if (name.find(".") == std::string::npos) {
+  if (name.find('.') == std::string::npos) {
     return StringPrintf("android.widget.%s", name.c_str());
   }
   return name;
@@ -205,4 +205,4 @@
   view_stack_.pop_back();
 }
 
-}  // namespace startop
\ No newline at end of file
+}  // namespace startop
diff --git a/startop/view_compiler/util.cc b/startop/view_compiler/util.cc
index a0637e6..c34d7b0 100644
--- a/startop/view_compiler/util.cc
+++ b/startop/view_compiler/util.cc
@@ -23,13 +23,13 @@
 
 // TODO: see if we can borrow this from somewhere else, like aapt2.
 string FindLayoutNameFromFilename(const string& filename) {
-  size_t start = filename.rfind("/");
+  size_t start = filename.rfind('/');
   if (start == string::npos) {
     start = 0;
   } else {
     start++;  // advance past '/' character
   }
-  size_t end = filename.find(".", start);
+  size_t end = filename.find('.', start);
 
   return filename.substr(start, end - start);
 }
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index f54f8d1..ec99f36 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -573,6 +573,7 @@
         /**
          * Indicates that the call is an adhoc conference call. This property can be set for both
          * incoming and outgoing calls.
+         * @hide
          */
         public static final int PROPERTY_IS_ADHOC_CONFERENCE = 0x00002000;
 
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 6b0845f..56acdff 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -174,6 +174,7 @@
     /**
      * Returns whether this conference is requesting that the system play a ringback tone
      * on its behalf.
+     * @hide
      */
     public final boolean isRingbackRequested() {
         return mRingbackRequested;
@@ -324,6 +325,7 @@
      * the default dialer's {@link InCallService}.
      *
      * @param videoState The video state in which to answer the connection.
+     * @hide
      */
     public void onAnswer(int videoState) {}
 
@@ -343,6 +345,7 @@
      * a request to reject.
      * For managed {@link ConnectionService}s, this will be called when the user rejects a call via
      * the default dialer's {@link InCallService}.
+     * @hide
      */
     public void onReject() {}
 
@@ -362,6 +365,7 @@
 
     /**
      * Sets state to be ringing.
+     * @hide
      */
     public final void setRinging() {
         setState(Connection.STATE_RINGING);
@@ -487,6 +491,7 @@
      * that do not play a ringback tone themselves in the conference's audio stream.
      *
      * @param ringback Whether the ringback tone is to be played.
+     * @hide
      */
     public final void setRingbackRequested(boolean ringback) {
         if (mRingbackRequested != ringback) {
@@ -736,6 +741,7 @@
      *
      * @param disconnectCause The disconnect cause, ({@see android.telecomm.DisconnectCause}).
      * @return A {@code Conference} which indicates failure.
+     * @hide
      */
     public @NonNull static Conference createFailedConference(
             @NonNull DisconnectCause disconnectCause, @NonNull PhoneAccountHandle phoneAccount) {
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 72c66d2..8049459 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -501,7 +501,7 @@
      * Set by the framework to indicate that it is an adhoc conference call.
      * <p>
      * This is used for Outgoing and incoming conference calls.
-     *
+     * @hide
      */
     public static final int PROPERTY_IS_ADHOC_CONFERENCE = 1 << 12;
 
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index bf4dee2..a28cc4f 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1875,8 +1875,8 @@
      *            {@link #registerPhoneAccount}.
      * @param extras A bundle that will be passed through to
      *            {@link ConnectionService#onCreateIncomingConference}.
+     * @hide
      */
-
     public void addNewIncomingConference(@NonNull PhoneAccountHandle phoneAccount,
             @NonNull Bundle extras) {
         try {
@@ -2115,6 +2115,7 @@
      *
      * @param participants List of participants to start conference with
      * @param extras Bundle of extras to use with the call
+     * @hide
      */
     @RequiresPermission(android.Manifest.permission.CALL_PHONE)
     public void startConference(@NonNull List<Uri> participants,
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 945c888..ebb53c5 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1084,6 +1084,7 @@
     /**
      * Determines whether adhoc conference calls are supported by a carrier.  When {@code true},
      * adhoc conference calling is supported, {@code false otherwise}.
+     * @hide
      */
     public static final String KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL =
             "support_adhoc_conference_calls_bool";
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 6d6eaa4..1cd45e9 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -244,19 +244,30 @@
         } else {
             mParametersUseForLevel = cc.getInt(
                     CarrierConfigManager.KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT);
-            Rlog.i(LOG_TAG, "Using signal strength level: " + mParametersUseForLevel);
+            if (DBG) {
+                Rlog.i(LOG_TAG, "Using signal strength level: " + mParametersUseForLevel);
+            }
             rsrpThresholds = cc.getIntArray(
                     CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY);
             if (rsrpThresholds == null) rsrpThresholds = sRsrpThresholds;
-            Rlog.i(LOG_TAG, "Applying LTE RSRP Thresholds: " + Arrays.toString(rsrpThresholds));
+            if (DBG) {
+                Rlog.i(LOG_TAG, "Applying LTE RSRP Thresholds: "
+                        + Arrays.toString(rsrpThresholds));
+            }
             rsrqThresholds = cc.getIntArray(
                     CarrierConfigManager.KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY);
             if (rsrqThresholds == null) rsrqThresholds = sRsrqThresholds;
-            Rlog.i(LOG_TAG, "Applying LTE RSRQ Thresholds: " + Arrays.toString(rsrqThresholds));
+            if (DBG) {
+                Rlog.i(LOG_TAG, "Applying LTE RSRQ Thresholds: "
+                        + Arrays.toString(rsrqThresholds));
+            }
             rssnrThresholds = cc.getIntArray(
                     CarrierConfigManager.KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY);
             if (rssnrThresholds == null) rssnrThresholds = sRssnrThresholds;
-            Rlog.i(LOG_TAG, "Applying LTE RSSNR Thresholds: " + Arrays.toString(rssnrThresholds));
+            if (DBG) {
+                Rlog.i(LOG_TAG, "Applying LTE RSSNR Thresholds: "
+                        + Arrays.toString(rssnrThresholds));
+            }
             rsrpOnly = cc.getBoolean(
                     CarrierConfigManager.KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL, false);
         }
@@ -283,15 +294,21 @@
 
         if (isLevelForParameter(USE_RSRP)) {
             rsrpLevel = updateLevelWithMeasure(rsrp, rsrpThresholds);
-            Rlog.i(LOG_TAG, "Updated 4G LTE RSRP Level: " + rsrpLevel);
+            if (DBG) {
+                Rlog.i(LOG_TAG, "Updated 4G LTE RSRP Level: " + rsrpLevel);
+            }
         }
         if (isLevelForParameter(USE_RSRQ)) {
             rsrqLevel = updateLevelWithMeasure(mRsrq, rsrqThresholds);
-            Rlog.i(LOG_TAG, "Updated 4G LTE RSRQ Level: " + rsrqLevel);
+            if (DBG) {
+                Rlog.i(LOG_TAG, "Updated 4G LTE RSRQ Level: " + rsrqLevel);
+            }
         }
         if (isLevelForParameter(USE_RSSNR)) {
             rssnrLevel = updateLevelWithMeasure(mRssnr, rssnrThresholds);
-            Rlog.i(LOG_TAG, "Updated 4G LTE RSSNR Level: " + rssnrLevel);
+            if (DBG) {
+                Rlog.i(LOG_TAG, "Updated 4G LTE RSSNR Level: " + rssnrLevel);
+            }
         }
         // Apply the smaller value among three levels of three measures.
         mLevel = Math.min(Math.min(rsrpLevel, rsrqLevel), rssnrLevel);
diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java
index e3d03a3..8562df1 100644
--- a/telephony/java/android/telephony/CellSignalStrengthNr.java
+++ b/telephony/java/android/telephony/CellSignalStrengthNr.java
@@ -40,6 +40,8 @@
      */
     public static final int UNKNOWN_ASU_LEVEL = 99;
 
+    private static final boolean VDBG = false;
+
     private static final String TAG = "CellSignalStrengthNr";
 
     // Lifted from Default carrier configs and max range of SSRSRP
@@ -301,31 +303,45 @@
         } else {
             mParametersUseForLevel = cc.getInt(
                     CarrierConfigManager.KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT, USE_SSRSRP);
-            Rlog.i(TAG, "Using SSRSRP for Level.");
             mSsRsrpThresholds = cc.getIntArray(
                     CarrierConfigManager.KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY);
-            Rlog.i(TAG, "Applying 5G NR SSRSRP Thresholds: " + Arrays.toString(mSsRsrpThresholds));
+            if (VDBG) {
+                Rlog.i(TAG, "Applying 5G NR SSRSRP Thresholds: "
+                        + Arrays.toString(mSsRsrpThresholds));
+            }
             mSsRsrqThresholds = cc.getIntArray(
                     CarrierConfigManager.KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY);
-            Rlog.i(TAG, "Applying 5G NR SSRSRQ Thresholds: " + Arrays.toString(mSsRsrqThresholds));
+            if (VDBG) {
+                Rlog.i(TAG, "Applying 5G NR SSRSRQ Thresholds: "
+                        + Arrays.toString(mSsRsrqThresholds));
+            }
             mSsSinrThresholds = cc.getIntArray(
                     CarrierConfigManager.KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY);
-            Rlog.i(TAG, "Applying 5G NR SSSINR Thresholds: " + Arrays.toString(mSsSinrThresholds));
+            if (VDBG) {
+                Rlog.i(TAG, "Applying 5G NR SSSINR Thresholds: "
+                        + Arrays.toString(mSsSinrThresholds));
+            }
         }
         int ssRsrpLevel = SignalStrength.INVALID;
         int ssRsrqLevel = SignalStrength.INVALID;
         int ssSinrLevel = SignalStrength.INVALID;
         if (isLevelForParameter(USE_SSRSRP)) {
             ssRsrpLevel = updateLevelWithMeasure(mSsRsrp, mSsRsrpThresholds);
-            Rlog.i(TAG, "Updated 5G NR SSRSRP Level: " + ssRsrpLevel);
+            if (VDBG) {
+                Rlog.i(TAG, "Updated 5G NR SSRSRP Level: " + ssRsrpLevel);
+            }
         }
         if (isLevelForParameter(USE_SSRSRQ)) {
             ssRsrqLevel = updateLevelWithMeasure(mSsRsrq, mSsRsrqThresholds);
-            Rlog.i(TAG, "Updated 5G NR SSRSRQ Level: " + ssRsrqLevel);
+            if (VDBG) {
+                Rlog.i(TAG, "Updated 5G NR SSRSRQ Level: " + ssRsrqLevel);
+            }
         }
         if (isLevelForParameter(USE_SSSINR)) {
             ssSinrLevel = updateLevelWithMeasure(mSsSinr, mSsSinrThresholds);
-            Rlog.i(TAG, "Updated 5G NR SSSINR Level: " + ssSinrLevel);
+            if (VDBG) {
+                Rlog.i(TAG, "Updated 5G NR SSSINR Level: " + ssSinrLevel);
+            }
         }
         // Apply the smaller value among three levels of three measures.
         mLevel = Math.min(Math.min(ssRsrpLevel, ssRsrqLevel), ssSinrLevel);
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 8e83c4c..6c920f1 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -801,11 +801,7 @@
                     "Invalid pdu format. format must be either 3gpp or 3gpp2");
         }
         try {
-            ISms iSms = ISms.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSmsServiceRegisterer()
-                            .get());
+            ISms iSms = TelephonyManager.getSmsService();
             if (iSms != null) {
                 iSms.injectSmsPduForSubscriber(
                         getSubscriptionId(), pdu, format, receivedIntent);
@@ -1642,7 +1638,7 @@
      * the service does not exist.
      */
     private static ISms getISmsServiceOrThrow() {
-        ISms iSms = getISmsService();
+        ISms iSms = TelephonyManager.getSmsService();
         if (iSms == null) {
             throw new UnsupportedOperationException("Sms is not supported");
         }
@@ -1650,11 +1646,7 @@
     }
 
     private static ISms getISmsService() {
-        return ISms.Stub.asInterface(
-                TelephonyFrameworkInitializer
-                        .getTelephonyServiceManager()
-                        .getSmsServiceRegisterer()
-                        .get());
+        return TelephonyManager.getSmsService();
     }
 
     /**
@@ -2091,11 +2083,7 @@
     public boolean isSMSPromptEnabled() {
         ISms iSms = null;
         try {
-            iSms = ISms.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSmsServiceRegisterer()
-                            .get());
+            iSms = TelephonyManager.getSmsService();
             return iSms.isSMSPromptEnabled();
         } catch (RemoteException ex) {
             return false;
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 6d82e51..37d3d32 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -627,7 +627,7 @@
                     destinationAddress, message, statusReportRequested);
         }
 
-        return new SubmitPdu(spb);
+        return spb != null ? new SubmitPdu(spb) : null;
     }
 
     /**
@@ -655,7 +655,7 @@
                     destinationAddress, destinationPort, data, statusReportRequested);
         }
 
-        return new SubmitPdu(spb);
+        return spb != null ? new SubmitPdu(spb) : null;
     }
 
     // TODO: SubmitPdu class is used for SMS-DELIVER also now. Refactor for SubmitPdu and new
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 97f50fd..b32e9d7 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1141,11 +1141,7 @@
         SubscriptionInfo subInfo = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 subInfo = iSub.getActiveSubscriptionInfo(subId, mContext.getOpPackageName(),
                         mContext.getFeatureId());
@@ -1179,11 +1175,7 @@
         SubscriptionInfo result = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getActiveSubscriptionInfoForIccId(iccId, mContext.getOpPackageName(),
                         mContext.getFeatureId());
@@ -1217,11 +1209,7 @@
         SubscriptionInfo result = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getActiveSubscriptionInfoForSimSlotIndex(slotIndex,
                         mContext.getOpPackageName(), mContext.getFeatureId());
@@ -1245,11 +1233,7 @@
         List<SubscriptionInfo> result = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getAllSubInfoList(mContext.getOpPackageName(),
                         mContext.getFeatureId());
@@ -1330,11 +1314,7 @@
         List<SubscriptionInfo> activeList = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName(),
                         mContext.getFeatureId());
@@ -1385,11 +1365,7 @@
         List<SubscriptionInfo> result = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getAvailableSubscriptionInfoList(mContext.getOpPackageName(),
                         mContext.getFeatureId());
@@ -1428,11 +1404,7 @@
         List<SubscriptionInfo> result = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getAccessibleSubscriptionInfoList(mContext.getOpPackageName());
             }
@@ -1461,11 +1433,7 @@
     public void requestEmbeddedSubscriptionInfoListRefresh() {
         int cardId = TelephonyManager.from(mContext).getCardIdForDefaultEuicc();
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId);
             }
@@ -1494,11 +1462,7 @@
     @SystemApi
     public void requestEmbeddedSubscriptionInfoListRefresh(int cardId) {
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId);
             }
@@ -1519,11 +1483,7 @@
         int result = 0;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getAllSubInfoCount(mContext.getOpPackageName(),
                         mContext.getFeatureId());
@@ -1552,11 +1512,7 @@
         int result = 0;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getActiveSubInfoCount(mContext.getOpPackageName(),
                         mContext.getFeatureId());
@@ -1577,11 +1533,7 @@
         int result = 0;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getActiveSubInfoCountMax();
             }
@@ -1638,11 +1590,7 @@
         }
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub == null) {
                 Log.e(LOG_TAG, "[addSubscriptionInfoRecord]- ISub service is null");
                 return;
@@ -1676,11 +1624,7 @@
         }
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub == null) {
                 Log.e(LOG_TAG, "[removeSubscriptionInfoRecord]- ISub service is null");
                 return;
@@ -1783,11 +1727,7 @@
         int result = INVALID_SIM_SLOT_INDEX;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getSlotIndex(subscriptionId);
             }
@@ -1821,11 +1761,7 @@
         int[] subId = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 subId = iSub.getSubId(slotIndex);
             }
@@ -1849,11 +1785,7 @@
         int result = INVALID_PHONE_INDEX;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getPhoneId(subId);
             }
@@ -1887,11 +1819,7 @@
         int subId = INVALID_SUBSCRIPTION_ID;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 subId = iSub.getDefaultSubId();
             }
@@ -1914,11 +1842,7 @@
         int subId = INVALID_SUBSCRIPTION_ID;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 subId = iSub.getDefaultVoiceSubId();
             }
@@ -1948,11 +1872,7 @@
     public void setDefaultVoiceSubscriptionId(int subscriptionId) {
         if (VDBG) logd("setDefaultVoiceSubId sub id = " + subscriptionId);
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 iSub.setDefaultVoiceSubId(subscriptionId);
             }
@@ -2000,11 +1920,7 @@
         int subId = INVALID_SUBSCRIPTION_ID;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 subId = iSub.getDefaultSmsSubId();
             }
@@ -2030,11 +1946,7 @@
     public void setDefaultSmsSubId(int subscriptionId) {
         if (VDBG) logd("setDefaultSmsSubId sub id = " + subscriptionId);
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 iSub.setDefaultSmsSubId(subscriptionId);
             }
@@ -2072,11 +1984,7 @@
         int subId = INVALID_SUBSCRIPTION_ID;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 subId = iSub.getDefaultDataSubId();
             }
@@ -2102,11 +2010,7 @@
     public void setDefaultDataSubId(int subscriptionId) {
         if (VDBG) logd("setDataSubscription sub id = " + subscriptionId);
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 iSub.setDefaultDataSubId(subscriptionId);
             }
@@ -2137,11 +2041,7 @@
     /** @hide */
     public void clearSubscriptionInfo() {
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 iSub.clearSubInfo();
             }
@@ -2277,11 +2177,7 @@
      */
     public @NonNull int[] getActiveSubscriptionIdList(boolean visibleOnly) {
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 int[] subId = iSub.getActiveSubIdList(visibleOnly);
                 if (subId != null) return subId;
@@ -2332,11 +2228,7 @@
         int simState = TelephonyManager.SIM_STATE_UNKNOWN;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 simState = iSub.getSimStateForSlotIndex(slotIndex);
             }
@@ -2355,11 +2247,7 @@
      */
     public static void setSubscriptionProperty(int subId, String propKey, String propValue) {
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 iSub.setSubscriptionProperty(subId, propKey, propValue);
             }
@@ -2379,11 +2267,7 @@
             Context context) {
         String resultValue = null;
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 resultValue = iSub.getSubscriptionProperty(subId, propKey,
                         context.getOpPackageName(), context.getFeatureId());
@@ -2547,11 +2431,7 @@
     @UnsupportedAppUsage
     public boolean isActiveSubId(int subId) {
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 return iSub.isActiveSubId(subId, mContext.getOpPackageName(),
                         mContext.getFeatureId());
@@ -2747,11 +2627,7 @@
             @TelephonyManager.SetOpportunisticSubscriptionResult Consumer<Integer> callback) {
         if (VDBG) logd("[setPreferredDataSubscriptionId]+ subId:" + subId);
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub == null) return;
 
             ISetOpportunisticDataCallback callbackStub = new ISetOpportunisticDataCallback.Stub() {
@@ -2794,11 +2670,7 @@
     public int getPreferredDataSubscriptionId() {
         int preferredSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 preferredSubId = iSub.getPreferredDataSubscriptionId();
             }
@@ -2829,11 +2701,7 @@
         List<SubscriptionInfo> subInfoList = null;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 subInfoList = iSub.getOpportunisticSubscriptions(contextPkg, contextFeature);
             }
@@ -2934,11 +2802,7 @@
         ParcelUuid groupUuid = null;
         int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray();
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 groupUuid = iSub.createSubscriptionGroup(subIdArray, pkgForDebug);
             } else {
@@ -2988,11 +2852,7 @@
         int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray();
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 iSub.addSubscriptionsIntoGroup(subIdArray, groupUuid, pkgForDebug);
             } else {
@@ -3044,11 +2904,7 @@
         int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray();
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 iSub.removeSubscriptionsFromGroup(subIdArray, groupUuid, pkgForDebug);
             } else {
@@ -3093,11 +2949,7 @@
 
         List<SubscriptionInfo> result = null;
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = iSub.getSubscriptionsInGroup(groupUuid, contextPkg, contextFeature);
             } else {
@@ -3210,11 +3062,7 @@
             logd("setSubscriptionActivated subId= " + subscriptionId + " enable " + enable);
         }
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 return iSub.setSubscriptionEnabled(enable, subscriptionId);
             }
@@ -3303,11 +3151,7 @@
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isSubscriptionEnabled(int subscriptionId) {
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 return iSub.isSubscriptionEnabled(subscriptionId);
             }
@@ -3330,11 +3174,7 @@
         int subId = INVALID_SUBSCRIPTION_ID;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 subId = iSub.getEnabledSubscriptionId(slotIndex);
             }
@@ -3360,11 +3200,7 @@
         int result = 0;
 
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 result = helper.callMethod(iSub);
             }
@@ -3387,11 +3223,7 @@
      */
     public static int getActiveDataSubscriptionId() {
         try {
-            ISub iSub = ISub.Stub.asInterface(
-                    TelephonyFrameworkInitializer
-                            .getTelephonyServiceManager()
-                            .getSubscriptionServiceRegisterer()
-                            .get());
+            ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
                 return iSub.getActiveDataSubscriptionId();
             }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e520a02..15103bf 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -54,6 +54,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
@@ -93,6 +94,7 @@
 import android.util.Pair;
 
 import com.android.ims.internal.IImsServiceFeatureCallback;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.CellNetworkScanResult;
 import com.android.internal.telephony.IBooleanConsumer;
@@ -100,6 +102,8 @@
 import com.android.internal.telephony.IOns;
 import com.android.internal.telephony.IPhoneSubInfo;
 import com.android.internal.telephony.ISetOpportunisticDataCallback;
+import com.android.internal.telephony.ISms;
+import com.android.internal.telephony.ISub;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.IUpdateAvailableNetworksCallback;
 import com.android.internal.telephony.OperatorInfo;
@@ -295,6 +299,21 @@
     private SubscriptionManager mSubscriptionManager;
     private TelephonyScanManager mTelephonyScanManager;
 
+    /** Cached service handles, cleared by resetServiceHandles() at death */
+    private static final Object sCacheLock = new Object();
+
+    /** @hide */
+    private static boolean sServiceHandleCacheEnabled = true;
+
+    @GuardedBy("sCacheLock")
+    private static IPhoneSubInfo sIPhoneSubInfo;
+    @GuardedBy("sCacheLock")
+    private static ISub sISub;
+    @GuardedBy("sCacheLock")
+    private static ISms sISms;
+    @GuardedBy("sCacheLock")
+    private static final DeathRecipient sServiceDeath = new DeathRecipient();
+
     /** Enum indicating multisim variants
      *  DSDS - Dual SIM Dual Standby
      *  DSDA - Dual SIM Dual Active
@@ -1991,7 +2010,7 @@
     public String getDeviceId(int slotIndex) {
         // FIXME this assumes phoneId == slotIndex
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             return info.getDeviceIdForPhone(slotIndex, mContext.getOpPackageName(),
@@ -2245,7 +2264,7 @@
 
     private String getNaiBySubscriberId(int subId) {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             String nai = info.getNaiForSubscriber(subId, mContext.getOpPackageName(),
@@ -3944,7 +3963,7 @@
     @UnsupportedAppUsage
     public String getSimSerialNumber(int subId) {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             return info.getIccSerialNumberForSubscriber(subId, mContext.getOpPackageName(),
@@ -4217,7 +4236,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public String getSubscriberId(int subId) {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             return info.getSubscriberIdForSubscriber(subId, mContext.getOpPackageName(),
@@ -4253,7 +4272,7 @@
     @Nullable
     public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(@KeyType int keyType) {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null) {
                 Rlog.e(TAG,"IMSI error: Subscriber Info is null");
                 return null;
@@ -4296,7 +4315,7 @@
     @SystemApi
     public void resetCarrierKeysForImsiEncryption() {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null) {
                 Rlog.e(TAG, "IMSI error: Subscriber Info is null");
                 if (!isSystemProcess()) {
@@ -4361,7 +4380,7 @@
      */
     public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo) {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null) return;
             info.setCarrierInfoForImsiEncryption(mSubId, mContext.getOpPackageName(),
                     imsiEncryptionInfo);
@@ -4385,7 +4404,7 @@
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getGroupIdLevel1() {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             return info.getGroupIdLevel1ForSubscriber(getSubId(), mContext.getOpPackageName(),
@@ -4409,7 +4428,7 @@
     @UnsupportedAppUsage
     public String getGroupIdLevel1(int subId) {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             return info.getGroupIdLevel1ForSubscriber(subId, mContext.getOpPackageName(),
@@ -4472,7 +4491,7 @@
             return number;
         }
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             return info.getLine1NumberForSubscriber(subId, mContext.getOpPackageName(),
@@ -4563,7 +4582,7 @@
             return alphaTag;
         }
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             return info.getLine1AlphaTagForSubscriber(subId, getOpPackageName(),
@@ -4651,7 +4670,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public String getMsisdn(int subId) {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             return info.getMsisdnForSubscriber(subId, getOpPackageName(), getFeatureId());
@@ -4685,7 +4704,7 @@
     @UnsupportedAppUsage
     public String getVoiceMailNumber(int subId) {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             return info.getVoiceMailNumberForSubscriber(subId, getOpPackageName(),
@@ -5284,7 +5303,7 @@
     @UnsupportedAppUsage
     public String getVoiceMailAlphaTag(int subId) {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             return info.getVoiceMailAlphaTagForSubscriber(subId, getOpPackageName(),
@@ -5332,7 +5351,7 @@
     @UnsupportedAppUsage
     public String getIsimImpi() {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             //get the Isim Impi based on subId
@@ -5359,7 +5378,7 @@
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public String getIsimDomain() {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             //get the Isim Domain based on subId
@@ -5383,7 +5402,7 @@
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public String[] getIsimImpu() {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             //get the Isim Impu based on subId
@@ -5396,19 +5415,6 @@
         }
     }
 
-   /**
-    * @hide
-    */
-    @UnsupportedAppUsage
-    private IPhoneSubInfo getSubscriberInfo() {
-        // get it each time because that process crashes a lot
-        return IPhoneSubInfo.Stub.asInterface(
-                TelephonyFrameworkInitializer
-                        .getTelephonyServiceManager()
-                        .getPhoneSubServiceRegisterer()
-                        .get());
-    }
-
     /**
      * Device call state: No activity.
      */
@@ -5465,6 +5471,14 @@
     }
 
     /**
+    * @hide
+    */
+    @UnsupportedAppUsage
+    private IPhoneSubInfo getSubscriberInfo() {
+        return getSubscriberInfoService();
+    }
+
+    /**
      * Returns the Telephony call state for calls on a specific SIM slot.
      * <p>
      * Note: This method considers ONLY telephony/mobile calls, where {@link #getCallState()}
@@ -7124,7 +7138,7 @@
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public String getIsimIst() {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             //get the Isim Ist based on subId
@@ -7146,7 +7160,7 @@
     @UnsupportedAppUsage
     public String[] getIsimPcscf() {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             //get the Isim Pcscf based on subId
@@ -7227,7 +7241,7 @@
     @UnsupportedAppUsage
     public String getIccAuthentication(int subId, int appType, int authType, String data) {
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
+            IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
                 return null;
             return info.getIccSimChallengeResponse(subId, appType, authType, data);
@@ -13086,4 +13100,153 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    private static class DeathRecipient implements IBinder.DeathRecipient {
+        @Override
+        public void binderDied() {
+            resetServiceCache();
+        }
+    }
+
+   /**
+    * Reset everything in the service cache; if one handle died then they are
+    * all probably broken.
+    * @hide
+    */
+    private static void resetServiceCache() {
+        synchronized (sCacheLock) {
+            if (sISub != null) {
+                sISub.asBinder().unlinkToDeath(sServiceDeath, 0);
+                sISub = null;
+            }
+            if (sISms != null) {
+                sISms.asBinder().unlinkToDeath(sServiceDeath, 0);
+                sISms = null;
+            }
+            if (sIPhoneSubInfo != null) {
+                sIPhoneSubInfo.asBinder().unlinkToDeath(sServiceDeath, 0);
+                sIPhoneSubInfo = null;
+            }
+        }
+    }
+
+   /**
+    * @hide
+    */
+    static IPhoneSubInfo getSubscriberInfoService() {
+        // Keeps cache disabled until test fixes are checked into AOSP.
+        if (true) {
+            return IPhoneSubInfo.Stub.asInterface(
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getPhoneSubServiceRegisterer()
+                        .get());
+        }
+
+        if (sIPhoneSubInfo == null) {
+            IPhoneSubInfo temp = IPhoneSubInfo.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getPhoneSubServiceRegisterer()
+                        .get());
+            synchronized (sCacheLock) {
+                if (sIPhoneSubInfo == null && temp != null) {
+                    try {
+                        sIPhoneSubInfo = temp;
+                        sIPhoneSubInfo.asBinder().linkToDeath(sServiceDeath, 0);
+                    } catch (Exception e) {
+                        // something has gone horribly wrong
+                        sIPhoneSubInfo = null;
+                    }
+                }
+            }
+        }
+        return sIPhoneSubInfo;
+    }
+
+   /**
+    * @hide
+    */
+    static ISub getSubscriptionService() {
+        // Keeps cache disabled until test fixes are checked into AOSP.
+        if (true) {
+            return ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
+        }
+
+        if (sISub == null) {
+            ISub temp = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
+            synchronized (sCacheLock) {
+                if (sISub == null && temp != null) {
+                    try {
+                        sISub = temp;
+                        sISub.asBinder().linkToDeath(sServiceDeath, 0);
+                    } catch (Exception e) {
+                        // something has gone horribly wrong
+                        sISub = null;
+                    }
+                }
+            }
+        }
+        return sISub;
+    }
+
+    /**
+    * @hide
+    */
+    static ISms getSmsService() {
+        // Keeps cache disabled until test fixes are checked into AOSP.
+        if (true) {
+            return ISms.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSmsServiceRegisterer()
+                            .get());
+        }
+
+        if (sISms == null) {
+            ISms temp = ISms.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSmsServiceRegisterer()
+                            .get());
+            synchronized (sCacheLock) {
+                if (sISms == null && temp != null) {
+                    try {
+                        sISms = temp;
+                        sISms.asBinder().linkToDeath(sServiceDeath, 0);
+                    } catch (Exception e) {
+                        // something has gone horribly wrong
+                        sISms = null;
+                    }
+                }
+            }
+        }
+        return sISms;
+    }
+
+    /**
+     * Disables service handle caching for tests that utilize mock services.
+     * @hide
+     */
+    @VisibleForTesting
+    public static void disableServiceHandleCaching() {
+        sServiceHandleCacheEnabled = false;
+    }
+
+    /**
+     * Reenables service handle caching.
+     * @hide
+     */
+    @VisibleForTesting
+    public static void enableServiceHandleCaching() {
+        sServiceHandleCacheEnabled = true;
+    }
 }
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index d483291..c506cd5 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -59,6 +59,7 @@
      * @see #isAvailable(int)
      * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback)
      * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback)
+     * @hide
      */
     public static class AvailabilityCallback {
 
@@ -141,14 +142,16 @@
     /**
      * @return A {@link RcsUceAdapter} used for User Capability Exchange (UCE) operations for
      * this subscription.
-     * @hide
      */
     @NonNull
     public RcsUceAdapter getUceAdapter() {
         return new RcsUceAdapter(mSubId);
     }
 
-    /**{@inheritDoc}*/
+    /**
+     * {@inheritDoc}
+     * @hide
+     */
     @Override
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void registerImsRegistrationCallback(
@@ -177,7 +180,10 @@
         }
     }
 
-    /**{@inheritDoc}*/
+    /**
+     * {@inheritDoc
+     * @hide
+     */
     @Override
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void unregisterImsRegistrationCallback(
@@ -199,7 +205,10 @@
         }
     }
 
-    /**{@inheritDoc}*/
+    /**
+     * {@inheritDoc}
+     * @hide
+     */
     @Override
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void getRegistrationState(@NonNull @CallbackExecutor Executor executor,
@@ -229,7 +238,10 @@
         }
     }
 
-    /**{@inheritDoc}*/
+    /**
+     * {@inheritDoc}
+     * @hide
+     */
     @Override
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor,
@@ -280,6 +292,7 @@
      * {@link ImsRcsManager} is valid, but the ImsService associated with the subscription is not
      * available. This can happen if the ImsService has crashed, for example, or if the subscription
      * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     * @hide
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void registerRcsAvailabilityCallback(@NonNull @CallbackExecutor Executor executor,
@@ -319,6 +332,7 @@
      * @throws ImsException if the IMS service is not available when calling this method
      * {@link ImsRcsController#unregisterRcsAvailabilityCallback()}.
      * See {@link ImsException#getCode()} for more information on the error codes.
+     * @hide
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void unregisterRcsAvailabilityCallback(@NonNull AvailabilityCallback c)
@@ -361,6 +375,7 @@
      * @throws ImsException if the IMS service is not available when calling this method
      * {@link ImsRcsController#isCapable(int, int)}.
      * See {@link ImsException#getCode()} for more information on the error codes.
+     * @hide
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isCapable(@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
@@ -395,6 +410,7 @@
      * @throws ImsException if the IMS service is not available when calling this method
      * {@link ImsRcsController#isAvailable(int, int)}.
      * See {@link ImsException#getCode()} for more information on the error codes.
+     * @hide
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isAvailable(@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability)
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 72a00ce..fc7c1ee 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -51,55 +51,67 @@
 
     /**
      * An unknown error has caused the request to fail.
+     * @hide
      */
     public static final int ERROR_GENERIC_FAILURE = 1;
     /**
      * The carrier network does not have UCE support enabled for this subscriber.
+     * @hide
      */
     public static final int ERROR_NOT_ENABLED = 2;
     /**
      * The data network that the device is connected to does not support UCE currently (e.g. it is
      * 1x only currently).
+     * @hide
      */
     public static final int ERROR_NOT_AVAILABLE = 3;
     /**
      * The network has responded with SIP 403 error and a reason "User not registered."
+     * @hide
      */
     public static final int ERROR_NOT_REGISTERED = 4;
     /**
      * The network has responded to this request with a SIP 403 error and reason "not authorized for
      * presence" for this subscriber.
+     * @hide
      */
     public static final int ERROR_NOT_AUTHORIZED = 5;
     /**
      * The network has responded to this request with a SIP 403 error and no reason.
+     * @hide
      */
     public static final int ERROR_FORBIDDEN = 6;
     /**
      * The contact URI requested is not provisioned for VoLTE or it is not known as an IMS
      * subscriber to the carrier network.
+     * @hide
      */
     public static final int ERROR_NOT_FOUND = 7;
     /**
      * The capabilities request contained too many URIs for the carrier network to handle. Retry
      * with a lower number of contact numbers. The number varies per carrier.
+     * @hide
      */
     // TODO: Try to integrate this into the API so that the service will split based on carrier.
     public static final int ERROR_REQUEST_TOO_LARGE = 8;
     /**
      * The network did not respond to the capabilities request before the request timed out.
+     * @hide
      */
     public static final int ERROR_REQUEST_TIMEOUT = 10;
     /**
      * The request failed due to the service having insufficient memory.
+     * @hide
      */
     public static final int ERROR_INSUFFICIENT_MEMORY = 11;
     /**
      * The network was lost while trying to complete the request.
+     * @hide
      */
     public static final int ERROR_LOST_NETWORK = 12;
     /**
      * The request has failed because the same request has already been added to the queue.
+     * @hide
      */
     public static final int ERROR_ALREADY_IN_QUEUE = 13;
 
@@ -124,28 +136,33 @@
     /**
      * The last publish has resulted in a "200 OK" response or the device is using SIP OPTIONS for
      * UCE.
+     * @hide
      */
     public static final int PUBLISH_STATE_200_OK = 1;
 
     /**
      * The hasn't published its capabilities since boot or hasn't gotten any publish response yet.
+     * @hide
      */
     public static final int PUBLISH_STATE_NOT_PUBLISHED = 2;
 
     /**
      * The device has tried to publish its capabilities, which has resulted in an error. This error
      * is related to the fact that the device is not VoLTE provisioned.
+     * @hide
      */
     public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3;
 
     /**
      * The device has tried to publish its capabilities, which has resulted in an error. This error
      * is related to the fact that the device is not RCS or UCE provisioned.
+     * @hide
      */
     public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4;
 
     /**
      * The last publish resulted in a "408 Request Timeout" response.
+     * @hide
      */
     public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5;
 
@@ -154,6 +171,7 @@
      * or SIP 423 - "Interval too short".
      * <p>
      * Device shall retry with exponential back-off.
+     * @hide
      */
     public static final int PUBLISH_STATE_OTHER_ERROR = 6;
 
@@ -174,6 +192,7 @@
      * Provides a one-time callback for the response to a UCE request. After this callback is called
      * by the framework, the reference to this callback will be discarded on the service side.
      * @see #requestCapabilities(Executor, List, CapabilitiesCallback)
+     * @hide
      */
     public static class CapabilitiesCallback {
 
@@ -225,6 +244,7 @@
      * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
      * available. This can happen if the ImsService has crashed, for example, or if the subscription
      * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     * @hide
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void requestCapabilities(@NonNull @CallbackExecutor Executor executor,
@@ -287,6 +307,7 @@
      * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
      * available. This can happen if the ImsService has crashed, for example, or if the subscription
      * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     * @hide
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public @PublishState int getUcePublishState() throws ImsException {
@@ -307,14 +328,13 @@
     }
 
     /**
-     * The user’s setting for whether or not Presence and User Capability Exchange (UCE) is enabled
-     * for the associated subscription.
+     * The user’s setting for whether or not User Capability Exchange (UCE) is enabled for the
+     * associated subscription.
+     * <p>
+     * Note: This setting does not affect whether or not the device publishes its service
+     * capabilities if the subscription supports presence publication.
      *
-     * @return true if the user’s setting for UCE is enabled, false otherwise. If false,
-     * {@link ImsRcsManager#isCapable(int, int)} will return false for
-     * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} and
-     * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE}
-     * @see #setUceSettingEnabled(boolean)
+     * @return true if the user’s setting for UCE is enabled, false otherwise.
      * @throws ImsException if the subscription associated with this instance of
      * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
      * available. This can happen if the ImsService has crashed, for example, or if the subscription
@@ -340,12 +360,12 @@
 
     /**
      * Change the user’s setting for whether or not UCE is enabled for the associated subscription.
-     * @param isEnabled the user's setting for whether or not they wish for Presence and User
-     *         Capability Exchange to be enabled. If false,
-     *         {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} and
-     *         {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} capability will be
-     *         disabled, depending on which type of UCE the carrier supports.
-     * @see #isUceSettingEnabled()
+     * <p>
+     * Note: This setting does not affect whether or not the device publishes its service
+     * capabilities if the subscription supports presence publication.
+     *
+     * @param isEnabled the user's setting for whether or not they wish for User
+     *         Capability Exchange to be enabled.
      * @throws ImsException if the subscription associated with this instance of
      * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
      * available. This can happen if the ImsService has crashed, for example, or if the subscription
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 8e67621..98b0bcf 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -193,6 +193,7 @@
      * of the capability and notify the capability status as true using
      * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the
      * framework that the capability is available for usage.
+     * @hide
      */
     public static class RcsImsCapabilities extends Capabilities {
         /** @hide*/
@@ -286,6 +287,7 @@
      * set, the {@link RcsFeature} has brought up the capability and is ready for framework
      * requests. To change the status of the capabilities
      * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called.
+     * @hide
      */
     @Override
     public @NonNull final RcsImsCapabilities queryCapabilityStatus() {
@@ -296,6 +298,7 @@
      * Notify the framework that the capabilities status has changed. If a capability is enabled,
      * this signals to the framework that the capability has been initialized and is ready.
      * Call {@link #queryCapabilityStatus()} to return the current capability status.
+     * @hide
      */
     public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities c) {
         if (c == null) {
@@ -310,6 +313,7 @@
      * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} to
      * enable or disable capability A, this method should return the correct configuration for
      * capability A afterwards (until it has changed).
+     * @hide
      */
     public boolean queryCapabilityConfiguration(
             @RcsImsCapabilities.RcsImsCapabilityFlag int capability,
@@ -331,6 +335,7 @@
      * If for some reason one or more of these capabilities can not be enabled/disabled,
      * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError(int, int, int)} should
      * be called for each capability change that resulted in an error.
+     * @hide
      */
     @Override
     public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
@@ -349,6 +354,7 @@
      *
      * @return An instance of {@link RcsSipOptionsImplBase} that implements SIP options exchange if
      * it is supported by the device.
+     * @hide
      */
     public @NonNull RcsSipOptionsImplBase getOptionsExchangeImpl() {
         // Base Implementation, override to implement functionality
@@ -364,6 +370,7 @@
      *
      * @return An instance of {@link RcsPresenceExchangeImplBase} that implements presence
      * exchange if it is supported by the device.
+     * @hide
      */
     public @NonNull RcsPresenceExchangeImplBase getPresenceExchangeImpl() {
         // Base Implementation, override to implement functionality.
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java
index a24af2f..fda295a 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java
@@ -17,8 +17,6 @@
 package android.telephony.ims.stub;
 
 import android.annotation.IntDef;
-import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.RemoteException;
 import android.telephony.ims.ImsException;
 import android.telephony.ims.aidl.IRcsFeatureListener;
@@ -34,8 +32,6 @@
  *
  * @hide
  */
-@SystemApi
-@TestApi
 public class RcsCapabilityExchange {
 
     /**  Service is unknown. */
diff --git a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java
index f200ea2..bb03448 100644
--- a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java
@@ -18,8 +18,6 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.net.Uri;
 import android.os.RemoteException;
 import android.telephony.ims.ImsException;
@@ -39,8 +37,6 @@
  *
  * @hide
  */
-@SystemApi
-@TestApi
 public class RcsPresenceExchangeImplBase extends RcsCapabilityExchange {
 
     private static final String LOG_TAG = "RcsPresenceExchangeIB";
diff --git a/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java
index 355c4dd..2035fac 100644
--- a/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java
@@ -19,8 +19,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.net.Uri;
 import android.os.RemoteException;
 import android.telephony.ims.ImsException;
@@ -37,8 +35,6 @@
  *
  * @hide
  */
-@SystemApi
-@TestApi
 public class RcsSipOptionsImplBase extends RcsCapabilityExchange {
 
     private static final String LOG_TAG = "RcsSipOptionsImplBase";
diff --git a/core/java/com/android/internal/telephony/IWapPushManager.aidl b/telephony/java/com/android/internal/telephony/IWapPushManager.aidl
similarity index 82%
rename from core/java/com/android/internal/telephony/IWapPushManager.aidl
rename to telephony/java/com/android/internal/telephony/IWapPushManager.aidl
index 9f6851b..1c3df65 100644
--- a/core/java/com/android/internal/telephony/IWapPushManager.aidl
+++ b/telephony/java/com/android/internal/telephony/IWapPushManager.aidl
@@ -18,7 +18,6 @@
 
 import android.content.Intent;
 
-/** @hide */
 interface IWapPushManager {
     /**
      * Processes WAP push message and triggers the receiver application registered
@@ -27,10 +26,11 @@
     int processMessage(String app_id, String content_type, in Intent intent);
 
     /**
-     * Adds receiver application into the application ID table.
-     * Returns true if inserting the information is successful. Inserting duplicated
+     * Add receiver application into the application ID table.
+     * Returns true if inserting the information is successfull. Inserting the duplicated
      * record in the application ID table is not allowed. Use update/delete method.
      */
+    @UnsupportedAppUsage
     boolean addPackage(String x_app_id, String content_type,
             String package_name, String class_name,
             int app_type, boolean need_signature, boolean further_processing);
@@ -39,14 +39,17 @@
      * Updates receiver application that is last added.
      * Returns true if updating the information is successfull.
      */
+    @UnsupportedAppUsage
     boolean updatePackage(String x_app_id, String content_type,
             String package_name, String class_name,
             int app_type, boolean need_signature, boolean further_processing);
 
     /**
-     * Deletes receiver application information.
+     * Delites receiver application information.
      * Returns true if deleting is successfull.
      */
+    @UnsupportedAppUsage
     boolean deletePackage(String x_app_id, String content_type,
-            String package_name, String class_name);
+                            String package_name, String class_name);
 }
+
diff --git a/tests/BootImageProfileTest/AndroidTest.xml b/tests/BootImageProfileTest/AndroidTest.xml
index b4f2663..d7f8204 100644
--- a/tests/BootImageProfileTest/AndroidTest.xml
+++ b/tests/BootImageProfileTest/AndroidTest.xml
@@ -19,18 +19,8 @@
     -->
     <target_preparer
         class="com.android.tradefed.targetprep.DeviceSetup">
-        <!-- we need this magic flag, otherwise it always reboots and breaks the selinux -->
+        <!-- we need this magic flag, otherwise it always reboots and breaks selinux -->
         <option name="force-skip-system-props" value="true" />
-
-        <option name="run-command" value="device_config put runtime_native_boot profilesystemserver true" />
-        <option name="run-command" value="device_config put runtime_native_boot profilebootclasspath true" />
-
-        <!-- Profiling does not pick up the above changes we restart the shell -->
-        <option name="run-command" value="stop" />
-        <option name="run-command" value="start" />
-
-        <!-- give it some time to restart the shell; otherwise the first unit test might fail -->
-        <option name="run-command" value="sleep 2" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.HostTest" >
diff --git a/tests/BootImageProfileTest/OWNERS b/tests/BootImageProfileTest/OWNERS
new file mode 100644
index 0000000..657b3f2
--- /dev/null
+++ b/tests/BootImageProfileTest/OWNERS
@@ -0,0 +1,4 @@
+mathieuc@google.com
+calin@google.com
+yawanng@google.com
+sehr@google.com
diff --git a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
index 10f3e54..1c8b6be 100644
--- a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
+++ b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
@@ -42,10 +42,9 @@
     }
 
     /**
-     * Test that the boot image profile properties are set.
+     * Validate that the boot image profile properties are set.
      */
-    @Test
-    public void testProperties() throws Exception {
+    public void validateProperties() throws Exception {
         String res = mTestDevice.getProperty(
                 "persist.device_config.runtime_native_boot.profilebootclasspath");
         assertTrue("profile boot class path not enabled", res != null && res.equals("true"));
@@ -67,13 +66,37 @@
 
     @Test
     public void testSystemServerProfile() throws Exception {
+        final int numIterations = 20;
+        for (int i = 1; i <= numIterations; ++i) {
+            String res;
+            res = mTestDevice.getProperty(
+                    "persist.device_config.runtime_native_boot.profilebootclasspath");
+            boolean profileBootClassPath = res != null && res.equals("true");
+            res = mTestDevice.getProperty(
+                    "persist.device_config.runtime_native_boot.profilesystemserver");
+            boolean profileSystemServer = res != null && res.equals("true");
+            if (profileBootClassPath && profileSystemServer) {
+                break;
+            }
+            if (i == numIterations) {
+                assertTrue("profile system server not enabled", profileSystemServer);
+                assertTrue("profile boot class path not enabled", profileSystemServer);
+            }
+
+            res = mTestDevice.executeShellCommand(
+                    "device_config put runtime_native_boot profilebootclasspath true");
+            res = mTestDevice.executeShellCommand(
+                    "device_config put runtime_native_boot profilesystemserver true");
+            res = mTestDevice.executeShellCommand("stop");
+            res = mTestDevice.executeShellCommand("start");
+            Thread.sleep(5000);
+        }
         // Trunacte the profile before force it to be saved to prevent previous profiles
         // causing the test to pass.
         String res;
         res = mTestDevice.executeShellCommand("truncate -s 0 " + SYSTEM_SERVER_PROFILE).trim();
         assertTrue(res, res.length() == 0);
         // Wait up to 20 seconds for the profile to be saved.
-        final int numIterations = 20;
         for (int i = 1; i <= numIterations; ++i) {
             // Force save the profile since we truncated it.
             if (forceSaveProfile("system_server")) {
@@ -89,6 +112,9 @@
             // In case the profile is partially saved, wait an extra second.
             Thread.sleep(1000);
 
+            // Validate that properties are still set.
+            validateProperties();
+
             // Validate that the profile is non empty.
             res = mTestDevice.executeShellCommand("profman --dump-only --profile-file="
                     + SYSTEM_SERVER_PROFILE);
diff --git a/tests/PlatformCompatGating/Android.bp b/tests/PlatformCompatGating/Android.bp
index 74dfde8..342c47d 100644
--- a/tests/PlatformCompatGating/Android.bp
+++ b/tests/PlatformCompatGating/Android.bp
@@ -18,6 +18,7 @@
     name: "PlatformCompatGating",
     // Only compile source java files in this apk.
     srcs: ["src/**/*.java"],
+    test_suites: ["device-tests"],
     static_libs: [
         "junit",
         "androidx.test.runner",
diff --git a/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java b/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
index 2c2e828..a72bd38 100644
--- a/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
+++ b/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
@@ -16,22 +16,99 @@
 
 package com.android.tests.rollback.host;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.testng.Assert.assertThrows;
+
+import com.android.tradefed.device.LogcatReceiver;
+import com.android.tradefed.result.InputStreamSource;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Runs the network rollback tests.
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
 public class NetworkStagedRollbackTest extends BaseHostJUnit4Test {
     /**
+     * Runs the given phase of a test by calling into the device.
+     * Throws an exception if the test phase fails.
+     * <p>
+     * For example, <code>runPhase("testApkOnlyEnableRollback");</code>
+     */
+    private void runPhase(String phase) throws Exception {
+        assertTrue(runDeviceTests("com.android.tests.rollback",
+                "com.android.tests.rollback.NetworkStagedRollbackTest",
+                phase));
+    }
+
+    private static final String REASON_EXPLICIT_HEALTH_CHECK = "REASON_EXPLICIT_HEALTH_CHECK";
+
+    private static final String ROLLBACK_INITIATE = "ROLLBACK_INITIATE";
+    private static final String ROLLBACK_BOOT_TRIGGERED = "ROLLBACK_BOOT_TRIGGERED";
+
+    private LogcatReceiver mReceiver;
+
+    @Before
+    public void setUp() throws Exception {
+        mReceiver =  new LogcatReceiver(getDevice(), "logcat -s WatchdogRollbackLogger",
+                getDevice().getOptions().getMaxLogcatDataSize(), 0);
+        mReceiver.start();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mReceiver.stop();
+        mReceiver.clear();
+    }
+
+    /**
      * Tests failed network health check triggers watchdog staged rollbacks.
      */
     @Test
     public void testNetworkFailedRollback() throws Exception {
+        try {
+            // Disconnect internet so we can test network health triggered rollbacks
+            getDevice().executeShellCommand("svc wifi disable");
+            getDevice().executeShellCommand("svc data disable");
+
+            runPhase("testNetworkFailedRollback_Phase1");
+            // Reboot device to activate staged package
+            getDevice().reboot();
+
+            // Verify rollback was enabled
+            runPhase("testNetworkFailedRollback_Phase2");
+            assertThrows(AssertionError.class, () -> runPhase("testNetworkFailedRollback_Phase3"));
+
+            getDevice().waitForDeviceAvailable();
+            // Verify rollback was executed after health check deadline
+            runPhase("testNetworkFailedRollback_Phase4");
+            InputStreamSource logcatStream = mReceiver.getLogcatData();
+            try {
+                List<String> watchdogEvents = getWatchdogLoggingEvents(logcatStream);
+                assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_INITIATE, null,
+                        REASON_EXPLICIT_HEALTH_CHECK, null));
+                assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null,
+                        null, null));
+            } finally {
+                logcatStream.close();
+            }
+        } finally {
+            // Reconnect internet again so we won't break tests which assume internet available
+            getDevice().executeShellCommand("svc wifi enable");
+            getDevice().executeShellCommand("svc data enable");
+        }
     }
 
     /**
@@ -39,5 +116,83 @@
      */
     @Test
     public void testNetworkPassedDoesNotRollback() throws Exception {
+        runPhase("testNetworkPassedDoesNotRollback_Phase1");
+        // Reboot device to activate staged package
+        getDevice().reboot();
+
+        // Verify rollback was enabled
+        runPhase("testNetworkPassedDoesNotRollback_Phase2");
+
+        // Connect to internet so network health check passes
+        getDevice().executeShellCommand("svc wifi enable");
+        getDevice().executeShellCommand("svc data enable");
+
+        // Wait for device available because emulator device may restart after turning
+        // on mobile data
+        getDevice().waitForDeviceAvailable();
+
+        // Verify rollback was not executed after health check deadline
+        runPhase("testNetworkPassedDoesNotRollback_Phase3");
+        InputStreamSource logcatStream = mReceiver.getLogcatData();
+        try {
+            List<String> watchdogEvents = getWatchdogLoggingEvents(logcatStream);
+            assertEquals(watchdogEventOccurred(watchdogEvents, null, null,
+                    REASON_EXPLICIT_HEALTH_CHECK, null), false);
+        } finally {
+            logcatStream.close();
+        }
+    }
+
+    /**
+     * Returns a list of all Watchdog logging events which have occurred.
+     */
+    private List<String> getWatchdogLoggingEvents(InputStreamSource inputStreamSource)
+            throws Exception {
+        List<String> watchdogEvents = new ArrayList<>();
+        InputStream inputStream = inputStreamSource.createInputStream();
+        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
+        String line;
+        while ((line = reader.readLine()) != null) {
+            if (line.contains("Watchdog event occurred")) {
+                watchdogEvents.add(line);
+            }
+        }
+        return watchdogEvents;
+    }
+
+    /**
+     * Returns whether a Watchdog event has occurred that matches the given criteria.
+     *
+     * Check the value of all non-null parameters against the list of Watchdog events that have
+     * occurred, and return {@code true} if an event exists which matches all criteria.
+     */
+    private boolean watchdogEventOccurred(List<String> loggingEvents,
+            String type, String logPackage,
+            String rollbackReason, String failedPackageName) throws Exception {
+        List<String> eventCriteria = new ArrayList<>();
+        if (type != null) {
+            eventCriteria.add("type: " + type);
+        }
+        if (logPackage != null) {
+            eventCriteria.add("logPackage: " + logPackage);
+        }
+        if (rollbackReason != null) {
+            eventCriteria.add("rollbackReason: " + rollbackReason);
+        }
+        if (failedPackageName != null) {
+            eventCriteria.add("failedPackageName: " + failedPackageName);
+        }
+        for (String loggingEvent: loggingEvents) {
+            boolean matchesCriteria = true;
+            for (String criterion: eventCriteria) {
+                if (!loggingEvent.contains(criterion)) {
+                    matchesCriteria = false;
+                }
+            }
+            if (matchesCriteria) {
+                return true;
+            }
+        }
+        return false;
     }
 }
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/NetworkStagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/NetworkStagedRollbackTest.java
index 04004d6..35bc65a 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/NetworkStagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/NetworkStagedRollbackTest.java
@@ -16,9 +16,182 @@
 
 package com.android.tests.rollback;
 
+import static com.android.cts.rollback.lib.RollbackInfoSubject.assertThat;
+import static com.android.cts.rollback.lib.RollbackUtils.getUniqueRollbackInfoForPackage;
+
+import android.Manifest;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.rollback.RollbackManager;
+import android.os.ParcelFileDescriptor;
+import android.provider.DeviceConfig;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.cts.install.lib.Install;
+import com.android.cts.install.lib.InstallUtils;
+import com.android.cts.install.lib.TestApp;
+import com.android.cts.rollback.lib.RollbackUtils;
+
+import libcore.io.IoUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.io.File;
+import java.util.concurrent.TimeUnit;
+
 @RunWith(JUnit4.class)
 public class NetworkStagedRollbackTest {
+    private static final String NETWORK_STACK_CONNECTOR_CLASS =
+            "android.net.INetworkStackConnector";
+    private static final String PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS =
+            "watchdog_request_timeout_millis";
+
+    private static final TestApp NETWORK_STACK = new TestApp("NetworkStack",
+            getNetworkStackPackageName(), -1, false, findNetworkStackApk());
+
+    private static File findNetworkStackApk() {
+        final File apk = new File("/system/priv-app/NetworkStack/NetworkStack.apk");
+        if (apk.isFile()) {
+            return apk;
+        }
+        return new File("/system/priv-app/NetworkStackNext/NetworkStackNext.apk");
+    }
+
+    /**
+     * Adopts common shell permissions needed for rollback tests.
+     */
+    @Before
+    public void adoptShellPermissions() {
+        InstallUtils.adoptShellPermissionIdentity(
+                Manifest.permission.INSTALL_PACKAGES,
+                Manifest.permission.DELETE_PACKAGES,
+                Manifest.permission.TEST_MANAGE_ROLLBACKS,
+                Manifest.permission.FORCE_STOP_PACKAGES,
+                Manifest.permission.WRITE_DEVICE_CONFIG);
+    }
+
+    /**
+     * Drops shell permissions needed for rollback tests.
+     */
+    @After
+    public void dropShellPermissions() {
+        InstallUtils.dropShellPermissionIdentity();
+    }
+
+    @Test
+    public void testNetworkFailedRollback_Phase1() throws Exception {
+        // Remove available rollbacks and uninstall NetworkStack on /data/
+        RollbackManager rm = RollbackUtils.getRollbackManager();
+        String networkStack = getNetworkStackPackageName();
+
+        rm.expireRollbackForPackage(networkStack);
+        uninstallNetworkStackPackage();
+
+        assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
+                networkStack)).isNull();
+
+        // Reduce health check deadline
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
+                PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS,
+                Integer.toString(120000), false);
+        // Simulate re-installation of new NetworkStack with rollbacks enabled
+        installNetworkStackPackage();
+    }
+
+    @Test
+    public void testNetworkFailedRollback_Phase2() throws Exception {
+        RollbackManager rm = RollbackUtils.getRollbackManager();
+        assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
+                getNetworkStackPackageName())).isNotNull();
+
+        // Sleep for < health check deadline
+        Thread.sleep(TimeUnit.SECONDS.toMillis(5));
+        // Verify rollback was not executed before health check deadline
+        assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
+                getNetworkStackPackageName())).isNull();
+    }
+
+    @Test
+    public void testNetworkFailedRollback_Phase3() throws Exception {
+        // Sleep for > health check deadline (120s to trigger rollback + 120s to reboot)
+        // The device is expected to reboot during sleeping. This device method will fail and
+        // the host will catch the assertion. If reboot doesn't happen, the host will fail the
+        // assertion.
+        Thread.sleep(TimeUnit.SECONDS.toMillis(240));
+    }
+
+    @Test
+    public void testNetworkFailedRollback_Phase4() throws Exception {
+        RollbackManager rm = RollbackUtils.getRollbackManager();
+        assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
+                getNetworkStackPackageName())).isNotNull();
+    }
+
+    private static String getNetworkStackPackageName() {
+        Intent intent = new Intent(NETWORK_STACK_CONNECTOR_CLASS);
+        ComponentName comp = intent.resolveSystemService(
+                InstrumentationRegistry.getInstrumentation().getContext().getPackageManager(), 0);
+        return comp.getPackageName();
+    }
+
+    private static void installNetworkStackPackage() throws Exception {
+        Install.single(NETWORK_STACK).setStaged().setEnableRollback()
+                .addInstallFlags(PackageManager.INSTALL_REPLACE_EXISTING).commit();
+    }
+
+    private static void uninstallNetworkStackPackage() {
+        // Uninstall the package as a privileged user so we won't fail due to permission.
+        runShellCommand("pm uninstall " + getNetworkStackPackageName());
+    }
+
+    @Test
+    public void testNetworkPassedDoesNotRollback_Phase1() throws Exception {
+        // Remove available rollbacks and uninstall NetworkStack on /data/
+        RollbackManager rm = RollbackUtils.getRollbackManager();
+        String networkStack = getNetworkStackPackageName();
+
+        rm.expireRollbackForPackage(networkStack);
+        uninstallNetworkStackPackage();
+
+        assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
+                networkStack)).isNull();
+
+        // Reduce health check deadline, here unlike the network failed case, we use
+        // a longer deadline because joining a network can take a much longer time for
+        // reasons external to the device than 'not joining'
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
+                PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS,
+                Integer.toString(300000), false);
+        // Simulate re-installation of new NetworkStack with rollbacks enabled
+        installNetworkStackPackage();
+    }
+
+    @Test
+    public void testNetworkPassedDoesNotRollback_Phase2() throws Exception {
+        RollbackManager rm = RollbackUtils.getRollbackManager();
+        assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
+                getNetworkStackPackageName())).isNotNull();
+    }
+
+    @Test
+    public void testNetworkPassedDoesNotRollback_Phase3() throws Exception {
+        // Sleep for > health check deadline. We expect no rollback should happen during sleeping.
+        // If the device reboots for rollback, this device test will fail as well as the host test.
+        Thread.sleep(TimeUnit.SECONDS.toMillis(310));
+        RollbackManager rm = RollbackUtils.getRollbackManager();
+        assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
+                getNetworkStackPackageName())).isNull();
+    }
+
+    private static void runShellCommand(String cmd) {
+        ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                .executeShellCommand(cmd);
+        IoUtils.closeQuietly(pfd);
+    }
 }
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index 0fdcbc5..70be83f 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -22,14 +22,11 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.Manifest;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.rollback.RollbackInfo;
 import android.content.rollback.RollbackManager;
-import android.os.ParcelFileDescriptor;
 import android.os.storage.StorageManager;
 import android.provider.DeviceConfig;
 
@@ -44,8 +41,6 @@
 import com.android.cts.rollback.lib.RollbackUtils;
 import com.android.internal.R;
 
-import libcore.io.IoUtils;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -65,24 +60,8 @@
  */
 @RunWith(JUnit4.class)
 public class StagedRollbackTest {
-
-    private static final String NETWORK_STACK_CONNECTOR_CLASS =
-            "android.net.INetworkStackConnector";
     private static final String PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT =
             "watchdog_trigger_failure_count";
-    private static final String PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS =
-            "watchdog_request_timeout_millis";
-
-    private static final TestApp NETWORK_STACK = new TestApp("NetworkStack",
-            getNetworkStackPackageName(), -1, false, findNetworkStackApk());
-
-    private static File findNetworkStackApk() {
-        final File apk = new File("/system/priv-app/NetworkStack/NetworkStack.apk");
-        if (apk.isFile()) {
-            return apk;
-        }
-        return new File("/system/priv-app/NetworkStackNext/NetworkStackNext.apk");
-    }
 
     /**
      * Adopts common shell permissions needed for rollback tests.
@@ -275,72 +254,6 @@
     }
 
     @Test
-    public void testNetworkFailedRollback_Phase1() throws Exception {
-        // Remove available rollbacks and uninstall NetworkStack on /data/
-        RollbackManager rm = RollbackUtils.getRollbackManager();
-        String networkStack = getNetworkStackPackageName();
-
-        rm.expireRollbackForPackage(networkStack);
-        uninstallNetworkStackPackage();
-
-        assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
-                        networkStack)).isNull();
-
-        // Reduce health check deadline
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
-                PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS,
-                Integer.toString(120000), false);
-        // Simulate re-installation of new NetworkStack with rollbacks enabled
-        installNetworkStackPackage();
-    }
-
-    @Test
-    public void testNetworkFailedRollback_Phase2() throws Exception {
-        RollbackManager rm = RollbackUtils.getRollbackManager();
-        assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
-                        getNetworkStackPackageName())).isNotNull();
-
-        // Sleep for < health check deadline
-        Thread.sleep(TimeUnit.SECONDS.toMillis(5));
-        // Verify rollback was not executed before health check deadline
-        assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
-                        getNetworkStackPackageName())).isNull();
-    }
-
-    @Test
-    public void testNetworkFailedRollback_Phase3() throws Exception {
-        // Sleep for > health check deadline (120s to trigger rollback + 120s to reboot)
-        // The device is expected to reboot during sleeping. This device method will fail and
-        // the host will catch the assertion. If reboot doesn't happen, the host will fail the
-        // assertion.
-        Thread.sleep(TimeUnit.SECONDS.toMillis(240));
-    }
-
-    @Test
-    public void testNetworkFailedRollback_Phase4() throws Exception {
-        RollbackManager rm = RollbackUtils.getRollbackManager();
-        assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
-                        getNetworkStackPackageName())).isNotNull();
-    }
-
-    private static String getNetworkStackPackageName() {
-        Intent intent = new Intent(NETWORK_STACK_CONNECTOR_CLASS);
-        ComponentName comp = intent.resolveSystemService(
-                InstrumentationRegistry.getInstrumentation().getContext().getPackageManager(), 0);
-        return comp.getPackageName();
-    }
-
-    private static void installNetworkStackPackage() throws Exception {
-        Install.single(NETWORK_STACK).setStaged().setEnableRollback()
-                .addInstallFlags(PackageManager.INSTALL_REPLACE_EXISTING).commit();
-    }
-
-    private static void uninstallNetworkStackPackage() {
-        // Uninstall the package as a privileged user so we won't fail due to permission.
-        runShellCommand("pm uninstall " + getNetworkStackPackageName());
-    }
-
-    @Test
     public void testPreviouslyAbandonedRollbacks_Phase1() throws Exception {
         Uninstall.packages(TestApp.A);
         Install.single(TestApp.A1).commit();
@@ -376,45 +289,6 @@
         Uninstall.packages(TestApp.A);
     }
 
-    @Test
-    public void testNetworkPassedDoesNotRollback_Phase1() throws Exception {
-        // Remove available rollbacks and uninstall NetworkStack on /data/
-        RollbackManager rm = RollbackUtils.getRollbackManager();
-        String networkStack = getNetworkStackPackageName();
-
-        rm.expireRollbackForPackage(networkStack);
-        uninstallNetworkStackPackage();
-
-        assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
-                        networkStack)).isNull();
-
-        // Reduce health check deadline, here unlike the network failed case, we use
-        // a longer deadline because joining a network can take a much longer time for
-        // reasons external to the device than 'not joining'
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
-                PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS,
-                Integer.toString(300000), false);
-        // Simulate re-installation of new NetworkStack with rollbacks enabled
-        installNetworkStackPackage();
-    }
-
-    @Test
-    public void testNetworkPassedDoesNotRollback_Phase2() throws Exception {
-        RollbackManager rm = RollbackUtils.getRollbackManager();
-        assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
-                        getNetworkStackPackageName())).isNotNull();
-    }
-
-    @Test
-    public void testNetworkPassedDoesNotRollback_Phase3() throws Exception {
-        // Sleep for > health check deadline. We expect no rollback should happen during sleeping.
-        // If the device reboots for rollback, this device test will fail as well as the host test.
-        Thread.sleep(TimeUnit.SECONDS.toMillis(310));
-        RollbackManager rm = RollbackUtils.getRollbackManager();
-        assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
-                        getNetworkStackPackageName())).isNull();
-    }
-
     private static String getModuleMetadataPackageName() {
         return InstrumentationRegistry.getInstrumentation().getContext()
                 .getResources().getString(R.string.config_defaultModuleMetadataProvider);
@@ -603,12 +477,6 @@
         InstallUtils.waitForSessionReady(committed.getCommittedSessionId());
     }
 
-    private static void runShellCommand(String cmd) {
-        ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation()
-                .executeShellCommand(cmd);
-        IoUtils.closeQuietly(pfd);
-    }
-
     @Test
     public void isCheckpointSupported() {
         Context context = InstrumentationRegistry.getInstrumentation().getContext();
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 032f182..c3fd962 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -228,77 +228,6 @@
     }
 
     /**
-     * Tests failed network health check triggers watchdog staged rollbacks.
-     */
-    @Test
-    public void testNetworkFailedRollback() throws Exception {
-        try {
-            // Disconnect internet so we can test network health triggered rollbacks
-            getDevice().executeShellCommand("svc wifi disable");
-            getDevice().executeShellCommand("svc data disable");
-
-            runPhase("testNetworkFailedRollback_Phase1");
-            // Reboot device to activate staged package
-            getDevice().reboot();
-
-            // Verify rollback was enabled
-            runPhase("testNetworkFailedRollback_Phase2");
-            assertThrows(AssertionError.class, () -> runPhase("testNetworkFailedRollback_Phase3"));
-
-            getDevice().waitForDeviceAvailable();
-            // Verify rollback was executed after health check deadline
-            runPhase("testNetworkFailedRollback_Phase4");
-            InputStreamSource logcatStream = mReceiver.getLogcatData();
-            try {
-                List<String> watchdogEvents = getWatchdogLoggingEvents(logcatStream);
-                assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_INITIATE, null,
-                                REASON_EXPLICIT_HEALTH_CHECK, null));
-                assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null,
-                        null, null));
-            } finally {
-                logcatStream.close();
-            }
-        } finally {
-            // Reconnect internet again so we won't break tests which assume internet available
-            getDevice().executeShellCommand("svc wifi enable");
-            getDevice().executeShellCommand("svc data enable");
-        }
-    }
-
-    /**
-     * Tests passed network health check does not trigger watchdog staged rollbacks.
-     */
-    @Test
-    public void testNetworkPassedDoesNotRollback() throws Exception {
-        runPhase("testNetworkPassedDoesNotRollback_Phase1");
-        // Reboot device to activate staged package
-        getDevice().reboot();
-
-        // Verify rollback was enabled
-        runPhase("testNetworkPassedDoesNotRollback_Phase2");
-
-        // Connect to internet so network health check passes
-        getDevice().executeShellCommand("svc wifi enable");
-        getDevice().executeShellCommand("svc data enable");
-
-        // Wait for device available because emulator device may restart after turning
-        // on mobile data
-        getDevice().waitForDeviceAvailable();
-
-        // Verify rollback was not executed after health check deadline
-        runPhase("testNetworkPassedDoesNotRollback_Phase3");
-        InputStreamSource logcatStream = mReceiver.getLogcatData();
-        try {
-            List<String> watchdogEvents = getWatchdogLoggingEvents(logcatStream);
-            assertEquals(watchdogEventOccurred(watchdogEvents, null, null,
-                    REASON_EXPLICIT_HEALTH_CHECK, null), false);
-        } finally {
-            logcatStream.close();
-        }
-
-    }
-
-    /**
      * Tests rolling back user data where there are multiple rollbacks for that package.
      */
     @Test
@@ -527,11 +456,6 @@
         }
     }
 
-    private String getNetworkStackPath() throws Exception {
-        // Find the NetworkStack path (can be NetworkStack.apk or NetworkStackNext.apk)
-        return getDevice().executeShellCommand("ls /system/priv-app/NetworkStack*/*.apk");
-    }
-
     private boolean isCheckpointSupported() throws Exception {
         try {
             runPhase("isCheckpointSupported");
diff --git a/tests/RollbackTest/TEST_MAPPING b/tests/RollbackTest/TEST_MAPPING
index fefde5b..0f4c460 100644
--- a/tests/RollbackTest/TEST_MAPPING
+++ b/tests/RollbackTest/TEST_MAPPING
@@ -7,6 +7,9 @@
       "name": "StagedRollbackTest"
     },
     {
+      "name": "NetworkStagedRollbackTest"
+    },
+    {
       "name": "MultiUserRollbackTest"
     }
   ]
diff --git a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
index 0628691..8eb5cfa 100644
--- a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
@@ -146,17 +146,14 @@
 
     @Test
     public void testConnectivityReportEquals() {
-        assertEquals(createSampleConnectivityReport(), createSampleConnectivityReport());
-        assertEquals(createDefaultConnectivityReport(), createDefaultConnectivityReport());
+        final ConnectivityReport defaultReport = createDefaultConnectivityReport();
+        final ConnectivityReport sampleReport = createSampleConnectivityReport();
+        assertEquals(sampleReport, createSampleConnectivityReport());
+        assertEquals(defaultReport, createDefaultConnectivityReport());
 
-        final LinkProperties linkProperties = new LinkProperties();
-        linkProperties.setInterfaceName(INTERFACE_NAME);
-
-        final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
-        networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
-
-        final PersistableBundle bundle = new PersistableBundle();
-        bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
+        final LinkProperties linkProperties = sampleReport.getLinkProperties();
+        final NetworkCapabilities networkCapabilities = sampleReport.getNetworkCapabilities();
+        final PersistableBundle bundle = sampleReport.getAdditionalInfo();
 
         assertNotEquals(
                 createDefaultConnectivityReport(),
@@ -206,39 +203,104 @@
     }
 
     private DataStallReport createSampleDataStallReport() {
+        final LinkProperties linkProperties = new LinkProperties();
+        linkProperties.setInterfaceName(INTERFACE_NAME);
+
         final PersistableBundle bundle = new PersistableBundle();
         bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
-        return new DataStallReport(new Network(NET_ID), TIMESTAMP, DETECTION_METHOD, bundle);
+
+        final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
+        networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
+
+        return new DataStallReport(
+                new Network(NET_ID),
+                TIMESTAMP,
+                DETECTION_METHOD,
+                linkProperties,
+                networkCapabilities,
+                bundle);
     }
 
     private DataStallReport createDefaultDataStallReport() {
-        return new DataStallReport(new Network(0), 0L, 0, PersistableBundle.EMPTY);
+        return new DataStallReport(
+                new Network(0),
+                0L,
+                0,
+                new LinkProperties(),
+                new NetworkCapabilities(),
+                PersistableBundle.EMPTY);
     }
 
     @Test
     public void testDataStallReportEquals() {
-        assertEquals(createSampleDataStallReport(), createSampleDataStallReport());
-        assertEquals(createDefaultDataStallReport(), createDefaultDataStallReport());
+        final DataStallReport defaultReport = createDefaultDataStallReport();
+        final DataStallReport sampleReport = createSampleDataStallReport();
+        assertEquals(sampleReport, createSampleDataStallReport());
+        assertEquals(defaultReport, createDefaultDataStallReport());
 
-        final PersistableBundle bundle = new PersistableBundle();
-        bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
+        final LinkProperties linkProperties = sampleReport.getLinkProperties();
+        final NetworkCapabilities networkCapabilities = sampleReport.getNetworkCapabilities();
+        final PersistableBundle bundle = sampleReport.getStallDetails();
 
         assertNotEquals(
-                createDefaultDataStallReport(),
-                new DataStallReport(new Network(NET_ID), 0L, 0, PersistableBundle.EMPTY));
+                defaultReport,
+                new DataStallReport(
+                        new Network(NET_ID),
+                        0L,
+                        0,
+                        new LinkProperties(),
+                        new NetworkCapabilities(),
+                        PersistableBundle.EMPTY));
         assertNotEquals(
-                createDefaultDataStallReport(),
-                new DataStallReport(new Network(0), TIMESTAMP, 0, PersistableBundle.EMPTY));
+                defaultReport,
+                new DataStallReport(
+                        new Network(0),
+                        TIMESTAMP,
+                        0,
+                        new LinkProperties(),
+                        new NetworkCapabilities(),
+                        PersistableBundle.EMPTY));
         assertNotEquals(
-                createDefaultDataStallReport(),
-                new DataStallReport(new Network(0), 0L, DETECTION_METHOD, PersistableBundle.EMPTY));
+                defaultReport,
+                new DataStallReport(
+                        new Network(0),
+                        0L,
+                        DETECTION_METHOD,
+                        new LinkProperties(),
+                        new NetworkCapabilities(),
+                        PersistableBundle.EMPTY));
         assertNotEquals(
-                createDefaultDataStallReport(), new DataStallReport(new Network(0), 0L, 0, bundle));
+                defaultReport,
+                new DataStallReport(
+                        new Network(0),
+                        0L,
+                        0,
+                        linkProperties,
+                        new NetworkCapabilities(),
+                        PersistableBundle.EMPTY));
+        assertNotEquals(
+                defaultReport,
+                new DataStallReport(
+                        new Network(0),
+                        0L,
+                        0,
+                        new LinkProperties(),
+                        networkCapabilities,
+                        PersistableBundle.EMPTY));
+        assertNotEquals(
+                defaultReport,
+                new DataStallReport(
+                        new Network(0),
+                        0L,
+                        0,
+                        new LinkProperties(),
+                        new NetworkCapabilities(),
+                        bundle));
     }
 
     @Test
     public void testDataStallReportParcelUnparcel() {
-        assertParcelSane(createSampleDataStallReport(), 4);
+        assertParcelSane(createSampleDataStallReport(), 6);
     }
 
     @Test
diff --git a/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt b/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
new file mode 100644
index 0000000..86c9116
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity
+
+import android.net.NetworkRequest
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import kotlin.test.assertEquals
+import kotlin.test.assertNull
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class NetworkRankerTest {
+    private val ranker = NetworkRanker()
+
+    private fun makeNai(satisfy: Boolean, score: Int) = mock(NetworkAgentInfo::class.java).also {
+        doReturn(satisfy).`when`(it).satisfies(any())
+        doReturn(score).`when`(it).currentScore
+    }
+
+    @Test
+    fun testGetBestNetwork() {
+        val scores = listOf(20, 50, 90, 60, 23, 68)
+        val nais = scores.map { makeNai(true, it) }
+        val bestNetwork = nais[2] // The one with the top score
+        val someRequest = mock(NetworkRequest::class.java)
+        assertEquals(bestNetwork, ranker.getBestNetwork(someRequest, nais))
+    }
+
+    @Test
+    fun testIgnoreNonSatisfying() {
+        val nais = listOf(makeNai(true, 20), makeNai(true, 50), makeNai(false, 90),
+                makeNai(false, 60), makeNai(true, 23), makeNai(false, 68))
+        val bestNetwork = nais[1] // Top score that's satisfying
+        val someRequest = mock(NetworkRequest::class.java)
+        assertEquals(bestNetwork, ranker.getBestNetwork(someRequest, nais))
+    }
+
+    @Test
+    fun testNoMatch() {
+        val nais = listOf(makeNai(false, 20), makeNai(false, 50), makeNai(false, 90))
+        val someRequest = mock(NetworkRequest::class.java)
+        assertNull(ranker.getBestNetwork(someRequest, nais))
+    }
+
+    @Test
+    fun testEmpty() {
+        val someRequest = mock(NetworkRequest::class.java)
+        assertNull(ranker.getBestNetwork(someRequest, emptyList()))
+    }
+
+    // Make sure the ranker is "stable" (as in stable sort), that is, it always returns the FIRST
+    // network satisfying the request if multiple of them have the same score.
+    @Test
+    fun testStable() {
+        val nais1 = listOf(makeNai(true, 30), makeNai(true, 30), makeNai(true, 30),
+                makeNai(true, 30), makeNai(true, 30), makeNai(true, 30))
+        val someRequest = mock(NetworkRequest::class.java)
+        assertEquals(nais1[0], ranker.getBestNetwork(someRequest, nais1))
+
+        val nais2 = listOf(makeNai(true, 30), makeNai(true, 50), makeNai(true, 20),
+                makeNai(true, 50), makeNai(true, 50), makeNai(true, 40))
+        assertEquals(nais2[1], ranker.getBestNetwork(someRequest, nais2))
+    }
+}
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 31aa249..e7a8203 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -1188,7 +1188,7 @@
     }
 
     // Fully qualify the activity name
-    ssize_t idx = name.find(".");
+    ssize_t idx = name.find('.');
     if (idx == 0) {
       name = package + name;
     } else if (idx < 0) {
@@ -2138,7 +2138,7 @@
     size_t pos = file_path.find("lib/");
     if (pos != std::string::npos) {
       file_path = file_path.substr(pos + 4);
-      pos = file_path.find("/");
+      pos = file_path.find('/');
       if (pos != std::string::npos) {
         file_path = file_path.substr(0, pos);
       }
diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp
index 10e504e..09ea03b 100644
--- a/tools/aapt2/java/ManifestClassGenerator.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator.cpp
@@ -39,7 +39,7 @@
   }
 
   // Normalize only the java identifier, leave the original value unchanged.
-  if (result.find("-") != std::string::npos) {
+  if (result.find('-') != std::string::npos) {
     result = JavaClassGenerator::TransformToFieldName(result);
   }
 
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index d4e024d..1c330e2 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -272,4 +272,8 @@
     boolean isScanThrottleEnabled();
 
     Map getAllMatchingPasspointProfilesForScanResults(in List<ScanResult> scanResult);
+
+    void setAutoWakeupEnabled(boolean enable);
+
+    boolean isAutoWakeupEnabled();
 }
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index a720236..dc5ac1e 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -951,7 +951,7 @@
      * Indicate whether the network is trusted or not. Networks are considered trusted
      * if the user explicitly allowed this network connection.
      * This bit can be used by suggestion network, see
-     * {@link WifiNetworkSuggestion.Builder#setUnTrusted(boolean)}
+     * {@link WifiNetworkSuggestion.Builder#setUntrusted(boolean)}
      * @hide
      */
     public boolean trusted;
@@ -2140,7 +2140,8 @@
     public boolean isEnterprise() {
         return (allowedKeyManagement.get(KeyMgmt.WPA_EAP)
                 || allowedKeyManagement.get(KeyMgmt.IEEE8021X)
-                || allowedKeyManagement.get(KeyMgmt.SUITE_B_192))
+                || allowedKeyManagement.get(KeyMgmt.SUITE_B_192)
+                || allowedKeyManagement.get(KeyMgmt.WAPI_CERT))
                 && enterpriseConfig != null
                 && enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE;
     }
@@ -2416,6 +2417,9 @@
             if (allowedKeyManagement.get(KeyMgmt.SUITE_B_192)) {
                 keyMgmt += KeyMgmt.strings[KeyMgmt.SUITE_B_192];
             }
+            if (allowedKeyManagement.get(KeyMgmt.WAPI_CERT)) {
+                keyMgmt += KeyMgmt.strings[KeyMgmt.WAPI_CERT];
+            }
 
             if (TextUtils.isEmpty(keyMgmt)) {
                 throw new IllegalStateException("Not an EAP network");
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index fb30910c..b6f4490 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -6163,4 +6163,50 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Enable/disable wifi auto wakeup feature.
+     *
+     * <p>
+     * The feature is described in
+     * <a href="Wi-Fi Turn on automatically">
+     * https://source.android.com/devices/tech/connect/wifi-infrastructure
+     * #turn_on_wi-fi_automatically
+     * </a>
+     *
+     * @param enable true to enable, false to disable.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    public void setAutoWakeupEnabled(boolean enable) {
+        try {
+            mService.setAutoWakeupEnabled(enable);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get the persisted Wi-Fi auto wakeup feature state. Defaults to false, unless changed by the
+     * user via Settings.
+     *
+     * <p>
+     * The feature is described in
+     * <a href="Wi-Fi Turn on automatically">
+     * https://source.android.com/devices/tech/connect/wifi-infrastructure
+     * #turn_on_wi-fi_automatically
+     * </a>
+     *
+     * @return true to indicate that wakeup feature is enabled, false to indicate that wakeup
+     * feature is disabled.
+     */
+    @RequiresPermission(ACCESS_WIFI_STATE)
+    public boolean isAutoWakeupEnabled() {
+        try {
+            return mService.isAutoWakeupEnabled();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/wifi/java/android/net/wifi/wificond/NativeScanResult.java b/wifi/java/android/net/wifi/wificond/NativeScanResult.java
index 7cc617d..bd99476 100644
--- a/wifi/java/android/net/wifi/wificond/NativeScanResult.java
+++ b/wifi/java/android/net/wifi/wificond/NativeScanResult.java
@@ -16,14 +16,21 @@
 
 package android.net.wifi.wificond;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.net.MacAddress;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -33,6 +40,8 @@
  */
 @SystemApi
 public final class NativeScanResult implements Parcelable {
+    private static final String TAG = "NativeScanResult";
+
     /** @hide */
     @VisibleForTesting
     public byte[] ssid;
@@ -53,7 +62,7 @@
     public long tsf;
     /** @hide */
     @VisibleForTesting
-    public int capability;
+    @BssCapabilityBits public int capability;
     /** @hide */
     @VisibleForTesting
     public boolean associated;
@@ -71,14 +80,17 @@
     }
 
     /**
-     * Returns raw bytes representing the MAC address (BSSID) of the AP represented by this scan
-     * result.
+     * Returns the MAC address (BSSID) of the AP represented by this scan result.
      *
-     * @return a byte array, possibly null or containing the incorrect number of bytes for a MAC
-     * address.
+     * @return a MacAddress or null on error.
      */
-    @NonNull public byte[] getBssid() {
-        return bssid;
+    @Nullable public MacAddress getBssid() {
+        try {
+            return MacAddress.fromBytes(bssid);
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "Illegal argument " + Arrays.toString(bssid), e);
+            return null;
+        }
     }
 
     /**
@@ -127,31 +139,103 @@
         return associated;
     }
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = {"BSS_CAPABILITY_"},
+            value = {BSS_CAPABILITY_ESS,
+                    BSS_CAPABILITY_IBSS,
+                    BSS_CAPABILITY_CF_POLLABLE,
+                    BSS_CAPABILITY_CF_POLL_REQUEST,
+                    BSS_CAPABILITY_PRIVACY,
+                    BSS_CAPABILITY_SHORT_PREAMBLE,
+                    BSS_CAPABILITY_PBCC,
+                    BSS_CAPABILITY_CHANNEL_AGILITY,
+                    BSS_CAPABILITY_SPECTRUM_MANAGEMENT,
+                    BSS_CAPABILITY_QOS,
+                    BSS_CAPABILITY_SHORT_SLOT_TIME,
+                    BSS_CAPABILITY_APSD,
+                    BSS_CAPABILITY_RADIO_MANAGEMENT,
+                    BSS_CAPABILITY_DSSS_OFDM,
+                    BSS_CAPABILITY_DELAYED_BLOCK_ACK,
+                    BSS_CAPABILITY_IMMEDIATE_BLOCK_ACK
+            })
+    public @interface BssCapabilityBits { }
+
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): ESS.
+     */
+    public static final int BSS_CAPABILITY_ESS = 0x1 << 0;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): IBSS.
+     */
+    public static final int BSS_CAPABILITY_IBSS = 0x1 << 1;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): CF Pollable.
+     */
+    public static final int BSS_CAPABILITY_CF_POLLABLE = 0x1 << 2;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): CF-Poll Request.
+     */
+    public static final int BSS_CAPABILITY_CF_POLL_REQUEST = 0x1 << 3;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): Privacy.
+     */
+    public static final int BSS_CAPABILITY_PRIVACY = 0x1 << 4;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): Short Preamble.
+     */
+    public static final int BSS_CAPABILITY_SHORT_PREAMBLE = 0x1 << 5;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): PBCC.
+     */
+    public static final int BSS_CAPABILITY_PBCC = 0x1 << 6;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): Channel Agility.
+     */
+    public static final int BSS_CAPABILITY_CHANNEL_AGILITY = 0x1 << 7;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): Spectrum Management.
+     */
+    public static final int BSS_CAPABILITY_SPECTRUM_MANAGEMENT = 0x1 << 8;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): QoS.
+     */
+    public static final int BSS_CAPABILITY_QOS = 0x1 << 9;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): Short Slot Time.
+     */
+    public static final int BSS_CAPABILITY_SHORT_SLOT_TIME = 0x1 << 10;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): APSD.
+     */
+    public static final int BSS_CAPABILITY_APSD = 0x1 << 11;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): Radio Management.
+     */
+    public static final int BSS_CAPABILITY_RADIO_MANAGEMENT = 0x1 << 12;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): DSSS-OFDM.
+     */
+    public static final int BSS_CAPABILITY_DSSS_OFDM = 0x1 << 13;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): Delayed Block Ack.
+     */
+    public static final int BSS_CAPABILITY_DELAYED_BLOCK_ACK = 0x1 << 14;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): Immediate Block Ack.
+     */
+    public static final int BSS_CAPABILITY_IMMEDIATE_BLOCK_ACK = 0x1 << 15;
+
     /**
      *  Returns the capabilities of the AP repseresented by this scan result as advertised in the
      *  received probe response or beacon.
      *
-     *  This is a bit mask describing the capabilities of a BSS. See IEEE Std 802.11: 9.4.1.4:
-     *    Bit 0 - ESS
-     *    Bit 1 - IBSS
-     *    Bit 2 - CF Pollable
-     *    Bit 3 - CF-Poll Request
-     *    Bit 4 - Privacy
-     *    Bit 5 - Short Preamble
-     *    Bit 6 - PBCC
-     *    Bit 7 - Channel Agility
-     *    Bit 8 - Spectrum Management
-     *    Bit 9 - QoS
-     *    Bit 10 - Short Slot Time
-     *    Bit 11 - APSD
-     *    Bit 12 - Radio Measurement
-     *    Bit 13 - DSSS-OFDM
-     *    Bit 14 - Delayed Block Ack
-     *    Bit 15 - Immediate Block Ack
+     *  This is a bit mask describing the capabilities of a BSS. See IEEE Std 802.11: 9.4.1.4: one
+     *  of the {@code BSS_CAPABILITY_*} flags.
      *
      * @return a bit mask of capabilities.
      */
-    @NonNull public int getCapabilities() {
+    @BssCapabilityBits public int getCapabilities() {
         return capability;
     }
 
diff --git a/wifi/java/android/net/wifi/wificond/NativeWifiClient.java b/wifi/java/android/net/wifi/wificond/NativeWifiClient.java
index 916c115..9ad2a27 100644
--- a/wifi/java/android/net/wifi/wificond/NativeWifiClient.java
+++ b/wifi/java/android/net/wifi/wificond/NativeWifiClient.java
@@ -17,11 +17,13 @@
 package android.net.wifi.wificond;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.net.MacAddress;
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * Structure providing information about clients (STAs) associated with a SoftAp.
@@ -30,16 +32,21 @@
  */
 @SystemApi
 public final class NativeWifiClient implements Parcelable {
+    private final MacAddress mMacAddress;
+
     /**
-     * The raw bytes of the MAC address of the client (STA) represented by this object.
+     * The MAC address of the client (STA) represented by this object. The MAC address may be null
+     * in case of an error.
      */
-    @NonNull public final byte[] macAddress;
+    @Nullable public MacAddress getMacAddress() {
+        return mMacAddress;
+    }
 
     /**
      * Construct a native Wi-Fi client.
      */
-    public NativeWifiClient(@NonNull byte[] macAddress) {
-        this.macAddress = macAddress;
+    public NativeWifiClient(@Nullable MacAddress macAddress) {
+        this.mMacAddress = macAddress;
     }
 
     /** override comparator */
@@ -50,13 +57,13 @@
             return false;
         }
         NativeWifiClient other = (NativeWifiClient) rhs;
-        return Arrays.equals(macAddress, other.macAddress);
+        return Objects.equals(mMacAddress, other.mMacAddress);
     }
 
     /** override hash code */
     @Override
     public int hashCode() {
-        return Arrays.hashCode(macAddress);
+        return mMacAddress.hashCode();
     }
 
     /** implement Parcelable interface */
@@ -71,7 +78,7 @@
      */
     @Override
     public void writeToParcel(@NonNull Parcel out, int flags) {
-        out.writeByteArray(macAddress);
+        out.writeByteArray(mMacAddress.toByteArray());
     }
 
     /** implement Parcelable interface */
@@ -79,9 +86,11 @@
             new Parcelable.Creator<NativeWifiClient>() {
                 @Override
                 public NativeWifiClient createFromParcel(Parcel in) {
-                    byte[] macAddress = in.createByteArray();
-                    if (macAddress == null) {
-                        macAddress = new byte[0];
+                    MacAddress macAddress;
+                    try {
+                        macAddress = MacAddress.fromBytes(in.createByteArray());
+                    } catch (IllegalArgumentException e) {
+                        macAddress = null;
                     }
                     return new NativeWifiClient(macAddress);
                 }
diff --git a/wifi/java/android/net/wifi/wificond/WifiCondManager.java b/wifi/java/android/net/wifi/wificond/WifiCondManager.java
index 7a31a5a..61f18e0 100644
--- a/wifi/java/android/net/wifi/wificond/WifiCondManager.java
+++ b/wifi/java/android/net/wifi/wificond/WifiCondManager.java
@@ -41,19 +41,16 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.stream.Collectors;
 
 /**
- * This class encapsulates the interface the wificond (Wi-Fi Conductor) daemon presents to the
- * Wi-Fi framework. The interface is only for use by the Wi-Fi framework and access is protected
- * by SELinux permissions: only the system server and wpa_supplicant can use WifiCondManager.
+ * This class encapsulates the interface the wificond daemon presents to the Wi-Fi framework. The
+ * interface is only for use by the Wi-Fi framework and access is protected by SELinux permissions.
  *
  * @hide
  */
@@ -371,7 +368,7 @@
         public void onConnectedClientsChanged(NativeWifiClient client, boolean isConnected) {
             if (mVerboseLoggingEnabled) {
                 Log.d(TAG, "onConnectedClientsChanged called with "
-                        + client.macAddress + " isConnected: " + isConnected);
+                        + client.getMacAddress() + " isConnected: " + isConnected);
             }
 
             Binder.clearCallingIdentity();
@@ -1046,13 +1043,13 @@
      * WifiScanner.WIFI_BAND_5_GHZ
      * WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY
      * WifiScanner.WIFI_BAND_6_GHZ
-     * @return frequencies List of valid frequencies (MHz), or an empty list for error.
+     * @return frequencies vector of valid frequencies (MHz), or an empty array for error.
      * @throws IllegalArgumentException if band is not recognized.
      */
-    public @NonNull List<Integer> getChannelsMhzForBand(@WifiAnnotations.WifiBandBasic int band) {
+    public @NonNull int[] getChannelsMhzForBand(@WifiAnnotations.WifiBandBasic int band) {
         if (mWificond == null) {
             Log.e(TAG, "No valid wificond scanner interface handler");
-            return Collections.emptyList();
+            return new int[0];
         }
         int[] result = null;
         try {
@@ -1076,9 +1073,9 @@
             Log.e(TAG, "Failed to request getChannelsForBand due to remote exception");
         }
         if (result == null) {
-            return Collections.emptyList();
+            result = new int[0];
         }
-        return Arrays.stream(result).boxed().collect(Collectors.toList());
+        return result;
     }
 
     /** Helper function to look up the interface handle using name */
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 847040c..853212a 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -2391,4 +2391,14 @@
         assertFalse(mWifiManager.isScanThrottleEnabled());
         verify(mWifiService).isScanThrottleEnabled();
     }
+
+    @Test
+    public void testAutoWakeup() throws Exception {
+        mWifiManager.setAutoWakeupEnabled(true);
+        verify(mWifiService).setAutoWakeupEnabled(true);
+
+        when(mWifiService.isAutoWakeupEnabled()).thenReturn(false);
+        assertFalse(mWifiManager.isAutoWakeupEnabled());
+        verify(mWifiService).isAutoWakeupEnabled();
+    }
 }
diff --git a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java b/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
index 32105be..b745a34 100644
--- a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
@@ -38,6 +38,7 @@
 import android.app.AlarmManager;
 import android.app.test.TestAlarmManager;
 import android.content.Context;
+import android.net.MacAddress;
 import android.net.wifi.ScanResult;
 import android.net.wifi.SoftApInfo;
 import android.net.wifi.WifiConfiguration;
@@ -65,8 +66,6 @@
 import java.nio.charset.CharsetEncoder;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -121,7 +120,8 @@
     private static final String TEST_QUOTED_SSID_2 = "\"testSsid2\"";
     private static final int[] TEST_FREQUENCIES_1 = {};
     private static final int[] TEST_FREQUENCIES_2 = {2500, 5124};
-    private static final byte[] TEST_RAW_MAC_BYTES = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
+    private static final MacAddress TEST_RAW_MAC_BYTES = MacAddress.fromBytes(
+            new byte[]{0x00, 0x01, 0x02, 0x03, 0x04, 0x05});
 
     private static final List<byte[]> SCAN_HIDDEN_NETWORK_SSID_LIST =
             new ArrayList<byte[]>() {{
@@ -742,43 +742,11 @@
         verify(deathHandler).run();
 
         // The handles should be cleared after death.
-        assertEquals(0, mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_5_GHZ).size());
+        assertEquals(0, mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_5_GHZ).length);
         verify(mWificond, never()).getAvailable5gNonDFSChannels();
     }
 
     /**
-     * Verify primitive array to list translation of channel API.
-     */
-    @Test
-    public void testGetChannels() throws Exception {
-        int[] resultsEmpty = new int[0];
-        int[] resultsSingle = new int[]{100};
-        int[] resultsMore = new int[]{100, 200};
-
-        List<Integer> emptyList = Collections.emptyList();
-        List<Integer> singleList = Arrays.asList(100);
-        List<Integer> moreList = Arrays.asList(100, 200);
-
-        when(mWificond.getAvailable2gChannels()).thenReturn(null);
-        assertEquals(mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_24_GHZ),
-                emptyList);
-        assertEquals(mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_5_GHZ),
-                emptyList);
-
-        when(mWificond.getAvailable2gChannels()).thenReturn(resultsEmpty);
-        assertEquals(mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_24_GHZ),
-                emptyList);
-
-        when(mWificond.getAvailable2gChannels()).thenReturn(resultsSingle);
-        assertEquals(mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_24_GHZ),
-                singleList);
-
-        when(mWificond.getAvailable2gChannels()).thenReturn(resultsMore);
-        assertEquals(mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_24_GHZ),
-                moreList);
-    }
-
-    /**
      * sendMgmtFrame() should fail if a null callback is passed in.
      */
     @Test