Merge "Add Content PiP support with launch-into-pip API"
diff --git a/Android.bp b/Android.bp
index ee5db70..1c4f10e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -110,7 +110,7 @@
 
         // AIDL sources from external directories
         ":android.hardware.graphics.common-V3-java-source",
-        ":android.hardware.security.keymint-V1-java-source",
+        ":android.hardware.security.keymint-V2-java-source",
         ":android.hardware.security.secureclock-V1-java-source",
         ":android.hardware.tv.tuner-V1-java-source",
         ":android.security.apc-java-source",
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 273f59c..5d76b08 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2507,9 +2507,10 @@
 package android.app.usage {
 
   public final class BroadcastResponseStats implements android.os.Parcelable {
-    ctor public BroadcastResponseStats(@NonNull String);
+    ctor public BroadcastResponseStats(@NonNull String, @IntRange(from=1) long);
     method public int describeContents();
     method @IntRange(from=0) public int getBroadcastsDispatchedCount();
+    method @IntRange(from=1) public long getId();
     method @IntRange(from=0) public int getNotificationsCancelledCount();
     method @IntRange(from=0) public int getNotificationsPostedCount();
     method @IntRange(from=0) public int getNotificationsUpdatedCount();
@@ -2566,13 +2567,13 @@
   }
 
   public final class UsageStatsManager {
-    method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void clearBroadcastResponseStats(@NonNull String, @IntRange(from=1) long);
+    method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void clearBroadcastResponseStats(@Nullable String, @IntRange(from=0) long);
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getAppStandbyBucket(String);
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public java.util.Map<java.lang.String,java.lang.Integer> getAppStandbyBuckets();
     method @RequiresPermission(allOf={android.Manifest.permission.INTERACT_ACROSS_USERS, android.Manifest.permission.PACKAGE_USAGE_STATS}) public long getLastTimeAnyComponentUsed(@NonNull String);
     method public int getUsageSource();
     method @RequiresPermission(android.Manifest.permission.BIND_CARRIER_SERVICES) public void onCarrierPrivilegedAppsChanged();
-    method @NonNull @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public android.app.usage.BroadcastResponseStats queryBroadcastResponseStats(@NonNull String, @IntRange(from=1) long);
+    method @NonNull @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public java.util.List<android.app.usage.BroadcastResponseStats> queryBroadcastResponseStats(@Nullable String, @IntRange(from=0) long);
     method @RequiresPermission(allOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.OBSERVE_APP_USAGE}) public void registerAppUsageLimitObserver(int, @NonNull String[], @NonNull java.time.Duration, @NonNull java.time.Duration, @Nullable android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerAppUsageObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, @NonNull android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerUsageSessionObserver(int, @NonNull String[], @NonNull java.time.Duration, @NonNull java.time.Duration, @NonNull android.app.PendingIntent, @Nullable android.app.PendingIntent);
@@ -6075,7 +6076,7 @@
     method public boolean isAudioServerRunning();
     method public boolean isHdmiSystemAudioSupported();
     method @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public boolean isPstnCallAudioInterceptable();
-    method public static boolean isUltrasoundSupported();
+    method @RequiresPermission(android.Manifest.permission.ACCESS_ULTRASOUND) public boolean isUltrasoundSupported();
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void muteAwaitConnection(@NonNull int[], @NonNull android.media.AudioDeviceAttributes, long, @NonNull java.util.concurrent.TimeUnit) throws java.lang.IllegalStateException;
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void registerMuteAwaitConnectionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.MuteAwaitConnectionCallback);
diff --git a/core/java/android/app/usage/BroadcastResponseStats.java b/core/java/android/app/usage/BroadcastResponseStats.java
index 5acc3dda..e1d37e1 100644
--- a/core/java/android/app/usage/BroadcastResponseStats.java
+++ b/core/java/android/app/usage/BroadcastResponseStats.java
@@ -23,6 +23,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Objects;
+
 /**
  * Class containing a collection of stats related to response events started from an app
  * after receiving a broadcast.
@@ -32,17 +34,30 @@
 @SystemApi
 public final class BroadcastResponseStats implements Parcelable {
     private final String mPackageName;
+    private final long mId;
     private int mBroadcastsDispatchedCount;
     private int mNotificationsPostedCount;
     private int mNotificationsUpdatedCount;
     private int mNotificationsCancelledCount;
 
-    public BroadcastResponseStats(@NonNull String packageName) {
+    /**
+     * Creates a new {@link BroadcastResponseStats} object that contain the stats for broadcasts
+     * with {@code id} (specified using
+     * {@link BroadcastOptions#recordResponseEventWhileInBackground(long)} by the sender) that
+     * were sent to {@code packageName}.
+     *
+     * @param packageName the name of the package that broadcasts were sent to.
+     * @param id the ID specified by the sender using
+     *           {@link BroadcastOptions#recordResponseEventWhileInBackground(long)}.
+     */
+    public BroadcastResponseStats(@NonNull String packageName, @IntRange(from = 1) long id) {
         mPackageName = packageName;
+        mId = id;
     }
 
     private BroadcastResponseStats(@NonNull Parcel in) {
         mPackageName = in.readString8();
+        mId = in.readLong();
         mBroadcastsDispatchedCount = in.readInt();
         mNotificationsPostedCount = in.readInt();
         mNotificationsUpdatedCount = in.readInt();
@@ -58,6 +73,14 @@
     }
 
     /**
+     * @return the ID of the broadcasts that the stats in this object correspond to.
+     */
+    @IntRange(from = 1)
+    public long getId() {
+        return mId;
+    }
+
+    /**
      * Returns the total number of broadcasts that were dispatched to the app by the caller.
      *
      * <b> Note that the returned count will only include the broadcasts that the caller explicitly
@@ -148,9 +171,35 @@
     }
 
     @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || !(obj instanceof BroadcastResponseStats)) {
+            return false;
+        }
+        final BroadcastResponseStats other = (BroadcastResponseStats) obj;
+        return this.mBroadcastsDispatchedCount == other.mBroadcastsDispatchedCount
+                && this.mNotificationsPostedCount == other.mNotificationsPostedCount
+                && this.mNotificationsUpdatedCount == other.mNotificationsUpdatedCount
+                && this.mNotificationsCancelledCount == other.mNotificationsCancelledCount
+                && this.mId == other.mId
+                && this.mPackageName.equals(other.mPackageName);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPackageName, mId, mBroadcastsDispatchedCount,
+                mNotificationsPostedCount, mNotificationsUpdatedCount,
+                mNotificationsCancelledCount);
+    }
+
+    @Override
     public @NonNull String toString() {
         return "stats {"
-                + "broadcastsSent=" + mBroadcastsDispatchedCount
+                + "package=" + mPackageName
+                + ",id=" + mId
+                + ",broadcastsSent=" + mBroadcastsDispatchedCount
                 + ",notificationsPosted=" + mNotificationsPostedCount
                 + ",notificationsUpdated=" + mNotificationsUpdatedCount
                 + ",notificationsCancelled=" + mNotificationsCancelledCount
@@ -165,6 +214,7 @@
     @Override
     public void writeToParcel(@NonNull Parcel dest, @WriteFlags int flags) {
         dest.writeString8(mPackageName);
+        dest.writeLong(mId);
         dest.writeInt(mBroadcastsDispatchedCount);
         dest.writeInt(mNotificationsPostedCount);
         dest.writeInt(mNotificationsUpdatedCount);
diff --git a/core/java/android/app/usage/BroadcastResponseStatsList.aidl b/core/java/android/app/usage/BroadcastResponseStatsList.aidl
new file mode 100644
index 0000000..7380359
--- /dev/null
+++ b/core/java/android/app/usage/BroadcastResponseStatsList.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.usage;
+
+/** {@hide} */
+parcelable BroadcastResponseStatsList;
\ No newline at end of file
diff --git a/core/java/android/app/usage/BroadcastResponseStatsList.java b/core/java/android/app/usage/BroadcastResponseStatsList.java
new file mode 100644
index 0000000..4d2ff286
--- /dev/null
+++ b/core/java/android/app/usage/BroadcastResponseStatsList.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.usage;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** @hide */
+public final class BroadcastResponseStatsList implements Parcelable {
+    private List<BroadcastResponseStats> mBroadcastResponseStats;
+
+    public BroadcastResponseStatsList(
+            @NonNull List<BroadcastResponseStats> broadcastResponseStats) {
+        mBroadcastResponseStats = broadcastResponseStats;
+    }
+
+    private BroadcastResponseStatsList(@NonNull Parcel in) {
+        mBroadcastResponseStats = new ArrayList<>();
+        final byte[] bytes = in.readBlob();
+        final Parcel data = Parcel.obtain();
+        try {
+            data.unmarshall(bytes, 0, bytes.length);
+            data.setDataPosition(0);
+            data.readTypedList(mBroadcastResponseStats, BroadcastResponseStats.CREATOR);
+        } finally {
+            data.recycle();
+        }
+    }
+
+    @NonNull
+    public List<BroadcastResponseStats> getList() {
+        return mBroadcastResponseStats == null ? Collections.emptyList() : mBroadcastResponseStats;
+    }
+
+    @Override
+    public @ContentsFlags int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, @WriteFlags int flags) {
+        final Parcel data = Parcel.obtain();
+        try {
+            data.writeTypedList(mBroadcastResponseStats);
+            dest.writeBlob(data.marshall());
+        } finally {
+            data.recycle();
+        }
+    }
+
+    public static final @NonNull Creator<BroadcastResponseStatsList> CREATOR =
+            new Creator<BroadcastResponseStatsList>() {
+                @Override
+                public @NonNull BroadcastResponseStatsList createFromParcel(
+                        @NonNull Parcel source) {
+                    return new BroadcastResponseStatsList(source);
+                }
+
+                @Override
+                public @NonNull BroadcastResponseStatsList[] newArray(int size) {
+                    return new BroadcastResponseStatsList[size];
+                }
+            };
+}
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 6f8fea1..a430714 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -18,6 +18,7 @@
 
 import android.app.PendingIntent;
 import android.app.usage.BroadcastResponseStats;
+import android.app.usage.BroadcastResponseStatsList;
 import android.app.usage.UsageEvents;
 import android.content.pm.ParceledListSlice;
 
@@ -73,9 +74,11 @@
     void forceUsageSourceSettingRead();
     long getLastTimeAnyComponentUsed(String packageName, String callingPackage);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)")
-    BroadcastResponseStats queryBroadcastResponseStats(
+    BroadcastResponseStatsList queryBroadcastResponseStats(
             String packageName, long id, String callingPackage, int userId);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)")
     void clearBroadcastResponseStats(String packageName, long id, String callingPackage,
             int userId);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)")
+    void clearBroadcastEvents(String callingPackage, int userId);
 }
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index b81c62d..d7152b3 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -1397,29 +1397,46 @@
      * Returns the broadcast response stats since the last boot corresponding to
      * {@code packageName} and {@code id}.
      *
-     * <p>Broadcast response stats will include the aggregated data of what actions an app took upon
-     * receiving a broadcast. This data will consider the broadcasts that the caller sent to
+     * <p> Broadcast response stats will include the aggregated data of what actions an app took
+     * upon receiving a broadcast. This data will consider the broadcasts that the caller sent to
      * {@code packageName} and explicitly requested to record the response events using
      * {@link BroadcastOptions#recordResponseEventWhileInBackground(long)}.
      *
-     * @param packageName The name of the package that the caller wants to query for.
-     * @param id The ID corresponding to the broadcasts that the caller wants to query for. This is
-     *           the ID the caller specifies when requesting a broadcast response event to be
-     *           recorded using {@link BroadcastOptions#recordResponseEventWhileInBackground(long)}.
+     * <p> The returned list could one or more {@link BroadcastResponseStats} objects or be empty
+     * depending on the {@code packageName} and {@code id} and whether there is any data
+     * corresponding to these. If the {@code packageName} is not {@code null} and {@code id} is
+     * {@code > 0}, then the returned list would contain at most one {@link BroadcastResponseStats}
+     * object. Otherwise, the returned list could contain more than one
+     * {@link BroadcastResponseStats} object in no particular order.
      *
-     * @return the broadcast response stats corresponding to {@code packageName} and {@code id}.
+     * <p> Note: It is possible that same {@code id} was used for broadcasts sent to different
+     * packages. So, callers can query the data corresponding to
+     * all broadcasts with a particular {@code id} by passing {@code packageName} as {@code null}.
      *
+     * @param packageName The name of the package that the caller wants to query for
+     *                    or {@code null} to indicate that data corresponding to all packages
+     *                    should be returned.
+     * @param id The ID corresponding to the broadcasts that the caller wants to query for, or
+     *           {@code 0} to indicate that data corresponding to all IDs should be returned.
+     *           This is the ID the caller specifies when requesting a broadcast response event
+     *           to be recorded using
+     *           {@link BroadcastOptions#recordResponseEventWhileInBackground(long)}.
+     *
+     * @return the list of broadcast response stats corresponding to {@code packageName}
+     *         and {@code id}.
+     *
+     * @see #clearBroadcastResponseStats(String, long)
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
     @UserHandleAware
     @NonNull
-    public BroadcastResponseStats queryBroadcastResponseStats(
-            @NonNull String packageName, @IntRange(from = 1) long id) {
+    public List<BroadcastResponseStats> queryBroadcastResponseStats(
+            @Nullable String packageName, @IntRange(from = 0) long id) {
         try {
             return mService.queryBroadcastResponseStats(packageName, id,
-                    mContext.getOpPackageName(), mContext.getUserId());
+                    mContext.getOpPackageName(), mContext.getUserId()).getList();
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -1428,12 +1445,15 @@
     /**
      * Clears the broadcast response stats corresponding to {@code packageName} and {@code id}.
      *
-     * When a caller uses this API, stats related to the events occurring till that point will be
-     * cleared and subsequent calls to {@link #queryBroadcastResponseStats(String, long)} will
+     * <p> When a caller uses this API, stats related to the events occurring till that point will
+     * be cleared and subsequent calls to {@link #queryBroadcastResponseStats(String, long)} will
      * return stats related to events occurring after this.
      *
-     * @param packageName The name of the package that the caller wants to clear the data for.
-     * @param id The ID corresponding to the broadcasts that the caller wants to clear the data for.
+     * @param packageName The name of the package that the caller wants to clear the data for or
+     *                    {@code null} to indicate that data corresponding to all packages should
+     *                    be cleared.
+     * @param id The ID corresponding to the broadcasts that the caller wants to clear the data
+     *           for, or {code 0} to indicate that data corresponding to all IDs should be deleted.
      *           This is the ID the caller specifies when requesting a broadcast response event
      *           to be recorded using
      *           {@link BroadcastOptions#recordResponseEventWhileInBackground(long)}.
@@ -1444,8 +1464,8 @@
     @SystemApi
     @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
     @UserHandleAware
-    public void clearBroadcastResponseStats(@NonNull String packageName,
-            @IntRange(from = 1) long id) {
+    public void clearBroadcastResponseStats(@Nullable String packageName,
+            @IntRange(from = 0) long id) {
         try {
             mService.clearBroadcastResponseStats(packageName, id,
                     mContext.getOpPackageName(), mContext.getUserId());
@@ -1453,4 +1473,19 @@
             throw re.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Clears the broadcast events that were sent by the caller uid.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
+    @UserHandleAware
+    public void clearBroadcastEvents() {
+        try {
+            mService.clearBroadcastEvents(mContext.getOpPackageName(), mContext.getUserId());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 179f6ee..4dc1fca 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -11785,7 +11785,10 @@
      * user and that ideally it should not be covered. Setting this is only appropriate for UI
      * where the user would likely take action to uncover it.
      * <p>
-     * The system will try to respect this, but when not possible will ignore it.
+     * The system will try to respect this preference, but when not possible will ignore it.
+     * <p>
+     * Note: while this is set to {@code true}, the system will ignore the {@code Rect}s provided
+     * through {@link #setPreferKeepClearRects} (but not clear them).
      * <p>
      * @see #setPreferKeepClearRects
      * @see #isPreferKeepClear
@@ -11817,11 +11820,11 @@
      * user and that ideally they should not be covered. Setting this is only appropriate for UI
      * where the user would likely take action to uncover it.
      * <p>
-     * If the whole view is preferred to be clear ({@link #isPreferKeepClear}), the rects set here
-     * will be ignored.
-     * <p>
      * The system will try to respect this preference, but when not possible will ignore it.
      * <p>
+     * Note: While {@link #isPreferKeepClear} is {@code true}, the {@code Rect}s set here are
+     * ignored.
+     * <p>
      * @see #setPreferKeepClear
      * @see #getPreferKeepClearRects
      */
diff --git a/core/java/com/android/internal/infra/AndroidFuture.java b/core/java/com/android/internal/infra/AndroidFuture.java
index 0443ad0..0835824 100644
--- a/core/java/com/android/internal/infra/AndroidFuture.java
+++ b/core/java/com/android/internal/infra/AndroidFuture.java
@@ -455,7 +455,14 @@
             if (mSourceU != null) {
                 // T done
                 mResultT = (T) res;
-                mSourceU.whenComplete(this);
+
+                // Subscribe to the second job completion.
+                mSourceU.whenComplete((r, e) -> {
+                    // Mark the first job completion by setting mSourceU to null, so that next time
+                    // the execution flow goes to the else case below.
+                    mSourceU = null;
+                    accept(r, e);
+                });
             } else {
                 // U done
                 try {
diff --git a/core/java/com/android/internal/util/PerfettoTrigger.java b/core/java/com/android/internal/util/PerfettoTrigger.java
index c758504..f3af528 100644
--- a/core/java/com/android/internal/util/PerfettoTrigger.java
+++ b/core/java/com/android/internal/util/PerfettoTrigger.java
@@ -18,6 +18,7 @@
 
 import android.os.SystemClock;
 import android.util.Log;
+import android.util.SparseLongArray;
 
 import java.io.IOException;
 
@@ -28,8 +29,9 @@
 public class PerfettoTrigger {
     private static final String TAG = "PerfettoTrigger";
     private static final String TRIGGER_COMMAND = "/system/bin/trigger_perfetto";
-    private static final long THROTTLE_MILLIS = 60000;
-    private static volatile long sLastTriggerTime = -THROTTLE_MILLIS;
+    private static final long THROTTLE_MILLIS = 300000;
+    private static final SparseLongArray sLastInvocationPerTrigger = new SparseLongArray(100);
+    private static final Object sLock = new Object();
 
     /**
      * @param triggerName The name of the trigger. Must match the value defined in the AOT
@@ -38,18 +40,23 @@
     public static void trigger(String triggerName) {
         // Trace triggering has a non-negligible cost (fork+exec).
         // To mitigate potential excessive triggering by the API client we ignore calls that happen
-        // too quickl after the most recent trigger.
-        long sinceLastTrigger = SystemClock.elapsedRealtime() - sLastTriggerTime;
-        if (sinceLastTrigger < THROTTLE_MILLIS) {
-            Log.v(TAG, "Not triggering " + triggerName + " - not enough time since last trigger");
-            return;
+        // too quickly after the most recent trigger.
+        synchronized (sLock) {
+            long lastTrigger = sLastInvocationPerTrigger.get(triggerName.hashCode());
+            long sinceLastTrigger = SystemClock.elapsedRealtime() - lastTrigger;
+            if (sinceLastTrigger < THROTTLE_MILLIS) {
+                Log.v(TAG, "Not triggering " + triggerName
+                        + " - not enough time since last trigger");
+                return;
+            }
+
+            sLastInvocationPerTrigger.put(triggerName.hashCode(), SystemClock.elapsedRealtime());
         }
 
         try {
             ProcessBuilder pb = new ProcessBuilder(TRIGGER_COMMAND, triggerName);
             Log.v(TAG, "Triggering " + String.join(" ", pb.command()));
             pb.start();
-            sLastTriggerTime = SystemClock.elapsedRealtime();
         } catch (IOException e) {
             Log.w(TAG, "Failed to trigger " + triggerName, e);
         }
diff --git a/core/java/com/android/internal/util/UserIcons.java b/core/java/com/android/internal/util/UserIcons.java
index 17b84ff..d9c1e57 100644
--- a/core/java/com/android/internal/util/UserIcons.java
+++ b/core/java/com/android/internal/util/UserIcons.java
@@ -46,11 +46,21 @@
      * Converts a given drawable to a bitmap.
      */
     public static Bitmap convertToBitmap(Drawable icon) {
+        return convertToBitmapAtSize(icon, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
+    }
+
+    /**
+     * Converts a given drawable to a bitmap, with width and height equal to the default icon size.
+     */
+    public static Bitmap convertToBitmapAtUserIconSize(Resources res, Drawable icon) {
+        int size = res.getDimensionPixelSize(R.dimen.user_icon_size);
+        return convertToBitmapAtSize(icon, size, size);
+    }
+
+    private static Bitmap convertToBitmapAtSize(Drawable icon, int width, int height) {
         if (icon == null) {
             return null;
         }
-        final int width = icon.getIntrinsicWidth();
-        final int height = icon.getIntrinsicHeight();
         Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
         Canvas canvas = new Canvas(bitmap);
         icon.setBounds(0, 0, width, height);
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 78bb53d..5fa4a65 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -150,6 +150,7 @@
     private Icon mShortcutIcon;
     private View mAppNameDivider;
     private TouchDelegateComposite mTouchDelegate = new TouchDelegateComposite(this);
+    private ArrayList<MessagingGroup> mToRecycle = new ArrayList<>();
 
     public ConversationLayout(@NonNull Context context) {
         super(context);
@@ -472,6 +473,12 @@
         updateTitleAndNamesDisplay();
 
         updateConversationLayout();
+
+        // Recycle everything at the end of the update, now that we know it's no longer needed.
+        for (MessagingGroup group : mToRecycle) {
+            group.recycle();
+        }
+        mToRecycle.clear();
     }
 
     /**
@@ -745,18 +752,18 @@
             MessagingGroup group = oldGroups.get(i);
             if (!mGroups.contains(group)) {
                 List<MessagingMessage> messages = group.getMessages();
-                Runnable endRunnable = () -> {
-                    mMessagingLinearLayout.removeTransientView(group);
-                    group.recycle();
-                };
-
                 boolean wasShown = group.isShown();
                 mMessagingLinearLayout.removeView(group);
                 if (wasShown && !MessagingLinearLayout.isGone(group)) {
                     mMessagingLinearLayout.addTransientView(group, 0);
-                    group.removeGroupAnimated(endRunnable);
+                    group.removeGroupAnimated(() -> {
+                        mMessagingLinearLayout.removeTransientView(group);
+                        group.recycle();
+                    });
                 } else {
-                    endRunnable.run();
+                    // Defer recycling until after the update is done, since we may still need the
+                    // old group around to perform other updates.
+                    mToRecycle.add(group);
                 }
                 mMessages.removeAll(messages);
                 mHistoricMessages.removeAll(messages);
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 47cb754..4aa00f6 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -64,7 +64,6 @@
         "libbase",
         "libcutils",
         "libharfbuzz_ng",
-        "libhwui",
         "liblog",
         "libminikin",
         "libz",
@@ -266,6 +265,7 @@
                 "libui",
                 "libgraphicsenv",
                 "libgui",
+                "libhwui",
                 "libmediandk",
                 "libpermission",
                 "libsensor",
@@ -344,9 +344,21 @@
             ],
             static_libs: [
                 "libandroidfw",
-                "libcompiler_rt",
-                "libutils",
+                "libbinary_parse",
+                "libdng_sdk",
+                "libft2",
                 "libhostgraphics",
+                "libhwui",
+                "libimage_type_recognition",
+                "libjpeg",
+                "libpiex",
+                "libpng",
+                "libtiff_directory",
+                "libui-types",
+                "libutils",
+                "libwebp-decode",
+                "libwebp-encode",
+                "libwuffs_mirror_release_c",
             ],
         },
         linux_glibc: {
diff --git a/core/jni/LayoutlibLoader.cpp b/core/jni/LayoutlibLoader.cpp
index 3e513df..93ba23b 100644
--- a/core/jni/LayoutlibLoader.cpp
+++ b/core/jni/LayoutlibLoader.cpp
@@ -14,15 +14,31 @@
  * limitations under the License.
  */
 
-#include "jni.h"
-#include "core_jni_helpers.h"
-
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android/graphics/jni_runtime.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/jni_macros.h>
 #include <unicode/putil.h>
+#include <unicode/udata.h>
+
 #include <clocale>
 #include <sstream>
 #include <unordered_map>
 #include <vector>
 
+#include "core_jni_helpers.h"
+#include "jni.h"
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#endif
+
+#include <iostream>
+
 using namespace std;
 
 /*
@@ -33,6 +49,33 @@
  */
 
 static JavaVM* javaVM;
+static jclass bridge;
+static jclass layoutLog;
+static jmethodID getLogId;
+static jmethodID logMethodId;
+
+extern int register_android_os_Binder(JNIEnv* env);
+extern int register_libcore_util_NativeAllocationRegistry_Delegate(JNIEnv* env);
+
+typedef void (*FreeFunction)(void*);
+
+static void NativeAllocationRegistry_Delegate_nativeApplyFreeFunction(JNIEnv*, jclass,
+                                                                      jlong freeFunction,
+                                                                      jlong ptr) {
+    void* nativePtr = reinterpret_cast<void*>(static_cast<uintptr_t>(ptr));
+    FreeFunction nativeFreeFunction =
+            reinterpret_cast<FreeFunction>(static_cast<uintptr_t>(freeFunction));
+    nativeFreeFunction(nativePtr);
+}
+
+static JNINativeMethod gMethods[] = {
+        NATIVE_METHOD(NativeAllocationRegistry_Delegate, nativeApplyFreeFunction, "(JJ)V"),
+};
+
+int register_libcore_util_NativeAllocationRegistry_Delegate(JNIEnv* env) {
+    return jniRegisterNativeMethods(env, "libcore/util/NativeAllocationRegistry_Delegate", gMethods,
+                                    NELEM(gMethods));
+}
 
 namespace android {
 
@@ -47,6 +90,7 @@
 extern int register_android_database_SQLiteDebug(JNIEnv* env);
 extern int register_android_os_FileObserver(JNIEnv* env);
 extern int register_android_os_MessageQueue(JNIEnv* env);
+extern int register_android_os_Parcel(JNIEnv* env);
 extern int register_android_os_SystemClock(JNIEnv* env);
 extern int register_android_os_SystemProperties(JNIEnv* env);
 extern int register_android_os_Trace(JNIEnv* env);
@@ -54,6 +98,11 @@
 extern int register_android_util_EventLog(JNIEnv* env);
 extern int register_android_util_Log(JNIEnv* env);
 extern int register_android_util_jar_StrictJarFile(JNIEnv* env);
+extern int register_android_view_KeyCharacterMap(JNIEnv* env);
+extern int register_android_view_KeyEvent(JNIEnv* env);
+extern int register_android_view_MotionEvent(JNIEnv* env);
+extern int register_android_view_ThreadedRenderer(JNIEnv* env);
+extern int register_android_view_VelocityTracker(JNIEnv* env);
 extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
 
 #define REG_JNI(name)      { name }
@@ -78,8 +127,10 @@
         {"android.content.res.StringBlock", REG_JNI(register_android_content_StringBlock)},
         {"android.content.res.XmlBlock", REG_JNI(register_android_content_XmlBlock)},
 #ifdef __linux__
+        {"android.os.Binder", REG_JNI(register_android_os_Binder)},
         {"android.os.FileObserver", REG_JNI(register_android_os_FileObserver)},
         {"android.os.MessageQueue", REG_JNI(register_android_os_MessageQueue)},
+        {"android.os.Parcel", REG_JNI(register_android_os_Parcel)},
 #endif
         {"android.os.SystemClock", REG_JNI(register_android_os_SystemClock)},
         {"android.os.SystemProperties", REG_JNI(register_android_os_SystemProperties)},
@@ -88,11 +139,15 @@
         {"android.util.EventLog", REG_JNI(register_android_util_EventLog)},
         {"android.util.Log", REG_JNI(register_android_util_Log)},
         {"android.util.jar.StrictJarFile", REG_JNI(register_android_util_jar_StrictJarFile)},
+        {"android.view.KeyCharacterMap", REG_JNI(register_android_view_KeyCharacterMap)},
+        {"android.view.KeyEvent", REG_JNI(register_android_view_KeyEvent)},
+        {"android.view.MotionEvent", REG_JNI(register_android_view_MotionEvent)},
+        {"android.view.VelocityTracker", REG_JNI(register_android_view_VelocityTracker)},
         {"com.android.internal.util.VirtualRefBasePtr",
          REG_JNI(register_com_android_internal_util_VirtualRefBasePtr)},
+        {"libcore.util.NativeAllocationRegistry_Delegate",
+         REG_JNI(register_libcore_util_NativeAllocationRegistry_Delegate)},
 };
-// Vector to store the names of classes that need delegates of their native methods
-static vector<string> classesToDelegate;
 
 static int register_jni_procs(const std::unordered_map<std::string, RegJNIRec>& jniRegMap,
         const vector<string>& classesToRegister, JNIEnv* env) {
@@ -102,36 +157,17 @@
             return -1;
         }
     }
+
+    if (register_android_graphics_classes(env) < 0) {
+        return -1;
+    }
+
     return 0;
 }
 
 int AndroidRuntime::registerNativeMethods(JNIEnv* env,
         const char* className, const JNINativeMethod* gMethods, int numMethods) {
-    string classNameString = string(className);
-    if (find(classesToDelegate.begin(), classesToDelegate.end(), classNameString)
-            != classesToDelegate.end()) {
-        // Register native methods to the delegate class <classNameString>_NativeDelegate
-        // by adding _Original to the name of each method.
-        replace(classNameString.begin(), classNameString.end(), '$', '_');
-        string delegateClassName = classNameString + "_NativeDelegate";
-        jclass clazz = env->FindClass(delegateClassName.c_str());
-        JNINativeMethod gTypefaceDelegateMethods[numMethods];
-        for (int i = 0; i < numMethods; i++) {
-            JNINativeMethod gTypefaceMethod = gMethods[i];
-            string newName = string(gTypefaceMethod.name) + "_Original";
-            gTypefaceDelegateMethods[i].name = strdup(newName.c_str());
-            gTypefaceDelegateMethods[i].signature = gTypefaceMethod.signature;
-            gTypefaceDelegateMethods[i].fnPtr = gTypefaceMethod.fnPtr;
-        }
-        int result = env->RegisterNatives(clazz, gTypefaceDelegateMethods, numMethods);
-        for (int i = 0; i < numMethods; i++) {
-            free((char*)gTypefaceDelegateMethods[i].name);
-        }
-        return result;
-    }
-
-    jclass clazz = env->FindClass(className);
-    return env->RegisterNatives(clazz, gMethods, numMethods);
+    return jniRegisterNativeMethods(env, className, gMethods, numMethods);
 }
 
 JNIEnv* AndroidRuntime::getJNIEnv() {
@@ -164,6 +200,125 @@
     return result;
 }
 
+void LayoutlibLogger(base::LogId, base::LogSeverity severity, const char* tag, const char* file,
+                     unsigned int line, const char* message) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jint logPrio = severity;
+    jstring tagString = env->NewStringUTF(tag);
+    jstring messageString = env->NewStringUTF(message);
+
+    jobject bridgeLog = env->CallStaticObjectMethod(bridge, getLogId);
+
+    env->CallVoidMethod(bridgeLog, logMethodId, logPrio, tagString, messageString);
+
+    env->DeleteLocalRef(tagString);
+    env->DeleteLocalRef(messageString);
+    env->DeleteLocalRef(bridgeLog);
+}
+
+void LayoutlibAborter(const char* abort_message) {
+    // Layoutlib should not call abort() as it would terminate Studio.
+    // Throw an exception back to Java instead.
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jniThrowRuntimeException(env, "The Android framework has encountered a fatal error");
+}
+
+// This method has been copied/adapted from system/core/init/property_service.cpp
+// If the ro.product.cpu.abilist* properties have not been explicitly
+// set, derive them from ro.system.product.cpu.abilist* properties.
+static void property_initialize_ro_cpu_abilist() {
+    const std::string EMPTY = "";
+    const char* kAbilistProp = "ro.product.cpu.abilist";
+    const char* kAbilist32Prop = "ro.product.cpu.abilist32";
+    const char* kAbilist64Prop = "ro.product.cpu.abilist64";
+
+    // If the properties are defined explicitly, just use them.
+    if (base::GetProperty(kAbilistProp, EMPTY) != EMPTY) {
+        return;
+    }
+
+    std::string abilist32_prop_val;
+    std::string abilist64_prop_val;
+    const auto abilist32_prop = "ro.system.product.cpu.abilist32";
+    const auto abilist64_prop = "ro.system.product.cpu.abilist64";
+    abilist32_prop_val = base::GetProperty(abilist32_prop, EMPTY);
+    abilist64_prop_val = base::GetProperty(abilist64_prop, EMPTY);
+
+    // Merge ABI lists for ro.product.cpu.abilist
+    auto abilist_prop_val = abilist64_prop_val;
+    if (abilist32_prop_val != EMPTY) {
+        if (abilist_prop_val != EMPTY) {
+            abilist_prop_val += ",";
+        }
+        abilist_prop_val += abilist32_prop_val;
+    }
+
+    // Set these properties
+    const std::pair<const char*, const std::string&> set_prop_list[] = {
+            {kAbilistProp, abilist_prop_val},
+            {kAbilist32Prop, abilist32_prop_val},
+            {kAbilist64Prop, abilist64_prop_val},
+    };
+    for (const auto& [prop, prop_val] : set_prop_list) {
+        base::SetProperty(prop, prop_val);
+    }
+}
+
+static void* mmapFile(const char* dataFilePath) {
+#ifdef _WIN32
+    // Windows needs file path in wide chars to handle unicode file paths
+    int size = MultiByteToWideChar(CP_UTF8, 0, dataFilePath, -1, NULL, 0);
+    std::vector<wchar_t> wideDataFilePath(size);
+    MultiByteToWideChar(CP_UTF8, 0, dataFilePath, -1, wideDataFilePath.data(), size);
+    HANDLE file =
+            CreateFileW(wideDataFilePath.data(), GENERIC_READ, FILE_SHARE_READ, nullptr,
+                        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, nullptr);
+    if ((HANDLE)INVALID_HANDLE_VALUE == file) {
+        return nullptr;
+    }
+
+    struct CloseHandleWrapper {
+        void operator()(HANDLE h) { CloseHandle(h); }
+    };
+    std::unique_ptr<void, CloseHandleWrapper> mmapHandle(
+            CreateFileMapping(file, nullptr, PAGE_READONLY, 0, 0, nullptr));
+    if (!mmapHandle) {
+        return nullptr;
+    }
+    return MapViewOfFile(mmapHandle.get(), FILE_MAP_READ, 0, 0, 0);
+#else
+    int fd = open(dataFilePath, O_RDONLY);
+    if (fd == -1) {
+        return nullptr;
+    }
+
+    struct stat sb;
+    if (fstat(fd, &sb) == -1) {
+        close(fd);
+        return nullptr;
+    }
+
+    void* addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+    if (addr == MAP_FAILED) {
+        close(fd);
+        return nullptr;
+    }
+
+    close(fd);
+    return addr;
+#endif
+}
+
+static bool init_icu(const char* dataPath) {
+    void* addr = mmapFile(dataPath);
+    UErrorCode err = U_ZERO_ERROR;
+    udata_setCommonData(addr, &err);
+    if (err != U_ZERO_ERROR) {
+        return false;
+    }
+    return true;
+}
+
 } // namespace android
 
 using namespace android;
@@ -175,37 +330,82 @@
         return JNI_ERR;
     }
 
+    init_android_graphics();
+
     // Configuration is stored as java System properties.
     // Get a reference to System.getProperty
     jclass system = FindClassOrDie(env, "java/lang/System");
     jmethodID getPropertyMethod = GetStaticMethodIDOrDie(env, system, "getProperty",
                                                          "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
 
-    // Get the names of classes that have to delegate their native methods
-    auto delegateNativesToNativesString =
-            (jstring) env->CallStaticObjectMethod(system,
-                    getPropertyMethod, env->NewStringUTF("delegate_natives_to_natives"),
-                    env->NewStringUTF(""));
-    classesToDelegate = parseCsv(env, delegateNativesToNativesString);
-
     // Get the names of classes that need to register their native methods
     auto nativesClassesJString =
-            (jstring) env->CallStaticObjectMethod(system,
-                                                  getPropertyMethod, env->NewStringUTF("native_classes"),
-                                                  env->NewStringUTF(""));
+            (jstring)env->CallStaticObjectMethod(system, getPropertyMethod,
+                                                 env->NewStringUTF("core_native_classes"),
+                                                 env->NewStringUTF(""));
     vector<string> classesToRegister = parseCsv(env, nativesClassesJString);
 
+    jstring registerProperty =
+            (jstring)env->CallStaticObjectMethod(system, getPropertyMethod,
+                                                 env->NewStringUTF(
+                                                         "register_properties_during_load"),
+                                                 env->NewStringUTF(""));
+    const char* registerPropertyString = env->GetStringUTFChars(registerProperty, 0);
+    if (strcmp(registerPropertyString, "true") == 0) {
+        // Set the system properties first as they could be used in the static initialization of
+        // other classes
+        if (register_android_os_SystemProperties(env) < 0) {
+            return JNI_ERR;
+        }
+        classesToRegister.erase(find(classesToRegister.begin(), classesToRegister.end(),
+                                     "android.os.SystemProperties"));
+        bridge = FindClassOrDie(env, "com/android/layoutlib/bridge/Bridge");
+        bridge = MakeGlobalRefOrDie(env, bridge);
+        jmethodID setSystemPropertiesMethod =
+                GetStaticMethodIDOrDie(env, bridge, "setSystemProperties", "()V");
+        env->CallStaticVoidMethod(bridge, setSystemPropertiesMethod);
+        property_initialize_ro_cpu_abilist();
+    }
+    env->ReleaseStringUTFChars(registerProperty, registerPropertyString);
+
     if (register_jni_procs(gRegJNIMap, classesToRegister, env) < 0) {
         return JNI_ERR;
     }
 
     // Set the location of ICU data
-    auto stringPath = (jstring) env->CallStaticObjectMethod(system,
-        getPropertyMethod, env->NewStringUTF("icu.dir"),
-        env->NewStringUTF(""));
+    auto stringPath = (jstring)env->CallStaticObjectMethod(system, getPropertyMethod,
+                                                           env->NewStringUTF("icu.data.path"),
+                                                           env->NewStringUTF(""));
     const char* path = env->GetStringUTFChars(stringPath, 0);
-    u_setDataDirectory(path);
+    bool icuInitialized = init_icu(path);
     env->ReleaseStringUTFChars(stringPath, path);
+    if (!icuInitialized) {
+        return JNI_ERR;
+    }
+
+    jstring useJniProperty =
+            (jstring)env->CallStaticObjectMethod(system, getPropertyMethod,
+                                                 env->NewStringUTF("use_bridge_for_logging"),
+                                                 env->NewStringUTF(""));
+    const char* useJniString = env->GetStringUTFChars(useJniProperty, 0);
+    if (strcmp(useJniString, "true") == 0) {
+        layoutLog = FindClassOrDie(env, "com/android/ide/common/rendering/api/ILayoutLog");
+        layoutLog = MakeGlobalRefOrDie(env, layoutLog);
+        logMethodId = GetMethodIDOrDie(env, layoutLog, "logAndroidFramework",
+                                       "(ILjava/lang/String;Ljava/lang/String;)V");
+        if (bridge == nullptr) {
+            bridge = FindClassOrDie(env, "com/android/layoutlib/bridge/Bridge");
+            bridge = MakeGlobalRefOrDie(env, bridge);
+        }
+        getLogId = GetStaticMethodIDOrDie(env, bridge, "getLog",
+                                          "()Lcom/android/ide/common/rendering/api/ILayoutLog;");
+        android::base::SetLogger(LayoutlibLogger);
+        android::base::SetAborter(LayoutlibAborter);
+    } else {
+        // initialize logging, so ANDROD_LOG_TAGS env variable is respected
+        android::base::InitLogging(nullptr, android::base::StderrLogger);
+    }
+    env->ReleaseStringUTFChars(useJniProperty, useJniString);
 
     // Use English locale for number format to ensure correct parsing of floats when using strtof
     setlocale(LC_NUMERIC, "en_US.UTF-8");
@@ -213,3 +413,9 @@
     return JNI_VERSION_1_6;
 }
 
+JNIEXPORT void JNI_OnUnload(JavaVM* vm, void*) {
+    JNIEnv* env = nullptr;
+    vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
+    env->DeleteGlobalRef(bridge);
+    env->DeleteGlobalRef(layoutLog);
+}
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 3a2401a..7150fca 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3364,7 +3364,7 @@
             area for the user and that ideally it should not be covered. Setting this is only
             appropriate for UI where the user would likely take action to uncover it.
             <p>The system will try to respect this, but when not possible will ignore it.
-            See {@link android.view.View#setPreferKeepClear}. -->
+            <p>This is equivalent to {@link android.view.View#setPreferKeepClear}.-->
         <attr name="preferKeepClear" format="boolean" />
 
         <!-- <p>Whether or not the auto handwriting initiation is enabled in this View.
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 4874e65..39b41b5 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -991,4 +991,7 @@
     <dimen name="secondary_rounded_corner_radius_adjustment">0px</dimen>
     <dimen name="secondary_rounded_corner_radius_top_adjustment">0px</dimen>
     <dimen name="secondary_rounded_corner_radius_bottom_adjustment">0px</dimen>
+
+    <!-- Default size for user icons (a.k.a. avatar images) -->
+    <dimen name="user_icon_size">190dp</dimen>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e27aa83..f531c3a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -545,6 +545,7 @@
   <java-symbol type="dimen" name="immersive_mode_cling_width" />
   <java-symbol type="dimen" name="accessibility_magnification_indicator_width" />
   <java-symbol type="dimen" name="circular_display_mask_thickness" />
+  <java-symbol type="dimen" name="user_icon_size" />
 
   <java-symbol type="string" name="add_account_button_label" />
   <java-symbol type="string" name="addToDictionary" />
diff --git a/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java b/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
index a2bc77a..3a27225 100644
--- a/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
+++ b/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
@@ -29,6 +29,7 @@
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
+import java.util.function.BiFunction;
 
 /**
  * Unit test for {@link AndroidFuture}.
@@ -154,4 +155,35 @@
                 expectThrows(ExecutionException.class, future1::get);
         assertThat(executionException.getCause()).isInstanceOf(UnsupportedOperationException.class);
     }
+
+    @Test
+    public void testThenCombine() throws Exception {
+        String nearFutureString = "near future comes";
+        AndroidFuture<String> nearFuture = AndroidFuture.supply(() -> nearFutureString);
+        String farFutureString = " before far future.";
+        AndroidFuture<String> farFuture = AndroidFuture.supply(() -> farFutureString);
+        AndroidFuture<String> combinedFuture =
+                nearFuture.thenCombine(farFuture, ((s1, s2) -> s1 + s2));
+
+        assertThat(combinedFuture.get()).isEqualTo(nearFutureString + farFutureString);
+    }
+
+    @Test
+    public void testThenCombine_functionThrowingException() throws Exception {
+        String nearFutureString = "near future comes";
+        AndroidFuture<String> nearFuture = AndroidFuture.supply(() -> nearFutureString);
+        String farFutureString = " before far future.";
+        AndroidFuture<String> farFuture = AndroidFuture.supply(() -> farFutureString);
+        UnsupportedOperationException exception = new UnsupportedOperationException(
+                "Unsupported operation exception thrown!");
+        BiFunction<String, String, String> throwingFunction = (s1, s2) -> {
+            throw exception;
+        };
+        AndroidFuture<String> combinedFuture = nearFuture.thenCombine(farFuture, throwingFunction);
+
+        ExecutionException thrown = expectThrows(ExecutionException.class,
+                () -> combinedFuture.get());
+
+        assertThat(thrown.getCause()).isSameInstanceAs(exception);
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 6ffcf10..241f1a7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -423,7 +423,6 @@
                             WindowContainerTransaction t) {
                         // This is triggered right before the rotation is applied
                         if (fromRotation != toRotation) {
-                            mBubblePositioner.setRotation(toRotation);
                             if (mStackView != null) {
                                 // Layout listener set on stackView will update the positioner
                                 // once the rotation is applied
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 127d5a8..75b19fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -20,6 +20,7 @@
 
 import android.annotation.IntDef;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Insets;
 import android.graphics.PointF;
@@ -112,10 +113,6 @@
         update();
     }
 
-    public void setRotation(int rotation) {
-        mRotation = rotation;
-    }
-
     /**
      * Available space and inset information. Call this when config changes
      * occur or when added to a window.
@@ -273,7 +270,8 @@
 
     /** @return whether the device is in landscape orientation. */
     public boolean isLandscape() {
-        return mRotation == Surface.ROTATION_90 || mRotation == Surface.ROTATION_270;
+        return mContext.getResources().getConfiguration().orientation
+                == Configuration.ORIENTATION_LANDSCAPE;
     }
 
     /** @return whether the screen is considered large. */
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 3887372..6e695e6 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -7294,8 +7294,13 @@
      * Ultrasound playback and capture, false otherwise.
      */
     @SystemApi
-    public static boolean isUltrasoundSupported() {
-        return AudioSystem.isUltrasoundSupported();
+    @RequiresPermission(android.Manifest.permission.ACCESS_ULTRASOUND)
+    public boolean isUltrasoundSupported() {
+        try {
+            return getService().isUltrasoundSupported();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 2c9f015..d702eb9 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -138,6 +138,8 @@
 
     boolean isMicrophoneMuted();
 
+    boolean isUltrasoundSupported();
+
     void setMicrophoneMute(boolean on, String callingPackage, int userId, in String attributionTag);
 
     oneway void setMicrophoneMuteFromSwitch(boolean on);
diff --git a/native/android/input.cpp b/native/android/input.cpp
index c06c81e..a231d8f 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -283,6 +283,21 @@
             axis, pointer_index, history_index);
 }
 
+int32_t AMotionEvent_getActionButton(const AInputEvent* motion_event) {
+    return static_cast<const MotionEvent*>(motion_event)->getActionButton();
+}
+
+int32_t AMotionEvent_getClassification(const AInputEvent* motion_event) {
+    switch (static_cast<const MotionEvent*>(motion_event)->getClassification()) {
+        case android::MotionClassification::NONE:
+            return AMOTION_EVENT_CLASSIFICATION_NONE;
+        case android::MotionClassification::AMBIGUOUS_GESTURE:
+            return AMOTION_EVENT_CLASSIFICATION_AMBIGUOUS_GESTURE;
+        case android::MotionClassification::DEEP_PRESS:
+            return AMOTION_EVENT_CLASSIFICATION_DEEP_PRESS;
+    }
+}
+
 const AInputEvent* AMotionEvent_fromJava(JNIEnv* env, jobject motionEvent) {
     MotionEvent* eventSrc = android::android_view_MotionEvent_getNativePtr(env, motionEvent);
     if (eventSrc == nullptr) {
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 3009a36..67a98a9 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -114,8 +114,10 @@
     ALooper_removeFd;
     ALooper_wake;
     AMotionEvent_getAction;
+    AMotionEvent_getActionButton; # introduced=Tiramisu
     AMotionEvent_getAxisValue; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=13 introduced-x86_64=21
     AMotionEvent_getButtonState; # introduced-arm=14 introduced-arm64=21 introduced-mips=14 introduced-mips64=21 introduced-x86=14 introduced-x86_64=21
+    AMotionEvent_getClassification; # introduced=Tiramisu
     AMotionEvent_getDownTime;
     AMotionEvent_getEdgeFlags;
     AMotionEvent_getEventTime;
diff --git a/packages/CompanionDeviceManager/Android.bp b/packages/CompanionDeviceManager/Android.bp
index 0e60873..6ded1637 100644
--- a/packages/CompanionDeviceManager/Android.bp
+++ b/packages/CompanionDeviceManager/Android.bp
@@ -39,6 +39,7 @@
     static_libs: [
         "androidx.lifecycle_lifecycle-livedata",
         "androidx.lifecycle_lifecycle-extensions",
+        "androidx.recyclerview_recyclerview",
         "androidx.appcompat_appcompat",
     ],
 
diff --git a/packages/CompanionDeviceManager/res/color/selector.xml b/packages/CompanionDeviceManager/res/color/selector.xml
new file mode 100644
index 0000000..fda827d
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/color/selector.xml
@@ -0,0 +1,20 @@
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" android:color="@android:color/darker_gray"/> <!-- pressed -->
+    <item android:color="@android:color/white"/>
+</selector>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
index 313e164..70cbfdf 100644
--- a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
+++ b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
@@ -49,8 +49,8 @@
             android:layout_height="0dp"
             android:layout_weight="1">
 
-        <ListView
-                android:id="@+id/device_list"
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/device_list"
                 style="@android:style/Widget.Material.ListView"
                 android:layout_width="match_parent"
                 android:layout_height="200dp" />
diff --git a/packages/CompanionDeviceManager/res/layout/list_item_device.xml b/packages/CompanionDeviceManager/res/layout/list_item_device.xml
index d79aea6..153fc1f 100644
--- a/packages/CompanionDeviceManager/res/layout/list_item_device.xml
+++ b/packages/CompanionDeviceManager/res/layout/list_item_device.xml
@@ -19,7 +19,8 @@
               android:layout_height="wrap_content"
               android:orientation="horizontal"
               android:gravity="center_vertical"
-              android:padding="12dp">
+              android:padding="12dp"
+              android:background="@color/selector">
 
     <!-- Do NOT change the ID of the root LinearLayout above: it's referenced in CTS tests. -->
 
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index 16e851b..b51d310 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -46,10 +46,11 @@
 import android.util.Log;
 import android.view.View;
 import android.widget.Button;
-import android.widget.ListView;
 import android.widget.TextView;
 
 import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
 
 import java.util.List;
 
@@ -94,9 +95,9 @@
     // regular.
     private Button mButtonAllow;
 
-    // The list is only shown for multiple-device regular association request, after at least one
-    // matching device is found.
-    private @Nullable ListView mListView;
+    // The recycler view is only shown for multiple-device regular association request, after
+    // at least one matching device is found.
+    private @Nullable RecyclerView mRecyclerView;
     private @Nullable DeviceListAdapter mAdapter;
 
     // The flag used to prevent double taps, that may lead to sending several requests for creating
@@ -195,14 +196,15 @@
         mTitle = findViewById(R.id.title);
         mSummary = findViewById(R.id.summary);
 
-        mListView = findViewById(R.id.device_list);
-        mListView.setOnItemClickListener((av, iv, position, id) -> onListItemClick(position));
+        mRecyclerView = findViewById(R.id.device_list);
+        mAdapter = new DeviceListAdapter(this, this::onListItemClick);
 
         mButtonAllow = findViewById(R.id.btn_positive);
         mButtonAllow.setOnClickListener(this::onPositiveButtonClick);
         findViewById(R.id.btn_negative).setOnClickListener(this::onNegativeButtonClick);
 
         final CharSequence appLabel = getApplicationLabel(this, mRequest.getPackageName());
+
         if (mRequest.isSelfManaged()) {
             initUiForSelfManagedAssociation(appLabel);
         } else if (mRequest.isSingleDevice()) {
@@ -333,7 +335,7 @@
         mTitle.setText(title);
         mSummary.setText(summary);
 
-        mListView.setVisibility(View.GONE);
+        mRecyclerView.setVisibility(View.GONE);
     }
 
     private void initUiForSingleDevice(CharSequence appLabel) {
@@ -345,12 +347,12 @@
                 deviceFilterPairs -> updateSingleDeviceUi(
                         deviceFilterPairs, deviceProfile, appLabel));
 
-        mListView.setVisibility(View.GONE);
+        mRecyclerView.setVisibility(View.GONE);
     }
 
     private void updateSingleDeviceUi(List<DeviceFilterPair<?>> deviceFilterPairs,
             String deviceProfile, CharSequence appLabel) {
-        // Ignore "empty" scan repots.
+        // Ignore "empty" scan reports.
         if (deviceFilterPairs.isEmpty()) return;
         mSelectedDevice = requireNonNull(deviceFilterPairs.get(0));
 
@@ -393,10 +395,12 @@
         mTitle.setText(title);
         mSummary.setText(summary);
 
-        mAdapter = new DeviceListAdapter(this);
+        mAdapter = new DeviceListAdapter(this, this::onListItemClick);
 
         // TODO: hide the list and show a spinner until a first device matching device is found.
-        mListView.setAdapter(mAdapter);
+        mRecyclerView.setAdapter(mAdapter);
+        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+
         CompanionDeviceDiscoveryService.getScanResult().observe(
                 /* lifecycleOwner */ this,
                 /* observer */ mAdapter);
@@ -414,6 +418,8 @@
             if (DEBUG) Log.w(TAG, "Already selected.");
             return;
         }
+        // Notify the adapter to highlight the selected item.
+        mAdapter.setSelectedPosition(position);
 
         mSelectedDevice = requireNonNull(selectedDevice);
 
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java
index 198b778..e5513b0 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java
@@ -15,73 +15,84 @@
  */
 
 package com.android.companiondevicemanager;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.BaseAdapter;
 import android.widget.ImageView;
 import android.widget.TextView;
 
 import androidx.lifecycle.Observer;
+import androidx.recyclerview.widget.RecyclerView;
 
 import java.util.List;
-
 /**
  * Adapter for the list of "found" devices.
  */
-class DeviceListAdapter extends BaseAdapter implements Observer<List<DeviceFilterPair<?>>> {
+class DeviceListAdapter extends RecyclerView.Adapter<DeviceListAdapter.ViewHolder> implements
+        Observer<List<DeviceFilterPair<?>>> {
+    public int mSelectedPosition = RecyclerView.NO_POSITION;
+
     private final Context mContext;
 
     // List if pairs (display name, address)
     private List<DeviceFilterPair<?>> mDevices;
 
-    DeviceListAdapter(Context context) {
+    private OnItemClickListener mListener;
+
+    private static final int TYPE_WIFI = 0;
+    private static final int TYPE_BT = 1;
+
+    DeviceListAdapter(Context context, OnItemClickListener listener) {
         mContext = context;
+        mListener = listener;
     }
 
-    @Override
-    public int getCount() {
-        return mDevices != null ? mDevices.size() : 0;
-    }
-
-    @Override
     public DeviceFilterPair<?> getItem(int position) {
         return mDevices.get(position);
     }
 
     @Override
+    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View view = LayoutInflater.from(parent.getContext()).inflate(
+                R.layout.list_item_device, parent, false);
+        ViewHolder viewHolder = new ViewHolder(view);
+        if (viewType == TYPE_WIFI) {
+            viewHolder.mImageView.setImageDrawable(getIcon(
+                    com.android.internal.R.drawable.ic_wifi_signal_3));
+        } else {
+            viewHolder.mImageView.setImageDrawable(getIcon(
+                    android.R.drawable.stat_sys_data_bluetooth));
+        }
+        return viewHolder;
+    }
+
+    @Override
+    public void onBindViewHolder(ViewHolder holder, int position) {
+        holder.itemView.setSelected(mSelectedPosition == position);
+        holder.mTextView.setText(mDevices.get(position).getDisplayName());
+        holder.itemView.setOnClickListener(v -> mListener.onItemClick(position));
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        return isWifiDevice(position) ? TYPE_WIFI : TYPE_BT;
+    }
+
+    @Override
     public long getItemId(int position) {
         return position;
     }
 
     @Override
-    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
-        final View view = convertView != null
-                ? convertView
-                : LayoutInflater.from(mContext).inflate(R.layout.list_item_device, parent, false);
-
-        final DeviceFilterPair<?> item = getItem(position);
-        bindView(view, item);
-
-        return view;
+    public int getItemCount() {
+        return mDevices != null ? mDevices.size() : 0;
     }
 
-    private void bindView(@NonNull View view, DeviceFilterPair<?> item) {
-        final TextView textView = view.findViewById(android.R.id.text1);
-        textView.setText(item.getDisplayName());
-
-        final ImageView iconView = view.findViewById(android.R.id.icon);
-
-        // TODO(b/211417476): Set either Bluetooth or WiFi icon.
-        iconView.setVisibility(View.GONE);
-        // final int iconRes = isBt ? android.R.drawable.stat_sys_data_bluetooth
-        //        : com.android.internal.R.drawable.ic_wifi_signal_3;
-        // final Drawable icon = getTintedIcon(mResources, iconRes);
-        // iconView.setImageDrawable(icon);
+    public void setSelectedPosition(int position) {
+        mSelectedPosition = position;
     }
 
     @Override
@@ -89,4 +100,28 @@
         mDevices = deviceFilterPairs;
         notifyDataSetChanged();
     }
+
+    static class ViewHolder extends RecyclerView.ViewHolder {
+        private TextView mTextView;
+        private ImageView mImageView;
+        ViewHolder(View itemView) {
+            super(itemView);
+            mTextView = itemView.findViewById(android.R.id.text1);
+            mImageView = itemView.findViewById(android.R.id.icon);
+        }
+    }
+
+    private boolean isWifiDevice(int position) {
+        return mDevices.get(position).getDevice() instanceof android.net.wifi.ScanResult;
+    }
+
+    private Drawable getIcon(int resId) {
+        Drawable icon = mContext.getResources().getDrawable(resId, null);
+        icon.setTint(Color.DKGRAY);
+        return icon;
+    }
+
+    public interface OnItemClickListener {
+        void onItemClick(int position);
+    }
 }
diff --git a/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
index 1a955c4..72243f9 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
@@ -146,7 +146,7 @@
          * @param iface the name of the interface.
          * @param state the current state of the interface, or {@link #STATE_ABSENT} if the
          *              interface was removed.
-         * @param role whether the interface is in the client mode or server mode.
+         * @param role whether the interface is in client mode or server mode.
          * @param configuration the current IP configuration of the interface.
          * @hide
          */
diff --git a/packages/SettingsLib/ActivityEmbedding/Android.bp b/packages/SettingsLib/ActivityEmbedding/Android.bp
index fc82b79..2569198 100644
--- a/packages/SettingsLib/ActivityEmbedding/Android.bp
+++ b/packages/SettingsLib/ActivityEmbedding/Android.bp
@@ -14,6 +14,8 @@
 
     static_libs: [
         "androidx.annotation_annotation",
+        "androidx.core_core",
+        "windowExtLib",
         "SettingsLibUtils",
     ],
     sdk_version: "system_current",
diff --git a/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
index 7f17d26..44b3b4e 100644
--- a/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
+++ b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
@@ -16,8 +16,14 @@
 
 package com.android.settingslib.activityembedding;
 
+import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+import androidx.core.os.BuildCompat;
+import androidx.window.embedding.SplitController;
 
 import com.android.settingslib.utils.BuildCompatUtils;
 
@@ -44,6 +50,33 @@
         return false;
     }
 
+    /**
+     * Whether current activity is embedded in the Settings app or not.
+     */
+    public static boolean isActivityEmbedded(Activity activity) {
+        return SplitController.getInstance().isActivityEmbedded(activity);
+    }
+
+    /**
+     * Whether current activity is suggested to show back button or not.
+     */
+    public static boolean shouldHideBackButton(Activity activity, boolean isSecondaryLayerPage) {
+        if (!BuildCompat.isAtLeastT()) {
+            return false;
+        }
+        if (!isSecondaryLayerPage) {
+            return false;
+        }
+        final String shouldHideBackButton = Settings.Global.getString(activity.getContentResolver(),
+                "settings_hide_secondary_page_back_button_in_two_pane");
+
+        if (TextUtils.isEmpty(shouldHideBackButton)
+                || TextUtils.equals("true", shouldHideBackButton)) {
+            return isActivityEmbedded(activity);
+        }
+        return false;
+    }
+
     private ActivityEmbeddingUtils() {
     }
 }
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 684f4de..7a5ea47 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -48,7 +48,6 @@
         "SettingsLibCollapsingToolbarBaseActivity",
         "SettingsLibTwoTargetPreference",
         "SettingsLibSettingsTransition",
-        "SettingsLibActivityEmbedding",
         "SettingsLibButtonPreference",
         "setupdesign",
     ],
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index 25d6c555..d893d09 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -22,8 +22,6 @@
     <!-- The translation for disappearing security views after having solved them. -->
     <dimen name="disappear_y_translation">-32dp</dimen>
 
-    <dimen name="circle_avatar_size">190dp</dimen>
-
     <!-- Height of a user icon view -->
     <dimen name="user_icon_view_height">24dp</dimen>
     <!-- User spinner -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 883e080..f6e3557 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -144,7 +144,7 @@
      * Returns a circular icon for a user.
      */
     public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) {
-        final int iconSize = UserIconDrawable.getSizeForList(context);
+        final int iconSize = UserIconDrawable.getDefaultSize(context);
         if (user.isManagedProfile()) {
             Drawable drawable = UserIconDrawable.getManagedUserDrawable(context);
             drawable.setBounds(0, 0, iconSize, iconSize);
diff --git a/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java b/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java
index fc38ada..afd3626 100644
--- a/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java
@@ -22,7 +22,6 @@
 
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.os.Handler;
 import android.os.UserHandle;
@@ -34,10 +33,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 
-import java.util.ArrayList;
 import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -52,12 +48,11 @@
     private static DeviceStateRotationLockSettingsManager sSingleton;
 
     private final ContentResolver mContentResolver;
+    private final String[] mDeviceStateRotationLockDefaults;
     private final Handler mMainHandler = Handler.getMain();
     private final Set<DeviceStateRotationLockSettingsListener> mListeners = new HashSet<>();
-    private String[] mDeviceStateRotationLockDefaults;
     private SparseIntArray mDeviceStateRotationLockSettings;
     private SparseIntArray mDeviceStateRotationLockFallbackSettings;
-    private List<SettableDeviceState> mSettableDeviceStates;
 
     private DeviceStateRotationLockSettingsManager(Context context) {
         mContentResolver = context.getContentResolver();
@@ -78,12 +73,6 @@
         return sSingleton;
     }
 
-    /** Resets the singleton instance of this class. Only used for testing. */
-    @VisibleForTesting
-    public static synchronized void resetInstance() {
-        sSingleton = null;
-    }
-
     /** Returns true if device-state based rotation lock settings are enabled. */
     public static boolean isDeviceStateRotationLockEnabled(Context context) {
         return context.getResources()
@@ -191,12 +180,6 @@
         return true;
     }
 
-    /** Returns a list of device states and their respective auto-rotation setting availability. */
-    public List<SettableDeviceState> getSettableDeviceStates() {
-        // Returning a copy to make sure that nothing outside can mutate our internal list.
-        return new ArrayList<>(mSettableDeviceStates);
-    }
-
     private void initializeInMemoryMap() {
         String serializedSetting =
                 Settings.Secure.getStringForUser(
@@ -232,17 +215,6 @@
         }
     }
 
-    /**
-     * Resets the state of the class and saved settings back to the default values provided by the
-     * resources config.
-     */
-    @VisibleForTesting
-    public void resetStateForTesting(Resources resources) {
-        mDeviceStateRotationLockDefaults =
-                resources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults);
-        fallbackOnDefaults();
-    }
-
     private void fallbackOnDefaults() {
         loadDefaults();
         persistSettings();
@@ -279,7 +251,6 @@
     }
 
     private void loadDefaults() {
-        mSettableDeviceStates = new ArrayList<>(mDeviceStateRotationLockDefaults.length);
         mDeviceStateRotationLockSettings = new SparseIntArray(
                 mDeviceStateRotationLockDefaults.length);
         mDeviceStateRotationLockFallbackSettings = new SparseIntArray(1);
@@ -300,8 +271,6 @@
                                         + values.length);
                     }
                 }
-                boolean isSettable = rotationLockSetting != DEVICE_STATE_ROTATION_LOCK_IGNORED;
-                mSettableDeviceStates.add(new SettableDeviceState(deviceState, isSettable));
                 mDeviceStateRotationLockSettings.put(deviceState, rotationLockSetting);
             } catch (NumberFormatException e) {
                 Log.wtf(TAG, "Error parsing settings entry. Entry was: " + entry, e);
@@ -331,38 +300,4 @@
         /** Called whenever the settings have changed. */
         void onSettingsChanged();
     }
-
-    /** Represents a device state and whether it has an auto-rotation setting. */
-    public static class SettableDeviceState {
-        private final int mDeviceState;
-        private final boolean mIsSettable;
-
-        SettableDeviceState(int deviceState, boolean isSettable) {
-            mDeviceState = deviceState;
-            mIsSettable = isSettable;
-        }
-
-        /** Returns the device state associated with this object. */
-        public int getDeviceState() {
-            return mDeviceState;
-        }
-
-        /** Returns whether there is an auto-rotation setting for this device state. */
-        public boolean isSettable() {
-            return mIsSettable;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (!(o instanceof SettableDeviceState)) return false;
-            SettableDeviceState that = (SettableDeviceState) o;
-            return mDeviceState == that.mDeviceState && mIsSettable == that.mIsSettable;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(mDeviceState, mIsSettable);
-        }
-    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
index e5ea446..7db00f3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
@@ -31,8 +31,6 @@
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 
-import com.android.settingslib.R;
-
 /**
  * Converts the user avatar icon to a circularly clipped one.
  * TODO: Move this to an internal framework class and share with the one in Keyguard.
@@ -49,9 +47,9 @@
 
     public static CircleFramedDrawable getInstance(Context context, Bitmap icon) {
         Resources res = context.getResources();
-        float iconSize = res.getDimension(R.dimen.circle_avatar_size);
+        int iconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.user_icon_size);
 
-        CircleFramedDrawable instance = new CircleFramedDrawable(icon, (int) iconSize);
+        CircleFramedDrawable instance = new CircleFramedDrawable(icon, iconSize);
         return instance;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
index 035fafd..d01f2b4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
@@ -49,8 +49,6 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.core.os.BuildCompat;
 
-import com.android.settingslib.R;
-
 /**
  * Converts the user avatar icon to a circularly clipped one with an optional badge and frame
  */
@@ -120,8 +118,9 @@
      * @param context
      * @return size in pixels
      */
-    public static int getSizeForList(Context context) {
-        return (int) context.getResources().getDimension(R.dimen.circle_avatar_size);
+    public static int getDefaultSize(Context context) {
+        return context.getResources()
+                .getDimensionPixelSize(com.android.internal.R.dimen.user_icon_size);
     }
 
     public UserIconDrawable() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
index f8bb38b..5862f60 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
@@ -16,19 +16,17 @@
 
 package com.android.settingslib.users;
 
-import android.annotation.NonNull;
 import android.app.Activity;
 import android.content.Intent;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.util.Log;
 import android.widget.ImageView;
 
 import com.android.internal.util.UserIcons;
-import com.android.settingslib.R;
 import com.android.settingslib.drawable.CircleFramedDrawable;
 import com.android.settingslib.utils.ThreadUtils;
 
@@ -114,10 +112,10 @@
     private void onDefaultIconSelected(int tintColor) {
         try {
             ThreadUtils.postOnBackgroundThread(() -> {
+                Resources res = mActivity.getResources();
                 Drawable drawable =
-                        UserIcons.getDefaultUserIconInColor(mActivity.getResources(), tintColor);
-                Bitmap bitmap = convertToBitmap(drawable,
-                        (int) mActivity.getResources().getDimension(R.dimen.circle_avatar_size));
+                        UserIcons.getDefaultUserIconInColor(res, tintColor);
+                Bitmap bitmap = UserIcons.convertToBitmapAtUserIconSize(res, drawable);
 
                 ThreadUtils.postOnMainThread(() -> onPhotoProcessed(bitmap));
             }).get();
@@ -126,14 +124,6 @@
         }
     }
 
-    private static Bitmap convertToBitmap(@NonNull Drawable icon, int size) {
-        Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(bitmap);
-        icon.setBounds(0, 0, size, size);
-        icon.draw(canvas);
-        return bitmap;
-    }
-
     private void onPhotoCropped(final Uri data) {
         ThreadUtils.postOnBackgroundThread(() -> {
             InputStream imageStream = null;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
deleted file mode 100644
index 8d687b6..0000000
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2022 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.settingslib.devicestate;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.res.Resources;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.R;
-import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager.SettableDeviceState;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class DeviceStateRotationLockSettingsManagerTest {
-
-    @Mock private Context mMockContext;
-    @Mock private Resources mMockResources;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        Context context = InstrumentationRegistry.getTargetContext();
-        when(mMockContext.getApplicationContext()).thenReturn(mMockContext);
-        when(mMockContext.getResources()).thenReturn(mMockResources);
-        when(mMockContext.getContentResolver()).thenReturn(context.getContentResolver());
-    }
-
-    @Test
-    public void getSettableDeviceStates_returnsExpectedValuesInOriginalOrder() {
-        when(mMockResources.getStringArray(
-                R.array.config_perDeviceStateRotationLockDefaults)).thenReturn(
-                new String[]{"2:2", "4:0", "1:1", "0:0"});
-
-        List<SettableDeviceState> settableDeviceStates =
-                DeviceStateRotationLockSettingsManager.getInstance(
-                        mMockContext).getSettableDeviceStates();
-
-        assertThat(settableDeviceStates).containsExactly(
-                new SettableDeviceState(/* deviceState= */ 2, /* isSettable= */ true),
-                new SettableDeviceState(/* deviceState= */ 4, /* isSettable= */ false),
-                new SettableDeviceState(/* deviceState= */ 1, /* isSettable= */ true),
-                new SettableDeviceState(/* deviceState= */ 0, /* isSettable= */ false)
-        ).inOrder();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index f982790..4a9a1f1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -263,22 +263,29 @@
         inoutInfo.touchableRegion.set(getTouchRegion(true));
     }
 
-    private Region getTouchRegion(boolean includeScrim) {
-        Region touchRegion = new Region();
+    private Region getSwipeRegion() {
+        Region swipeRegion = new Region();
 
         final Rect tmpRect = new Rect();
         mScreenshotPreview.getBoundsOnScreen(tmpRect);
         tmpRect.inset((int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP),
                 (int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP));
-        touchRegion.op(tmpRect, Region.Op.UNION);
+        swipeRegion.op(tmpRect, Region.Op.UNION);
         mActionsContainerBackground.getBoundsOnScreen(tmpRect);
         tmpRect.inset((int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP),
                 (int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP));
-        touchRegion.op(tmpRect, Region.Op.UNION);
+        swipeRegion.op(tmpRect, Region.Op.UNION);
         mDismissButton.getBoundsOnScreen(tmpRect);
-        touchRegion.op(tmpRect, Region.Op.UNION);
+        swipeRegion.op(tmpRect, Region.Op.UNION);
+
+        return swipeRegion;
+    }
+
+    private Region getTouchRegion(boolean includeScrim) {
+        Region touchRegion = getSwipeRegion();
 
         if (includeScrim && mScrollingScrim.getVisibility() == View.VISIBLE) {
+            final Rect tmpRect = new Rect();
             mScrollingScrim.getBoundsOnScreen(tmpRect);
             touchRegion.op(tmpRect, Region.Op.UNION);
         }
@@ -328,7 +335,7 @@
     @Override // ViewGroup
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         // scrolling scrim should not be swipeable; return early if we're on the scrim
-        if (!getTouchRegion(false).contains((int) ev.getRawX(), (int) ev.getRawY())) {
+        if (!getSwipeRegion().contains((int) ev.getRawX(), (int) ev.getRawY())) {
             return false;
         }
         // always pass through the down event so the swipe handler knows the initial state
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserCreator.java b/packages/SystemUI/src/com/android/systemui/user/UserCreator.java
index 3a270bb..0686071c 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserCreator.java
+++ b/packages/SystemUI/src/com/android/systemui/user/UserCreator.java
@@ -19,6 +19,7 @@
 import android.app.Dialog;
 import android.content.Context;
 import android.content.pm.UserInfo;
+import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.os.UserManager;
 
@@ -70,10 +71,12 @@
             }
 
             Drawable newUserIcon = userIcon;
+            Resources res = mContext.getResources();
             if (newUserIcon == null) {
-                newUserIcon = UserIcons.getDefaultUserIcon(mContext.getResources(), user.id, false);
+                newUserIcon = UserIcons.getDefaultUserIcon(res, user.id, false);
             }
-            mUserManager.setUserIcon(user.id, UserIcons.convertToBitmap(newUserIcon));
+            mUserManager.setUserIcon(
+                    user.id, UserIcons.convertToBitmapAtUserIconSize(res, newUserIcon));
 
             userCreationProgressDialog.dismiss();
             successCallback.accept(user);
diff --git a/services/Android.bp b/services/Android.bp
index e0ca8a6..2e4405f 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -54,7 +54,9 @@
         SYSTEM_OPTIMIZE_JAVA: {
             optimize: {
                 enabled: true,
-                optimize: true,
+                // TODO(b/210510433): Enable optimizations after improving
+                // retracing infra.
+                optimize: false,
                 shrink: true,
                 proguard_flags_files: ["proguard.flags"],
             },
diff --git a/services/companion/java/com/android/server/companion/CompanionApplicationController.java b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
index be1bc79..c39b59a 100644
--- a/services/companion/java/com/android/server/companion/CompanionApplicationController.java
+++ b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
@@ -26,6 +26,7 @@
 import android.content.Context;
 import android.os.Handler;
 import android.util.Log;
+import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
@@ -120,6 +121,12 @@
             mBoundCompanionApplications.setValueForPackage(userId, packageName, serviceConnectors);
         }
 
+        if (serviceConnectors.isEmpty()) {
+            Slog.e(TAG, "Can't find CompanionDeviceService implementer in package: "
+                    + packageName + ". Please check if they are correctly declared.");
+            return;
+        }
+
         // The first connector in the list is always the primary connector: set a listener to it.
         serviceConnectors.get(0).setListener(this::onPrimaryServiceBindingDied);
 
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 9c8ed5a..efbc4de 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -3019,14 +3019,32 @@
         intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
         intent.putExtra(PHONE_CONSTANTS_SLOT_KEY, phoneId);
         intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, phoneId);
+
         // Send the broadcast twice -- once for all apps with READ_PHONE_STATE, then again
-        // for all apps with READ_PRIV but not READ_PHONE_STATE. This ensures that any app holding
-        // either READ_PRIV or READ_PHONE get this broadcast exactly once.
-        mContext.sendBroadcastAsUser(intent, UserHandle.ALL, Manifest.permission.READ_PHONE_STATE);
-        mContext.createContextAsUser(UserHandle.ALL, 0)
-                .sendBroadcastMultiplePermissions(intent,
-                        new String[] { Manifest.permission.READ_PRIVILEGED_PHONE_STATE },
-                        new String[] { Manifest.permission.READ_PHONE_STATE });
+        // for all apps with READ_PRIVILEGED_PHONE_STATE but not READ_PHONE_STATE.
+        // Do this again twice, the first time for apps with ACCESS_FINE_LOCATION, then again with
+        // the location-sanitized service state for all apps without ACCESS_FINE_LOCATION.
+        // This ensures that any app holding either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE
+        // get this broadcast exactly once, and we are not exposing location without permission.
+        mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(intent,
+                new String[] {Manifest.permission.READ_PHONE_STATE,
+                        Manifest.permission.ACCESS_FINE_LOCATION});
+        mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(intent,
+                new String[] {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+                        Manifest.permission.ACCESS_FINE_LOCATION},
+                new String[] {Manifest.permission.READ_PHONE_STATE});
+
+        // Replace bundle with location-sanitized ServiceState
+        data = new Bundle();
+        state.createLocationInfoSanitizedCopy(true).fillInNotifierBundle(data);
+        intent.putExtras(data);
+        mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(intent,
+                new String[] {Manifest.permission.READ_PHONE_STATE},
+                new String[] {Manifest.permission.ACCESS_FINE_LOCATION});
+        mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(intent,
+                new String[] {Manifest.permission.READ_PRIVILEGED_PHONE_STATE},
+                new String[] {Manifest.permission.READ_PHONE_STATE,
+                        Manifest.permission.ACCESS_FINE_LOCATION});
     }
 
     private void broadcastSignalStrengthChanged(SignalStrength signalStrength, int phoneId,
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 6c1a00d..97dd323 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -1018,6 +1018,7 @@
                         Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
                         0,
                         mService.mUserController.getCurrentUserId()) != 0;
+                final String packageName = proc.info.packageName;
                 final boolean crashSilenced = mAppsNotReportingCrashes != null
                         && mAppsNotReportingCrashes.contains(proc.info.packageName);
                 final long now = SystemClock.uptimeMillis();
@@ -1026,6 +1027,7 @@
                 if ((mService.mAtmInternal.canShowErrorDialogs() || showBackground)
                         && !crashSilenced && !shouldThottle
                         && (showFirstCrash || showFirstCrashDevOption || data.repeating)) {
+                    Slog.i(TAG, "Showing crash dialog for package " + packageName + " u" + userId);
                     errState.getDialogController().showCrashDialogs(data);
                     if (!proc.isolated) {
                         mProcessCrashShowDialogTimes.put(proc.processName, proc.uid, now);
diff --git a/services/core/java/com/android/server/am/AppPermissionTracker.java b/services/core/java/com/android/server/am/AppPermissionTracker.java
index 7f48d52..69f70ca 100644
--- a/services/core/java/com/android/server/am/AppPermissionTracker.java
+++ b/services/core/java/com/android/server/am/AppPermissionTracker.java
@@ -64,6 +64,8 @@
     @GuardedBy("mLock")
     private SparseArray<ArraySet<String>> mUidGrantedPermissionsInMonitor = new SparseArray<>();
 
+    private volatile boolean mLockedBootCompleted = false;
+
     AppPermissionTracker(Context context, AppRestrictionController controller) {
         this(context, controller, null, null);
     }
@@ -85,20 +87,20 @@
         final PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
         final PermissionManagerServiceInternal pm = mInjector.getPermissionManagerServiceInternal();
         final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor();
+        final SparseArray<ArraySet<String>> uidPerms = mUidGrantedPermissionsInMonitor;
         for (int userId : allUsers) {
             final List<ApplicationInfo> apps = pmi.getInstalledApplications(0, userId, SYSTEM_UID);
             if (apps == null) {
                 continue;
             }
-            synchronized (mLock) {
-                final SparseArray<ArraySet<String>> uidPerms = mUidGrantedPermissionsInMonitor;
-                final long now = SystemClock.elapsedRealtime();
-                for (int i = 0, size = apps.size(); i < size; i++) {
-                    final ApplicationInfo ai = apps.get(i);
-                    for (String permission : permissions) {
-                        if (pm.checkUidPermission(ai.uid, permission) != PERMISSION_GRANTED) {
-                            continue;
-                        }
+            final long now = SystemClock.elapsedRealtime();
+            for (int i = 0, size = apps.size(); i < size; i++) {
+                final ApplicationInfo ai = apps.get(i);
+                for (String permission : permissions) {
+                    if (pm.checkUidPermission(ai.uid, permission) != PERMISSION_GRANTED) {
+                        continue;
+                    }
+                    synchronized (mLock) {
                         ArraySet<String> grantedPermissions = uidPerms.get(ai.uid);
                         if (grantedPermissions == null) {
                             grantedPermissions = new ArraySet<String>();
@@ -132,25 +134,30 @@
     private void handlePermissionsChanged(int uid) {
         final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor();
         if (permissions != null && permissions.length > 0) {
+            final PermissionManagerServiceInternal pm =
+                    mInjector.getPermissionManagerServiceInternal();
+            final boolean[] states = new boolean[permissions.length];
+            for (int i = 0; i < permissions.length; i++) {
+                states[i] = pm.checkUidPermission(uid, permissions[i]) == PERMISSION_GRANTED;
+                if (DEBUG_PERMISSION_TRACKER) {
+                    Slog.i(TAG, UserHandle.formatUid(uid) + " " + permissions[i] + "=" + states[i]);
+                }
+            }
             synchronized (mLock) {
-                handlePermissionsChangedLocked(uid);
+                handlePermissionsChangedLocked(uid, permissions, states);
             }
         }
     }
 
     @GuardedBy("mLock")
-    private void handlePermissionsChangedLocked(int uid) {
-        final PermissionManagerServiceInternal pm = mInjector.getPermissionManagerServiceInternal();
+    private void handlePermissionsChangedLocked(int uid, String[] permissions, boolean[] states) {
         final int index = mUidGrantedPermissionsInMonitor.indexOfKey(uid);
         ArraySet<String> grantedPermissions = index >= 0
                 ? mUidGrantedPermissionsInMonitor.valueAt(index) : null;
-        final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor();
         final long now = SystemClock.elapsedRealtime();
-        for (String permission: permissions) {
-            boolean granted = pm.checkUidPermission(uid, permission) == PERMISSION_GRANTED;
-            if (DEBUG_PERMISSION_TRACKER) {
-                Slog.i(TAG, UserHandle.formatUid(uid) + " " + permission + "=" + granted);
-            }
+        for (int i = 0; i < permissions.length; i++) {
+            final String permission = permissions[i];
+            final boolean granted = states[i];
             boolean changed = false;
             if (granted) {
                 if (grantedPermissions == null) {
@@ -200,6 +207,10 @@
     }
 
     private void onPermissionTrackerEnabled(boolean enabled) {
+        if (!mLockedBootCompleted) {
+            // Not ready, bail out.
+            return;
+        }
         final PermissionManager pm = mInjector.getPermissionManager();
         if (enabled) {
             pm.addOnPermissionsChangeListener(this);
@@ -211,6 +222,12 @@
     }
 
     @Override
+    void onLockedBootCompleted() {
+        mLockedBootCompleted = true;
+        onPermissionTrackerEnabled(mInjector.getPolicy().isEnabled());
+    }
+
+    @Override
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix);
         pw.println("APP PERMISSIONS TRACKER:");
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index 1129c19..2ffd487 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -71,6 +71,7 @@
 import static android.os.PowerExemptionManager.REASON_SYSTEM_UID;
 import static android.os.PowerExemptionManager.reasonCodeToString;
 import static android.os.Process.SYSTEM_UID;
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
 
 import static com.android.internal.notification.SystemNotificationChannels.ABUSIVE_BACKGROUND_APPS;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
@@ -792,7 +793,7 @@
         mInjector = injector;
         mContext = injector.getContext();
         mActivityManagerService = service;
-        mBgHandlerThread = new HandlerThread("bgres-controller");
+        mBgHandlerThread = new HandlerThread("bgres-controller", THREAD_PRIORITY_BACKGROUND);
         mBgHandlerThread.start();
         mBgHandler = new BgHandler(mBgHandlerThread.getLooper(), injector);
         mBgExecutor = new HandlerExecutor(mBgHandler);
@@ -816,9 +817,11 @@
         mInjector.getAppStandbyInternal().addListener(mAppIdleStateChangeListener);
         mInjector.getRoleManager().addOnRoleHoldersChangedListenerAsUser(mBgExecutor,
                 mRoleHolderChangedListener, UserHandle.ALL);
-        for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
-            mAppStateTrackers.get(i).onSystemReady();
-        }
+        mInjector.scheduleInitTrackers(mBgHandler, () -> {
+            for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
+                mAppStateTrackers.get(i).onSystemReady();
+            }
+        });
     }
 
     @VisibleForTesting
@@ -2137,6 +2140,10 @@
             }
             return null;
         }
+
+        void scheduleInitTrackers(Handler handler, Runnable initializers) {
+            handler.post(initializers);
+        }
     }
 
     private void registerForSystemBroadcasts() {
@@ -2221,6 +2228,21 @@
         userFilter.addAction(Intent.ACTION_USER_REMOVED);
         userFilter.addAction(Intent.ACTION_UID_REMOVED);
         mContext.registerReceiverForAllUsers(broadcastReceiver, userFilter, null, mBgHandler);
+        final BroadcastReceiver bootReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final String action = intent.getAction();
+                switch (intent.getAction()) {
+                    case Intent.ACTION_LOCKED_BOOT_COMPLETED: {
+                        onLockedBootCompleted();
+                    } break;
+                }
+            }
+        };
+        final IntentFilter bootFilter = new IntentFilter();
+        bootFilter.addAction(Intent.ACTION_LOCKED_BOOT_COMPLETED);
+        mContext.registerReceiverAsUser(bootReceiver, UserHandle.SYSTEM,
+                bootFilter, null, mBgHandler);
     }
 
     void forEachTracker(Consumer<BaseAppStateTracker> sink) {
@@ -2275,6 +2297,12 @@
         mRestrictionSettings.removeUid(uid);
     }
 
+    private void onLockedBootCompleted() {
+        for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
+            mAppStateTrackers.get(i).onLockedBootCompleted();
+        }
+    }
+
     boolean isBgAutoRestrictedBucketFeatureFlagEnabled() {
         return mConstantsObserver.mBgAutoRestrictedBucket;
     }
diff --git a/services/core/java/com/android/server/am/BaseAppStateTracker.java b/services/core/java/com/android/server/am/BaseAppStateTracker.java
index 482d697..0fada53 100644
--- a/services/core/java/com/android/server/am/BaseAppStateTracker.java
+++ b/services/core/java/com/android/server/am/BaseAppStateTracker.java
@@ -204,6 +204,12 @@
     }
 
     /**
+     * Called when the system sends LOCKED_BOOT_COMPLETED.
+     */
+    void onLockedBootCompleted() {
+    }
+
+    /**
      * Called when a device config property in the activity manager namespace
      * has changed.
      */
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 9929ed7..86ca699 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -1115,6 +1115,9 @@
                     int lastOomAdj = msg.arg1;
                     int procState = msg.arg2;
                     synchronized (mProcLock) {
+                        if(mPendingCompactionProcesses.isEmpty()) {
+                            return;
+                        }
                         proc = mPendingCompactionProcesses.remove(0);
                         opt = proc.mOptRecord;
 
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 807293f..0b9fb1a 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3330,6 +3330,13 @@
         }
     }
 
+    private void enforceAccessUltrasoundPermission() {
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.ACCESS_ULTRASOUND)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Missing ACCESS_ULTRASOUND permission");
+        }
+    }
+
     private void enforceQueryStatePermission() {
         if (mContext.checkCallingOrSelfPermission(Manifest.permission.QUERY_AUDIO_STATE)
                 != PackageManager.PERMISSION_GRANTED) {
@@ -3462,6 +3469,12 @@
                 attributionTag, Binder.getCallingUid(), callingOrSelfHasAudioSettingsPermission());
     }
 
+    /** @see AudioManager#isUltrasoundSupported() */
+    public boolean isUltrasoundSupported() {
+        enforceAccessUltrasoundPermission();
+        return AudioSystem.isUltrasoundSupported();
+    }
+
     private boolean canChangeAccessibilityVolume() {
         synchronized (mAccessibilityServiceUidsLock) {
             if (PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 2c2a2bf..17215e5 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -131,6 +131,7 @@
     private static final int MSG_STOP_SENSOR_LISTENER = 2;
     private static final int MSG_START_SENSOR_LISTENER = 3;
     private static final int MSG_BRIGHTNESS_CONFIG_CHANGED = 4;
+    private static final int MSG_SENSOR_CHANGED = 5;
 
     private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
 
@@ -158,6 +159,7 @@
     // These members should only be accessed on the mBgHandler thread.
     private BroadcastReceiver mBroadcastReceiver;
     private SensorListener mSensorListener;
+    private Sensor mLightSensor;
     private SettingsObserver mSettingsObserver;
     private DisplayListener mDisplayListener;
     private boolean mSensorRegistered;
@@ -327,6 +329,14 @@
         m.sendToTarget();
     }
 
+    /**
+     * Updates the light sensor to use.
+     */
+    public void setLightSensor(Sensor lightSensor) {
+        mBgHandler.obtainMessage(MSG_SENSOR_CHANGED, 0 /*unused*/, 0/*unused*/, lightSensor)
+                .sendToTarget();
+    }
+
     private void handleBrightnessChanged(float brightness, boolean userInitiated,
             float powerBrightnessFactor, boolean isUserSetBrightness,
             boolean isDefaultBrightnessConfig, long timestamp, String uniqueDisplayId) {
@@ -428,13 +438,28 @@
         }
     }
 
+    private void handleSensorChanged(Sensor lightSensor) {
+        if (mLightSensor != lightSensor) {
+            mLightSensor = lightSensor;
+            stopSensorListener();
+            synchronized (mDataCollectionLock) {
+                mLastSensorReadings.clear();
+            }
+            // Attempt to restart the sensor listener. It will check to see if it should be running
+            // so there is no need to also check here.
+            startSensorListener();
+        }
+    }
+
     private void startSensorListener() {
         if (!mSensorRegistered
+                && mLightSensor != null
+                && mAmbientBrightnessStatsTracker != null
                 && mInjector.isInteractive(mContext)
                 && mInjector.isBrightnessModeAutomatic(mContentResolver)) {
             mAmbientBrightnessStatsTracker.start();
             mSensorRegistered = true;
-            mInjector.registerSensorListener(mContext, mSensorListener,
+            mInjector.registerSensorListener(mContext, mSensorListener, mLightSensor,
                     mInjector.getBackgroundHandler());
         }
     }
@@ -736,6 +761,7 @@
         pw.println("BrightnessTracker state:");
         synchronized (mDataCollectionLock) {
             pw.println("  mStarted=" + mStarted);
+            pw.println("  mLightSensor=" + mLightSensor);
             pw.println("  mLastBatteryLevel=" + mLastBatteryLevel);
             pw.println("  mLastBrightness=" + mLastBrightness);
             pw.println("  mLastSensorReadings.size=" + mLastSensorReadings.size());
@@ -1017,6 +1043,9 @@
                         disableColorSampling();
                     }
                     break;
+                case MSG_SENSOR_CHANGED:
+                    handleSensorChanged((Sensor) msg.obj);
+                    break;
 
             }
         }
@@ -1045,9 +1074,8 @@
     @VisibleForTesting
     static class Injector {
         public void registerSensorListener(Context context,
-                SensorEventListener sensorListener, Handler handler) {
+                SensorEventListener sensorListener, Sensor lightSensor, Handler handler) {
             SensorManager sensorManager = context.getSystemService(SensorManager.class);
-            Sensor lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
             sensorManager.registerListener(sensorListener,
                     lightSensor, SensorManager.SENSOR_DELAY_NORMAL, handler);
         }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 418e91d..9067f2e 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -826,7 +826,6 @@
     private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info) {
         // All properties that depend on the associated DisplayDevice and the DDC must be
         // updated here.
-        loadAmbientLightSensor();
         loadBrightnessRampRates();
         loadProximitySensor();
         loadNitsRange(mContext.getResources());
@@ -972,6 +971,9 @@
             }
 
             loadAmbientLightSensor();
+            if (mBrightnessTracker != null) {
+                mBrightnessTracker.setLightSensor(mLightSensor);
+            }
 
             if (mAutomaticBrightnessController != null) {
                 mAutomaticBrightnessController.stop();
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 39ee0f4..9b10058 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -40,6 +40,7 @@
 import android.os.ServiceManager;
 import android.os.Trace;
 import android.sysprop.ApexProperties;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.PrintWriterPrinter;
@@ -608,18 +609,21 @@
                             continue;
                         }
 
-                        String name = service.getName();
-                        for (ApexSystemServiceInfo info : mApexSystemServices) {
-                            if (info.getName().equals(name)) {
-                                throw new IllegalStateException(String.format(
-                                        "Duplicate apex-system-service %s from %s, %s",
-                                        name, info.mJarPath, service.getJarPath()));
+                        if (ai.isActive) {
+                            String name = service.getName();
+                            for (int j = 0; j < mApexSystemServices.size(); j++) {
+                                ApexSystemServiceInfo info = mApexSystemServices.get(j);
+                                if (info.getName().equals(name)) {
+                                    throw new IllegalStateException(TextUtils.formatSimple(
+                                            "Duplicate apex-system-service %s from %s, %s", name,
+                                            info.mJarPath, service.getJarPath()));
+                                }
                             }
+                            ApexSystemServiceInfo info = new ApexSystemServiceInfo(
+                                    service.getName(), service.getJarPath(),
+                                    service.getInitOrder());
+                            mApexSystemServices.add(info);
                         }
-
-                        ApexSystemServiceInfo info = new ApexSystemServiceInfo(
-                                service.getName(), service.getJarPath(), service.getInitOrder());
-                        mApexSystemServices.add(info);
                     }
                     Collections.sort(mApexSystemServices);
                     mPackageNameToApexModuleName.put(packageInfo.packageName, ai.moduleName);
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 8a105dd..70a030d 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -2684,8 +2684,8 @@
     @GuardedBy("mLock")
     private void updateUserActivitySummaryLocked(long now, int dirty) {
         // Update the status of the user activity timeout timer.
-        if ((dirty & (DIRTY_DISPLAY_GROUP_WAKEFULNESS | DIRTY_WAKE_LOCKS
-                | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) == 0) {
+        if ((dirty & (DIRTY_DISPLAY_GROUP_WAKEFULNESS | DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY
+                | DIRTY_WAKEFULNESS | DIRTY_SETTINGS | DIRTY_ATTENTIVE)) == 0) {
             return;
         }
         mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT);
@@ -2772,6 +2772,11 @@
                             screenDimDuration);
                 }
 
+                if (isAttentiveTimeoutExpired(powerGroup, now)) {
+                    groupUserActivitySummary = 0;
+                    groupNextTimeout = -1;
+                }
+
                 hasUserActivitySummary |= groupUserActivitySummary != 0;
 
                 if (nextTimeout == -1) {
@@ -3127,7 +3132,7 @@
                     Message msg = mHandler.obtainMessage(MSG_SANDMAN);
                     msg.arg1 = powerGroup.getGroupId();
                     msg.setAsynchronous(true);
-                    mHandler.sendMessage(msg);
+                    mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
                 }
             }
         }
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 33b807b..9893f68 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -106,12 +106,6 @@
         synchronized (task.mWmService.mGlobalLock) {
             activityRecord = task.topRunningActivity();
 
-            if(!activityRecord.info.applicationInfo.isOnBackInvokedCallbackEnabled()) {
-                ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Activity %s: enableOnBackInvokedCallback=false."
-                        + " Returning null BackNavigationInfo.", activityRecord.getName());
-                return null;
-            }
-
             removedWindowContainer = activityRecord;
             taskWindowConfiguration = task.getTaskInfo().configuration.windowConfiguration;
             WindowState window = task.getWindow(WindowState::isFocused);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
index 936940f..e6bb0ce 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
@@ -2648,6 +2648,11 @@
         AppPermissionTracker getAppPermissionTracker() {
             return mAppPermissionTracker;
         }
+
+        @Override
+        void scheduleInitTrackers(Handler handler, Runnable initializers) {
+            initializers.run();
+        }
     }
 
     private class TestBaseTrackerInjector<T extends BaseAppStatePolicy>
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index bdf94f3..356600d 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -33,6 +33,7 @@
 import android.content.IntentFilter;
 import android.content.pm.ParceledListSlice;
 import android.database.ContentObserver;
+import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.display.AmbientBrightnessDayStats;
@@ -42,6 +43,7 @@
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayedContentSample;
 import android.hardware.display.DisplayedContentSamplingAttributes;
+import android.hardware.input.InputSensorInfo;
 import android.os.BatteryManager;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -63,6 +65,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
@@ -84,8 +88,11 @@
     private static final String DEFAULT_DISPLAY_ID = "123";
     private static final float FLOAT_DELTA = 0.01f;
 
+    @Mock private InputSensorInfo mInputSensorInfoMock;
+
     private BrightnessTracker mTracker;
     private TestInjector mInjector;
+    private Sensor mLightSensorFake;
 
     private static Object sHandlerLock = new Object();
     private static Handler sHandler;
@@ -108,9 +115,12 @@
 
     @Before
     public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
         mInjector = new TestInjector(ensureHandler());
+        mLightSensorFake = new Sensor(mInputSensorInfoMock);
 
         mTracker = new BrightnessTracker(InstrumentationRegistry.getContext(), mInjector);
+        mTracker.setLightSensor(mLightSensorFake);
         mDefaultNightModeColorTemperature =
                 InstrumentationRegistry.getContext().getResources().getInteger(
                 R.integer.config_nightDisplayColorTemperatureDefault);
@@ -834,6 +844,47 @@
         mTracker.stop();
     }
 
+    @Test
+    public void testLightSensorChange() {
+        // verify the tracker started correctly and a listener registered
+        startTracker(mTracker);
+        assertNotNull(mInjector.mSensorListener);
+        assertEquals(mInjector.mLightSensor, mLightSensorFake);
+
+        // Setting the sensor to null should stop the registered listener.
+        mTracker.setLightSensor(null);
+        mInjector.waitForHandler();
+        assertNull(mInjector.mSensorListener);
+        assertNull(mInjector.mLightSensor);
+
+        // Resetting sensor should start listener again
+        mTracker.setLightSensor(mLightSensorFake);
+        mInjector.waitForHandler();
+        assertNotNull(mInjector.mSensorListener);
+        assertEquals(mInjector.mLightSensor, mLightSensorFake);
+
+        Sensor secondSensor = new Sensor(mInputSensorInfoMock);
+        // Setting a different listener should keep things working
+        mTracker.setLightSensor(secondSensor);
+        mInjector.waitForHandler();
+        assertNotNull(mInjector.mSensorListener);
+        assertEquals(mInjector.mLightSensor, secondSensor);
+    }
+
+    @Test
+    public void testSetLightSensorDoesntStartListener() {
+        mTracker.setLightSensor(mLightSensorFake);
+        assertNull(mInjector.mSensorListener);
+    }
+
+    @Test
+    public void testNullLightSensorWontRegister() {
+        mTracker.setLightSensor(null);
+        startTracker(mTracker);
+        assertNull(mInjector.mSensorListener);
+        assertNull(mInjector.mLightSensor);
+    }
+
     private InputStream getInputStream(String data) {
         return new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
     }
@@ -924,6 +975,7 @@
 
     private class TestInjector extends BrightnessTracker.Injector {
         SensorEventListener mSensorListener;
+        Sensor mLightSensor;
         BroadcastReceiver mBroadcastReceiver;
         DisplayManager.DisplayListener mDisplayListener;
         Map<String, Integer> mSecureIntSettings = new HashMap<>();
@@ -974,14 +1026,16 @@
 
         @Override
         public void registerSensorListener(Context context,
-                SensorEventListener sensorListener, Handler handler) {
+                SensorEventListener sensorListener, Sensor lightSensor, Handler handler) {
             mSensorListener = sensorListener;
+            mLightSensor = lightSensor;
         }
 
         @Override
         public void unregisterSensorListener(Context context,
                 SensorEventListener sensorListener) {
             mSensorListener = null;
+            mLightSensor = null;
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
index 2f5993d1..5d3da43 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -132,7 +132,12 @@
 
     @Test
     public void testGetApexSystemServices() throws RemoteException {
-        when(mApexService.getAllPackages()).thenReturn(createApexInfoForTestPkg(true, false));
+        when(mApexService.getAllPackages()).thenReturn(new ApexInfo[] {
+                createApexInfoForTestPkg(false, true, 1),
+                // only active apex reports apex-system-service
+                createApexInfoForTestPkg(true, false, 2),
+        });
+
         mApexManager.scanApexPackagesTraced(mPackageParser2,
                 ParallelPackageParser.makeExecutorService());
 
@@ -484,17 +489,20 @@
         assertThat(e).hasMessageThat().contains("Failed to collect certificates from ");
     }
 
-    private ApexInfo[] createApexInfoForTestPkg(boolean isActive, boolean isFactory) {
+    private ApexInfo createApexInfoForTestPkg(boolean isActive, boolean isFactory, int version) {
         File apexFile = extractResource(TEST_APEX_PKG,  TEST_APEX_FILE_NAME);
         ApexInfo apexInfo = new ApexInfo();
         apexInfo.isActive = isActive;
         apexInfo.isFactory = isFactory;
         apexInfo.moduleName = TEST_APEX_PKG;
         apexInfo.modulePath = apexFile.getPath();
-        apexInfo.versionCode = 191000070;
+        apexInfo.versionCode = version;
         apexInfo.preinstalledModulePath = apexFile.getPath();
+        return apexInfo;
+    }
 
-        return new ApexInfo[]{apexInfo};
+    private ApexInfo[] createApexInfoForTestPkg(boolean isActive, boolean isFactory) {
+        return new ApexInfo[]{createApexInfoForTestPkg(isActive, isFactory, 191000070)};
     }
 
     private ApexInfo createApexInfo(String moduleName, int versionCode, boolean isActive,
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 c94168c..ed7aac7 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -1058,6 +1058,23 @@
         assertThat(mService.getGlobalWakefulnessLocked()).isNotEqualTo(WAKEFULNESS_ASLEEP);
     }
 
+
+    @SuppressWarnings("GuardedBy")
+    @Test
+    public void testInattentiveSleep_goesToSleepFromDream() throws Exception {
+        setAttentiveTimeout(20000);
+        createService();
+        startSystem();
+        setPluggedIn(true);
+        forceAwake();
+        forceDream();
+        when(mDreamManagerInternalMock.isDreaming()).thenReturn(true);
+        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DREAMING);
+
+        advanceTime(20500);
+        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
+    }
+
     @Test
     public void testWakeLock_affectsProperDisplayGroup() {
         final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
diff --git a/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java b/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
index 27e8d69..ab8f69b 100644
--- a/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
+++ b/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
@@ -22,6 +22,7 @@
 
 import android.annotation.ElapsedRealtimeLong;
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -39,6 +40,8 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
 
 class BroadcastResponseStatsTracker {
     private static final String TAG = "ResponseStatsTracker";
@@ -172,28 +175,27 @@
         }
     }
 
-    @NonNull BroadcastResponseStats queryBroadcastResponseStats(int callingUid,
-            @NonNull String packageName, long id, @UserIdInt int userId) {
-        final BroadcastResponseStats aggregatedResponseStats =
-                new BroadcastResponseStats(packageName);
+    @NonNull List<BroadcastResponseStats> queryBroadcastResponseStats(int callingUid,
+            @Nullable String packageName, @IntRange(from = 0) long id, @UserIdInt int userId) {
+        final List<BroadcastResponseStats> broadcastResponseStatsList = new ArrayList<>();
         synchronized (mLock) {
             final SparseArray<UserBroadcastResponseStats> responseStatsForCaller =
                     mUserResponseStats.get(callingUid);
             if (responseStatsForCaller == null) {
-                return aggregatedResponseStats;
+                return broadcastResponseStatsList;
             }
             final UserBroadcastResponseStats responseStatsForUser =
                     responseStatsForCaller.get(userId);
             if (responseStatsForUser == null) {
-                return aggregatedResponseStats;
+                return broadcastResponseStatsList;
             }
-            responseStatsForUser.aggregateBroadcastResponseStats(aggregatedResponseStats,
-                    packageName, id);
+            responseStatsForUser.populateAllBroadcastResponseStats(
+                    broadcastResponseStatsList, packageName, id);
         }
-        return aggregatedResponseStats;
+        return broadcastResponseStatsList;
     }
 
-    void clearBroadcastResponseStats(int callingUid, @NonNull String packageName, long id,
+    void clearBroadcastResponseStats(int callingUid, @Nullable String packageName, long id,
             @UserIdInt int userId) {
         synchronized (mLock) {
             final SparseArray<UserBroadcastResponseStats> responseStatsForCaller =
@@ -210,6 +212,16 @@
         }
     }
 
+    void clearBroadcastEvents(int callingUid, @UserIdInt int userId) {
+        synchronized (mLock) {
+            final UserBroadcastEvents userBroadcastEvents = mUserBroadcastEvents.get(userId);
+            if (userBroadcastEvents == null) {
+                return;
+            }
+            userBroadcastEvents.clear(callingUid);
+        }
+    }
+
     void onUserRemoved(@UserIdInt int userId) {
         synchronized (mLock) {
             mUserBroadcastEvents.remove(userId);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 4a761a7..06aa8f0 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -49,7 +49,7 @@
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.usage.AppLaunchEstimateInfo;
 import android.app.usage.AppStandbyInfo;
-import android.app.usage.BroadcastResponseStats;
+import android.app.usage.BroadcastResponseStatsList;
 import android.app.usage.ConfigurationStats;
 import android.app.usage.EventStats;
 import android.app.usage.IUsageStatsManager;
@@ -2686,16 +2686,15 @@
 
         @Override
         @NonNull
-        public BroadcastResponseStats queryBroadcastResponseStats(
-                @NonNull String packageName,
-                @IntRange(from = 1) long id,
+        public BroadcastResponseStatsList queryBroadcastResponseStats(
+                @Nullable String packageName,
+                @IntRange(from = 0) long id,
                 @NonNull String callingPackage,
                 @UserIdInt int userId) {
-            Objects.requireNonNull(packageName);
             Objects.requireNonNull(callingPackage);
             // TODO: Move to Preconditions utility class
-            if (id <= 0) {
-                throw new IllegalArgumentException("id needs to be >0");
+            if (id < 0) {
+                throw new IllegalArgumentException("id needs to be >=0");
             }
 
             final int callingUid = Binder.getCallingUid();
@@ -2708,8 +2707,9 @@
             userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), callingUid,
                     userId, false /* allowAll */, false /* requireFull */,
                     "queryBroadcastResponseStats" /* name */, callingPackage);
-            return mResponseStatsTracker.queryBroadcastResponseStats(
-                    callingUid, packageName, id, userId);
+            return new BroadcastResponseStatsList(
+                    mResponseStatsTracker.queryBroadcastResponseStats(
+                            callingUid, packageName, id, userId));
         }
 
         @Override
@@ -2718,10 +2718,9 @@
                 @IntRange(from = 1) long id,
                 @NonNull String callingPackage,
                 @UserIdInt int userId) {
-            Objects.requireNonNull(packageName);
             Objects.requireNonNull(callingPackage);
-            if (id <= 0) {
-                throw new IllegalArgumentException("id needs to be >0");
+            if (id < 0) {
+                throw new IllegalArgumentException("id needs to be >=0");
             }
 
             final int callingUid = Binder.getCallingUid();
@@ -2737,6 +2736,23 @@
             mResponseStatsTracker.clearBroadcastResponseStats(callingUid,
                     packageName, id, userId);
         }
+
+        @Override
+        public void clearBroadcastEvents(@NonNull String callingPackage, @UserIdInt int userId) {
+            Objects.requireNonNull(callingPackage);
+
+            final int callingUid = Binder.getCallingUid();
+            if (!hasPermission(callingPackage)) {
+                throw new SecurityException(
+                        "Caller does not have the permission needed to call this API; "
+                                + "callingPackage=" + callingPackage
+                                + ", callingUid=" + callingUid);
+            }
+            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), callingUid,
+                    userId, false /* allowAll */, false /* requireFull */,
+                    "clearBroadcastResponseStats" /* name */, callingPackage);
+            mResponseStatsTracker.clearBroadcastEvents(callingUid, userId);
+        }
     }
 
     void registerAppUsageObserver(int callingUid, int observerId, String[] packages,
diff --git a/services/usage/java/com/android/server/usage/UserBroadcastEvents.java b/services/usage/java/com/android/server/usage/UserBroadcastEvents.java
index 819644846..0ec59c3 100644
--- a/services/usage/java/com/android/server/usage/UserBroadcastEvents.java
+++ b/services/usage/java/com/android/server/usage/UserBroadcastEvents.java
@@ -51,6 +51,10 @@
     }
 
     void onUidRemoved(int uid) {
+        clear(uid);
+    }
+
+    void clear(int uid) {
         for (int i = mBroadcastEvents.size() - 1; i >= 0; --i) {
             final LongSparseArray<BroadcastEvent> broadcastEvents = mBroadcastEvents.valueAt(i);
             for (int j = broadcastEvents.size() - 1; j >= 0; --j) {
diff --git a/services/usage/java/com/android/server/usage/UserBroadcastResponseStats.java b/services/usage/java/com/android/server/usage/UserBroadcastResponseStats.java
index ac2a320..1828a71 100644
--- a/services/usage/java/com/android/server/usage/UserBroadcastResponseStats.java
+++ b/services/usage/java/com/android/server/usage/UserBroadcastResponseStats.java
@@ -16,6 +16,7 @@
 
 package com.android.server.usage;
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.usage.BroadcastResponseStats;
@@ -23,6 +24,8 @@
 
 import com.android.internal.util.IndentingPrintWriter;
 
+import java.util.List;
+
 class UserBroadcastResponseStats {
     /**
      * Contains the mapping of a BroadcastEvent type to it's aggregated stats.
@@ -39,31 +42,38 @@
             BroadcastEvent broadcastEvent) {
         BroadcastResponseStats responseStats = mResponseStats.get(broadcastEvent);
         if (responseStats == null) {
-            responseStats = new BroadcastResponseStats(broadcastEvent.getTargetPackage());
+            responseStats = new BroadcastResponseStats(broadcastEvent.getTargetPackage(),
+                    broadcastEvent.getIdForResponseEvent());
             mResponseStats.put(broadcastEvent, responseStats);
         }
         return responseStats;
     }
 
-    void aggregateBroadcastResponseStats(
-            @NonNull BroadcastResponseStats responseStats,
-            @NonNull String packageName, long id) {
+    void populateAllBroadcastResponseStats(
+            @NonNull List<BroadcastResponseStats> broadcastResponseStatsList,
+            @Nullable String packageName, @IntRange(from = 0) long id) {
         for (int i = mResponseStats.size() - 1; i >= 0; --i) {
             final BroadcastEvent broadcastEvent = mResponseStats.keyAt(i);
-            if (broadcastEvent.getIdForResponseEvent() == id
-                    && broadcastEvent.getTargetPackage().equals(packageName)) {
-                responseStats.addCounts(mResponseStats.valueAt(i));
+            if (id != 0 && id != broadcastEvent.getIdForResponseEvent()) {
+                continue;
             }
+            if (packageName != null && !packageName.equals(broadcastEvent.getTargetPackage())) {
+                continue;
+            }
+            broadcastResponseStatsList.add(mResponseStats.valueAt(i));
         }
     }
 
-    void clearBroadcastResponseStats(@NonNull String packageName, long id) {
+    void clearBroadcastResponseStats(@Nullable String packageName, @IntRange(from = 0) long id) {
         for (int i = mResponseStats.size() - 1; i >= 0; --i) {
             final BroadcastEvent broadcastEvent = mResponseStats.keyAt(i);
-            if (broadcastEvent.getIdForResponseEvent() == id
-                    && broadcastEvent.getTargetPackage().equals(packageName)) {
-                mResponseStats.removeAt(i);
+            if (id != 0 && id != broadcastEvent.getIdForResponseEvent()) {
+                continue;
             }
+            if (packageName != null && !packageName.equals(broadcastEvent.getTargetPackage())) {
+                continue;
+            }
+            mResponseStats.removeAt(i);
         }
     }
 
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 3d00474..eb3df1c 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -8226,7 +8226,9 @@
             "store_sim_pin_for_unattended_reboot_bool";
 
     /**
-     * Determine whether "Enable 2G" toggle can be shown.
+     * Allow whether the user can use the "Allow 2G" toggle in Settings.
+     *
+     * If {@code true} then the toggle is disabled (i.e. grayed out).
      *
      * Used to trade privacy/security against potentially reduced carrier coverage for some
      * carriers.
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index a49a61b5..b6ae530 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -25,15 +25,20 @@
 import android.annotation.SystemApi;
 import android.app.Activity;
 import android.app.PendingIntent;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.pm.PackageManager;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.telephony.SubscriptionInfo;
 import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.TelephonyManager;
 import android.telephony.euicc.EuiccCardManager.ResetOption;
+import android.util.Log;
 
 import com.android.internal.telephony.euicc.IEuiccController;
 
@@ -57,6 +62,7 @@
  */
 @RequiresFeature(PackageManager.FEATURE_TELEPHONY_EUICC)
 public class EuiccManager {
+    private static final String TAG = "EuiccManager";
 
     /**
      * Intent action to launch the embedded SIM (eUICC) management settings screen.
@@ -811,6 +817,14 @@
      */
     public static final int ERROR_INVALID_PORT = 10017;
 
+    /**
+     * Apps targeting on Android T and beyond will get exception whenever switchToSubscription
+     * without portIndex is called for disable subscription.
+     * @hide
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    public static final long SWITCH_WITHOUT_PORT_INDEX_EXCEPTION_ON_DISABLE = 218393363L;
 
     private final Context mContext;
     private int mCardId;
@@ -1127,7 +1141,7 @@
      * intent to prompt the user to accept the download. The caller should also be authorized to
      * manage the subscription to be enabled.
      *
-     * <p> From Android T, devices might support MEP(Multiple Enabled Profile), the subscription
+     * <p> From Android T, devices might support MEP(Multiple Enabled Profiles), the subscription
      * can be installed on different port from the eUICC. Calling apps with carrier privilege
      * (see {@link TelephonyManager#hasCarrierPrivileges}) over the currently active subscriptions
      * can use {@link #switchToSubscription(int, int, PendingIntent)} to specify which port to
@@ -1138,10 +1152,12 @@
      *
      * @param subscriptionId the ID of the subscription to enable. May be
      *     {@link android.telephony.SubscriptionManager#INVALID_SUBSCRIPTION_ID} to deactivate the
-     *     current profile without activating another profile to replace it. If it's a disable
-     *     operation, requires the {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS}
-     *     permission, or the calling app must be authorized to manage the active subscription on
-     *     the target eUICC.
+     *     current profile without activating another profile to replace it. Calling apps targeting
+     *     on android T must use {@link #switchToSubscription(int, int, PendingIntent)} API for
+     *     disable profile, port index can be found from {@link SubscriptionInfo#getPortIndex()}.
+     *     If it's a disable operation, requires the
+     *     {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission, or the
+     *     calling app must be authorized to manage the active subscription on the target eUICC.
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      */
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
@@ -1151,6 +1167,18 @@
             return;
         }
         try {
+            // TODO: Uncomment below compat change code once callers are ported to use
+            //  switchToSubscription with portIndex for disable operation.
+            // if (subscriptionId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
+            //        && getIEuiccController().isCompatChangeEnabled(mContext.getOpPackageName(),
+            //        SWITCH_WITHOUT_PORT_INDEX_EXCEPTION_ON_DISABLE)) {
+            //    // Apps targeting on Android T and beyond will get exception whenever
+            //    // switchToSubscription without portIndex is called with INVALID_SUBSCRIPTION_ID.
+            //    Log.e(TAG, "switchToSubscription without portIndex is not allowed for"
+            //            + " disable operation");
+            //    throw new IllegalArgumentException("Must use switchToSubscription with portIndex"
+            //            + " API for disable operation");
+            // }
             getIEuiccController().switchToSubscription(mCardId,
                     subscriptionId, mContext.getOpPackageName(), callbackIntent);
         } catch (RemoteException e) {
@@ -1180,7 +1208,10 @@
      *     current profile without activating another profile to replace it. If it's a disable
      *     operation, requires the {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS}
      *     permission, or the calling app must be authorized to manage the active subscription on
-     *     the target eUICC.
+     *     the target eUICC. From Android T, multiple enabled profiles is supported. Calling apps
+     *     targeting on android T must use {@link #switchToSubscription(int, int, PendingIntent)}
+     *     API for disable profile, port index can be found from
+     *     {@link SubscriptionInfo#getPortIndex()}.
      * @param portIndex the index of the port to target for the enabled subscription
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      */
@@ -1192,6 +1223,17 @@
             return;
         }
         try {
+            boolean canWriteEmbeddedSubscriptions = mContext.checkCallingOrSelfPermission(
+                    Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+                    == PackageManager.PERMISSION_GRANTED;
+            // If the caller is not privileged caller and does not have the carrier privilege over
+            // any active subscription, do not continue.
+            if (!canWriteEmbeddedSubscriptions && !getIEuiccController()
+                    .hasCarrierPrivilegesForPackageOnAnyPhone(mContext.getOpPackageName())) {
+                Log.e(TAG, "Not permitted to use switchToSubscription with portIndex");
+                throw new SecurityException(
+                        "Must have carrier privileges to use switchToSubscription with portIndex");
+            }
             getIEuiccController().switchToSubscriptionWithPort(mCardId,
                     subscriptionId, portIndex, mContext.getOpPackageName(), callbackIntent);
         } catch (RemoteException e) {
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
index dda95b1..19f1a5b 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
@@ -55,4 +55,6 @@
     List<String> getSupportedCountries(boolean isSupported);
     boolean isSupportedCountry(String countryIso);
     boolean isSimPortAvailable(int cardId, int portIndex, String callingPackage);
+    boolean hasCarrierPrivilegesForPackageOnAnyPhone(String callingPackage);
+    boolean isCompatChangeEnabled(String callingPackage, long changeId);
 }
diff --git a/tests/Internal/src/com/android/internal/util/UserIconsTest.java b/tests/Internal/src/com/android/internal/util/UserIconsTest.java
new file mode 100644
index 0000000..cc7b20b
--- /dev/null
+++ b/tests/Internal/src/com/android/internal/util/UserIconsTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 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.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class UserIconsTest {
+
+    @Test
+    public void convertToBitmapAtUserIconSize_sizeIsCorrect() {
+        Resources res = InstrumentationRegistry.getTargetContext().getResources();
+        Drawable icon = UserIcons.getDefaultUserIcon(res, 0, true);
+        Bitmap bitmap = UserIcons.convertToBitmapAtUserIconSize(res, icon);
+        int expectedSize = res.getDimensionPixelSize(R.dimen.user_icon_size);
+
+        assertThat(bitmap.getWidth()).isEqualTo(expectedSize);
+        assertThat(bitmap.getHeight()).isEqualTo(expectedSize);
+    }
+
+}