Merge "OPS_MIC refactoring" into main
diff --git a/api/api.go b/api/api.go
index a003aba..e09be03 100644
--- a/api/api.go
+++ b/api/api.go
@@ -146,7 +146,7 @@
 	metalavaCmd := "$(location metalava)"
 	// Silence reflection warnings. See b/168689341
 	metalavaCmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
-	metalavaCmd += " --quiet --no-banner --format=v2 "
+	metalavaCmd += " --quiet merge-signatures --format=v2 "
 
 	filename := txt.TxtFilename
 	if txt.Scope != "public" {
@@ -156,7 +156,7 @@
 	props.Name = proptools.StringPtr(ctx.ModuleName() + "-" + filename)
 	props.Tools = []string{"metalava"}
 	props.Out = []string{filename}
-	props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --api $(out)")
+	props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --out $(out)")
 	props.Srcs = append([]string{txt.BaseTxt}, createSrcs(txt.Modules, txt.ModuleTag)...)
 	props.Dists = []android.Dist{
 		{
diff --git a/core/java/android/os/PowerMonitor.java b/core/java/android/os/PowerMonitor.java
index ebdd463..5fb0df7 100644
--- a/core/java/android/os/PowerMonitor.java
+++ b/core/java/android/os/PowerMonitor.java
@@ -23,6 +23,10 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
+ * A PowerMonitor represents either a Channel aka ODPM rail (on-device power monitor) or an
+ * EnergyConsumer, as defined in
+ * <a href="https://android.googlesource.com/platform/hardware/interfaces/+/refs/heads/main/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats">android.hardware.power.stats</a>
+ *
  * @hide
  */
 public final class PowerMonitor implements Parcelable {
@@ -92,6 +96,7 @@
         return 0;
     }
 
+    @NonNull
     public static final Creator<PowerMonitor> CREATOR = new Creator<>() {
         @Override
         public PowerMonitor createFromParcel(@NonNull Parcel in) {
diff --git a/core/java/android/os/PowerMonitorReadings.java b/core/java/android/os/PowerMonitorReadings.java
index 3d7f859..e767059 100644
--- a/core/java/android/os/PowerMonitorReadings.java
+++ b/core/java/android/os/PowerMonitorReadings.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.annotation.ElapsedRealtimeLong;
 import android.annotation.NonNull;
 
 import java.util.Arrays;
@@ -43,8 +44,8 @@
      * @param powerMonitors array of power monitor (ODPM) rails, sorted by PowerMonitor.index
      * @hide
      */
-    public PowerMonitorReadings(PowerMonitor[] powerMonitors,
-            long[] energyUws, long[] timestampsMs) {
+    public PowerMonitorReadings(@NonNull PowerMonitor[] powerMonitors,
+            @NonNull long[] energyUws, @NonNull long[] timestampsMs) {
         mPowerMonitors = powerMonitors;
         mEnergyUws = energyUws;
         mTimestampsMs = timestampsMs;
@@ -55,7 +56,7 @@
      * Does not persist across reboots.
      * Represents total energy: both on-battery and plugged-in.
      */
-    public long getConsumedEnergyUws(PowerMonitor powerMonitor) {
+    public long getConsumedEnergyUws(@NonNull PowerMonitor powerMonitor) {
         int offset = Arrays.binarySearch(mPowerMonitors, powerMonitor, POWER_MONITOR_COMPARATOR);
         if (offset >= 0) {
             return mEnergyUws[offset];
@@ -64,9 +65,10 @@
     }
 
     /**
-     * Elapsed realtime when the snapshot was taken.
+     * Elapsed realtime, in milliseconds, when the snapshot was taken.
      */
-    public long getTimestampMs(PowerMonitor powerMonitor) {
+    @ElapsedRealtimeLong
+    public long getTimestamp(@NonNull PowerMonitor powerMonitor) {
         int offset = Arrays.binarySearch(mPowerMonitors, powerMonitor, POWER_MONITOR_COMPARATOR);
         if (offset >= 0) {
             return mTimestampsMs[offset];
diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java
index ab30a8b..dfc43f4 100644
--- a/core/java/android/os/health/SystemHealthManager.java
+++ b/core/java/android/os/health/SystemHealthManager.java
@@ -24,6 +24,8 @@
 import android.os.BatteryStats;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.ConditionVariable;
+import android.os.Handler;
 import android.os.IPowerStatsService;
 import android.os.PowerMonitor;
 import android.os.PowerMonitorReadings;
@@ -36,8 +38,8 @@
 
 import java.util.Arrays;
 import java.util.Comparator;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
+import java.util.List;
+import java.util.function.Consumer;
 
 /**
  * Provides access to data about how various system resources are used by applications.
@@ -62,7 +64,8 @@
     private final IBatteryStats mBatteryStats;
     @Nullable
     private final IPowerStatsService mPowerStats;
-    private PowerMonitor[] mPowerMonitorsInfo;
+    private List<PowerMonitor> mPowerMonitorsInfo;
+    private final Object mPowerMonitorsLock = new Object();
 
     /**
      * Construct a new SystemHealthManager object.
@@ -161,53 +164,68 @@
      * @hide
      */
     @NonNull
-    public PowerMonitor[] getSupportedPowerMonitors() {
-        synchronized (this) {
+    public List<PowerMonitor> getSupportedPowerMonitors() {
+        synchronized (mPowerMonitorsLock) {
             if (mPowerMonitorsInfo != null) {
                 return mPowerMonitorsInfo;
             }
+        }
+        ConditionVariable lock = new ConditionVariable();
+        // Populate mPowerMonitorsInfo by side-effect
+        getSupportedPowerMonitors(null, unused -> lock.open());
+        lock.block();
 
-            CompletableFuture<PowerMonitor[]> future = new CompletableFuture<>();
-            getSupportedPowerMonitors(future);
-            try {
-                return future.get();
-            } catch (InterruptedException | ExecutionException e) {
-                throw new RuntimeException(e);
-            }
+        synchronized (mPowerMonitorsLock) {
+            return mPowerMonitorsInfo;
         }
     }
 
     /**
-     * Retrieves a list of supported power monitors, see {@link #getSupportedPowerMonitors()}
+     * Asynchronously retrieves a list of supported power monitors, see
+     * {@link #getSupportedPowerMonitors()}
+     *
+     * @param handler optional Handler to deliver the callback. If not supplied, the callback
+     *                may be invoked on an arbitrary thread.
+     * @param onResult callback for the result
      *
      * @hide
      */
-    public void getSupportedPowerMonitors(@NonNull CompletableFuture<PowerMonitor[]> future) {
-        synchronized (this) {
+    public void getSupportedPowerMonitors(@Nullable Handler handler,
+            @NonNull Consumer<List<PowerMonitor>> onResult) {
+        final List<PowerMonitor> result;
+        synchronized (mPowerMonitorsLock) {
             if (mPowerMonitorsInfo != null) {
-                future.complete(mPowerMonitorsInfo);
-                return;
+                result = mPowerMonitorsInfo;
+            } else if (mPowerStats == null) {
+                mPowerMonitorsInfo = List.of();
+                result = mPowerMonitorsInfo;
+            } else {
+                result = null;
             }
-            try {
-                if (mPowerStats == null) {
-                    mPowerMonitorsInfo = new PowerMonitor[0];
-                    future.complete(mPowerMonitorsInfo);
-                    return;
-                }
-
-                mPowerStats.getSupportedPowerMonitors(new ResultReceiver(null) {
-                    @Override
-                    protected void onReceiveResult(int resultCode, Bundle resultData) {
-                        synchronized (this) {
-                            mPowerMonitorsInfo = resultData.getParcelableArray(
-                                    IPowerStatsService.KEY_MONITORS, PowerMonitor.class);
-                        }
-                        future.complete(mPowerMonitorsInfo);
+        }
+        if (result != null) {
+            if (handler != null) {
+                handler.post(() -> onResult.accept(result));
+            } else {
+                onResult.accept(result);
+            }
+            return;
+        }
+        try {
+            mPowerStats.getSupportedPowerMonitors(new ResultReceiver(handler) {
+                @Override
+                protected void onReceiveResult(int resultCode, Bundle resultData) {
+                    PowerMonitor[] array = resultData.getParcelableArray(
+                            IPowerStatsService.KEY_MONITORS, PowerMonitor.class);
+                    List<PowerMonitor> result = array != null ? Arrays.asList(array) : List.of();
+                    synchronized (mPowerMonitorsLock) {
+                        mPowerMonitorsInfo = result;
                     }
-                });
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
+                    onResult.accept(result);
+                }
+            });
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -215,54 +233,74 @@
      * Retrieves the accumulated power consumption reported by the specified power monitors.
      *
      * @param powerMonitors power monitors to be returned.
+     *
      * @hide
      */
     @NonNull
-    public PowerMonitorReadings getPowerMonitorReadings(@NonNull PowerMonitor[] powerMonitors) {
-        CompletableFuture<PowerMonitorReadings> future = new CompletableFuture<>();
-        getPowerMonitorReadings(powerMonitors, future);
-        try {
-            return future.get();
-        } catch (InterruptedException | ExecutionException e) {
-            throw new RuntimeException(e);
+    public PowerMonitorReadings getPowerMonitorReadings(@NonNull List<PowerMonitor> powerMonitors) {
+        PowerMonitorReadings[] outReadings = new PowerMonitorReadings[1];
+        RuntimeException[] outException = new RuntimeException[1];
+        ConditionVariable lock = new ConditionVariable();
+        getPowerMonitorReadings(powerMonitors, null,
+                pms -> {
+                    outReadings[0] = pms;
+                    lock.open();
+                },
+                error -> {
+                    outException[0] = error;
+                    lock.open();
+                }
+        );
+        lock.block();
+        if (outException[0] != null) {
+            throw outException[0];
         }
+        return outReadings[0];
     }
 
     private static final Comparator<PowerMonitor> POWER_MONITOR_COMPARATOR =
             Comparator.comparingInt(pm -> pm.index);
 
     /**
+     * Asynchronously retrieves the accumulated power consumption reported by the specified power
+     * monitors.
+     *
      * @param powerMonitors power monitors to be retrieved.
+     * @param handler       optional Handler to deliver the callbacks. If not supplied, the callback
+     *                      may be invoked on an arbitrary thread.
+     * @param onSuccess     callback for the result
+     * @param onError       callback invoked in case of an error
+     *
      * @hide
      */
-    public void getPowerMonitorReadings(@NonNull PowerMonitor[] powerMonitors,
-            @NonNull CompletableFuture<PowerMonitorReadings> future) {
+    public void getPowerMonitorReadings(@NonNull List<PowerMonitor> powerMonitors,
+            @Nullable Handler handler, @NonNull Consumer<PowerMonitorReadings> onSuccess,
+            @NonNull Consumer<RuntimeException> onError) {
         if (mPowerStats == null) {
-            future.completeExceptionally(
-                    new IllegalArgumentException("Unsupported power monitor"));
+            onError.accept(new IllegalArgumentException("Unsupported power monitor"));
             return;
         }
 
-        Arrays.sort(powerMonitors, POWER_MONITOR_COMPARATOR);
-        int[] indices = new int[powerMonitors.length];
-        for (int i = 0; i < powerMonitors.length; i++) {
-            indices[i] = powerMonitors[i].index;
+        PowerMonitor[] powerMonitorsArray =
+                powerMonitors.toArray(new PowerMonitor[powerMonitors.size()]);
+        Arrays.sort(powerMonitorsArray, POWER_MONITOR_COMPARATOR);
+        int[] indices = new int[powerMonitors.size()];
+        for (int i = 0; i < powerMonitors.size(); i++) {
+            indices[i] = powerMonitorsArray[i].index;
         }
         try {
-            mPowerStats.getPowerMonitorReadings(indices, new ResultReceiver(null) {
+            mPowerStats.getPowerMonitorReadings(indices, new ResultReceiver(handler) {
                 @Override
                 protected void onReceiveResult(int resultCode, Bundle resultData) {
                     if (resultCode == IPowerStatsService.RESULT_SUCCESS) {
-                        future.complete(new PowerMonitorReadings(powerMonitors,
+                        onSuccess.accept(new PowerMonitorReadings(powerMonitorsArray,
                                 resultData.getLongArray(IPowerStatsService.KEY_ENERGY),
                                 resultData.getLongArray(IPowerStatsService.KEY_TIMESTAMPS)));
                     } else if (resultCode == IPowerStatsService.RESULT_UNSUPPORTED_POWER_MONITOR) {
-                        future.completeExceptionally(
-                                new IllegalArgumentException("Unsupported power monitor"));
+                        onError.accept(new IllegalArgumentException("Unsupported power monitor"));
                     } else {
-                        future.completeExceptionally(
-                                new IllegalStateException(
-                                        "Unrecognized result code " + resultCode));
+                        onError.accept(new IllegalStateException(
+                                "Unrecognized result code " + resultCode));
                     }
                 }
             });
diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java
index b85cf6d..fce87db 100644
--- a/core/java/android/service/controls/ControlsProviderService.java
+++ b/core/java/android/service/controls/ControlsProviderService.java
@@ -25,6 +25,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
+import android.os.DeadObjectException;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -285,6 +286,7 @@
         private IControlsSubscriber mCs;
         private boolean mEnforceStateless;
         private Context mContext;
+        private SubscriptionAdapter mSubscription;
 
         SubscriberProxy(boolean enforceStateless, IBinder token, IControlsSubscriber cs) {
             mEnforceStateless = enforceStateless;
@@ -300,11 +302,14 @@
 
         public void onSubscribe(Subscription subscription) {
             try {
-                mCs.onSubscribe(mToken, new SubscriptionAdapter(subscription));
+                SubscriptionAdapter subscriptionAdapter = new SubscriptionAdapter(subscription);
+                mCs.onSubscribe(mToken, subscriptionAdapter);
+                mSubscription = subscriptionAdapter;
             } catch (RemoteException ex) {
-                ex.rethrowAsRuntimeException();
+                handleRemoteException(ex);
             }
         }
+
         public void onNext(@NonNull Control control) {
             Preconditions.checkNotNull(control);
             try {
@@ -318,20 +323,36 @@
                 }
                 mCs.onNext(mToken, control);
             } catch (RemoteException ex) {
-                ex.rethrowAsRuntimeException();
+                handleRemoteException(ex);
             }
         }
+
         public void onError(Throwable t) {
             try {
                 mCs.onError(mToken, t.toString());
+                mSubscription = null;
             } catch (RemoteException ex) {
-                ex.rethrowAsRuntimeException();
+                handleRemoteException(ex);
             }
         }
+
         public void onComplete() {
             try {
                 mCs.onComplete(mToken);
+                mSubscription = null;
             } catch (RemoteException ex) {
+                handleRemoteException(ex);
+            }
+        }
+
+        private void handleRemoteException(RemoteException ex) {
+            if (ex instanceof DeadObjectException) {
+                // System UI crashed or is restarting. There is no need to rethrow this
+                SubscriptionAdapter subscriptionAdapter = mSubscription;
+                if (subscriptionAdapter != null) {
+                    subscriptionAdapter.cancel();
+                }
+            } else {
                 ex.rethrowAsRuntimeException();
             }
         }
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index 4464d19..d31f823 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.hardware.input.InputManagerGlobal;
+import android.os.IInputConstants;
 import android.util.ArrayMap;
 import android.util.Pools.SynchronizedPool;
 
@@ -53,11 +54,13 @@
     public @interface VelocityTrackableMotionEventAxis {}
 
     /**
-     * Velocity Tracker Strategy: Invalid.
+     * Use the default Velocity Tracker Strategy. Different axes may use different default
+     * strategies.
      *
      * @hide
      */
-    public static final int VELOCITY_TRACKER_STRATEGY_DEFAULT = -1;
+    public static final int VELOCITY_TRACKER_STRATEGY_DEFAULT =
+            IInputConstants.VELOCITY_TRACKER_STRATEGY_DEFAULT;
 
     /**
      * Velocity Tracker Strategy: Impulse.
@@ -66,7 +69,8 @@
      *
      * @hide
      */
-    public static final int VELOCITY_TRACKER_STRATEGY_IMPULSE = 0;
+    public static final int VELOCITY_TRACKER_STRATEGY_IMPULSE =
+            IInputConstants.VELOCITY_TRACKER_STRATEGY_IMPULSE;
 
     /**
      * Velocity Tracker Strategy: LSQ1.
@@ -77,7 +81,8 @@
      *
      * @hide
      */
-    public static final int VELOCITY_TRACKER_STRATEGY_LSQ1 = 1;
+    public static final int VELOCITY_TRACKER_STRATEGY_LSQ1 =
+            IInputConstants.VELOCITY_TRACKER_STRATEGY_LSQ1;
 
     /**
      * Velocity Tracker Strategy: LSQ2.
@@ -88,7 +93,8 @@
      *
      * @hide
      */
-    public static final int VELOCITY_TRACKER_STRATEGY_LSQ2 = 2;
+    public static final int VELOCITY_TRACKER_STRATEGY_LSQ2 =
+            IInputConstants.VELOCITY_TRACKER_STRATEGY_LSQ2;
 
     /**
      * Velocity Tracker Strategy: LSQ3.
@@ -98,7 +104,8 @@
      *
      * @hide
      */
-    public static final int VELOCITY_TRACKER_STRATEGY_LSQ3 = 3;
+    public static final int VELOCITY_TRACKER_STRATEGY_LSQ3 =
+            IInputConstants.VELOCITY_TRACKER_STRATEGY_LSQ3;
 
     /**
      * Velocity Tracker Strategy: WLSQ2_DELTA.
@@ -106,7 +113,8 @@
      *
      * @hide
      */
-    public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA = 4;
+    public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA =
+            IInputConstants.VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA;
 
     /**
      * Velocity Tracker Strategy: WLSQ2_CENTRAL.
@@ -114,7 +122,8 @@
      *
      * @hide
      */
-    public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL = 5;
+    public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL =
+            IInputConstants.VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL;
 
     /**
      * Velocity Tracker Strategy: WLSQ2_RECENT.
@@ -122,7 +131,8 @@
      *
      * @hide
      */
-    public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT = 6;
+    public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT =
+            IInputConstants.VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT;
 
     /**
      * Velocity Tracker Strategy: INT1.
@@ -134,7 +144,8 @@
      *
      * @hide
      */
-    public static final int VELOCITY_TRACKER_STRATEGY_INT1 = 7;
+    public static final int VELOCITY_TRACKER_STRATEGY_INT1 =
+            IInputConstants.VELOCITY_TRACKER_STRATEGY_INT1;
 
     /**
      * Velocity Tracker Strategy: INT2.
@@ -144,7 +155,8 @@
      *
      * @hide
      */
-    public static final int VELOCITY_TRACKER_STRATEGY_INT2 = 8;
+    public static final int VELOCITY_TRACKER_STRATEGY_INT2 =
+            IInputConstants.VELOCITY_TRACKER_STRATEGY_INT2;
 
     /**
      * Velocity Tracker Strategy: Legacy.
@@ -155,7 +167,8 @@
      *
      * @hide
      */
-    public static final int VELOCITY_TRACKER_STRATEGY_LEGACY = 9;
+    public static final int VELOCITY_TRACKER_STRATEGY_LEGACY =
+            IInputConstants.VELOCITY_TRACKER_STRATEGY_LEGACY;
 
 
     /**
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 5d1a81f..bb1d5b1 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1302,7 +1302,7 @@
      * ratio or orientation specified in the app manifest.
      *
      * <p>The aspect ratio compatibility override is exposed to users in device
-     * settings. A menu in device settings lists all apps that don't opt out of
+     * settings. A menu in device settings lists all apps that have not opted out of
      * the compatibility override. Users select apps from the menu and set the
      * app aspect ratio on a per-app basis. Typically, the menu is available
      * only on large screen devices.
@@ -1347,11 +1347,11 @@
      * Application level
      * {@link android.content.pm.PackageManager.Property PackageManager.Property}
      * tag that (when set to false) informs the system the app has opted out of the
-     * full-screen option of the aspect ratio compatibility override. (For
-     * background information about the aspect ratio compatibility override, see
+     * full-screen option of the user aspect ratio compatibility override settings. (For
+     * background information about the user aspect ratio compatibility override, see
      * {@link #PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE}.)
      *
-     * <p>When users apply the aspect ratio compatibility override, the orientation
+     * <p>When users apply the full-screen compatibility override, the orientation
      * of the activity is forced to {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_USER}.
      *
      * <p>The user override is intended to improve the app experience on devices
diff --git a/core/jni/android_view_VelocityTracker.cpp b/core/jni/android_view_VelocityTracker.cpp
index 05c9f68..03e9a6a 100644
--- a/core/jni/android_view_VelocityTracker.cpp
+++ b/core/jni/android_view_VelocityTracker.cpp
@@ -16,13 +16,14 @@
 
 #define LOG_TAG "VelocityTracker-JNI"
 
+#include <android-base/logging.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <cutils/properties.h>
 #include <input/Input.h>
 #include <input/VelocityTracker.h>
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedUtfChars.h>
-#include <utils/Log.h>
+
 #include "android_view_MotionEvent.h"
 #include "core_jni_helpers.h"
 
@@ -102,7 +103,7 @@
         jobject eventObj) {
     const MotionEvent* event = android_view_MotionEvent_getNativePtr(env, eventObj);
     if (!event) {
-        ALOGW("nativeAddMovement failed because MotionEvent was finalized.");
+        LOG(WARNING) << "nativeAddMovement failed because MotionEvent was finalized.";
         return;
     }
 
diff --git a/core/tests/coretests/src/android/os/health/SystemHealthManagerTest.java b/core/tests/coretests/src/android/os/health/SystemHealthManagerTest.java
index 2232e3a..e1f9523 100644
--- a/core/tests/coretests/src/android/os/health/SystemHealthManagerTest.java
+++ b/core/tests/coretests/src/android/os/health/SystemHealthManagerTest.java
@@ -20,6 +20,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.os.ConditionVariable;
 import android.os.PowerMonitor;
 import android.os.PowerMonitorReadings;
 
@@ -29,13 +30,16 @@
 import java.util.List;
 
 public class SystemHealthManagerTest {
+    private List<PowerMonitor> mPowerMonitorInfo;
+    private PowerMonitorReadings mReadings;
+    private RuntimeException mException;
 
     @Test
     public void getPowerMonitors() {
         SystemHealthManager shm = getContext().getSystemService(SystemHealthManager.class);
-        PowerMonitor[] powerMonitorInfo = shm.getSupportedPowerMonitors();
+        List<PowerMonitor> powerMonitorInfo = shm.getSupportedPowerMonitors();
         assertThat(powerMonitorInfo).isNotNull();
-        if (powerMonitorInfo.length == 0) {
+        if (powerMonitorInfo.isEmpty()) {
             // This device does not support PowerStats HAL
             return;
         }
@@ -50,20 +54,73 @@
             }
         }
 
-        List<PowerMonitor> pmis = new ArrayList<>();
+        List<PowerMonitor> selectedMonitors = new ArrayList<>();
         if (consumerMonitor != null) {
-            pmis.add(consumerMonitor);
+            selectedMonitors.add(consumerMonitor);
         }
         if (measurementMonitor != null) {
-            pmis.add(measurementMonitor);
+            selectedMonitors.add(measurementMonitor);
         }
 
-        PowerMonitor[] selectedMonitors = pmis.toArray(new PowerMonitor[0]);
         PowerMonitorReadings readings = shm.getPowerMonitorReadings(selectedMonitors);
 
         for (PowerMonitor monitor : selectedMonitors) {
             assertThat(readings.getConsumedEnergyUws(monitor)).isAtLeast(0);
-            assertThat(readings.getTimestampMs(monitor)).isGreaterThan(0);
+            assertThat(readings.getTimestamp(monitor)).isGreaterThan(0);
+        }
+    }
+
+    @Test
+    public void getPowerMonitorsAsync() {
+        SystemHealthManager shm = getContext().getSystemService(SystemHealthManager.class);
+        ConditionVariable done = new ConditionVariable();
+        shm.getSupportedPowerMonitors(null, pms -> {
+            mPowerMonitorInfo = pms;
+            done.open();
+        });
+        done.block();
+        assertThat(mPowerMonitorInfo).isNotNull();
+        if (mPowerMonitorInfo.isEmpty()) {
+            // This device does not support PowerStats HAL
+            return;
+        }
+
+        PowerMonitor consumerMonitor = null;
+        PowerMonitor measurementMonitor = null;
+        for (PowerMonitor pmi : mPowerMonitorInfo) {
+            if (pmi.type == PowerMonitor.POWER_MONITOR_TYPE_MEASUREMENT) {
+                measurementMonitor = pmi;
+            } else {
+                consumerMonitor = pmi;
+            }
+        }
+
+        List<PowerMonitor> selectedMonitors = new ArrayList<>();
+        if (consumerMonitor != null) {
+            selectedMonitors.add(consumerMonitor);
+        }
+        if (measurementMonitor != null) {
+            selectedMonitors.add(measurementMonitor);
+        }
+
+        done.close();
+        shm.getPowerMonitorReadings(selectedMonitors, null,
+                readings -> {
+                    mReadings = readings;
+                    done.open();
+                },
+                exception -> {
+                    mException = exception;
+                    done.open();
+                }
+        );
+        done.block();
+
+        assertThat(mException).isNull();
+
+        for (PowerMonitor monitor : selectedMonitors) {
+            assertThat(mReadings.getConsumedEnergyUws(monitor)).isAtLeast(0);
+            assertThat(mReadings.getTimestamp(monitor)).isGreaterThan(0);
         }
     }
 }
diff --git a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
index 5fe17ee..4d446901 100644
--- a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
+++ b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -36,6 +37,7 @@
 import android.graphics.drawable.Icon;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.DeadObjectException;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.service.controls.actions.CommandAction;
@@ -53,6 +55,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
 import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -307,6 +310,18 @@
                 intent.getParcelableExtra(ControlsProviderService.EXTRA_CONTROL)));
     }
 
+    @Test
+    public void testOnNextDoesntRethrowDeadObjectException() throws RemoteException {
+        doAnswer(invocation -> {
+            throw new DeadObjectException();
+        }).when(mSubscriber).onNext(ArgumentMatchers.any(), ArgumentMatchers.any());
+        Control control = new Control.StatelessBuilder("TEST_ID", mPendingIntent).build();
+
+        sendControlGetControl(control);
+
+        assertTrue(mControlsProviderService.mSubscription.mIsCancelled);
+    }
+
     /**
      * Sends the control through the publisher in {@code mControlsProviderService}, returning
      * the control obtained by the subscriber
@@ -359,6 +374,7 @@
         }
 
         private List<Control> mControls;
+        private FakeSubscription mSubscription;
 
         public void setControls(List<Control> controls) {
             mControls = controls;
@@ -398,17 +414,35 @@
         }
 
         private Subscription createSubscription(Subscriber s, List<Control> controls) {
-            return new Subscription() {
-                public void request(long n) {
-                    int i = 0;
-                    for (Control c : mControls) {
-                        if (i++ < n) s.onNext(c);
-                        else break;
-                    }
-                    s.onComplete();
-                }
-                public void cancel() {}
-            };
+            FakeSubscription subscription = new FakeSubscription(s, controls);
+            mSubscription = subscription;
+            return subscription;
+        }
+    }
+
+    private static final class FakeSubscription implements Subscription {
+
+        private final Subscriber mSubscriber;
+        private final List<Control> mControls;
+
+        private boolean mIsCancelled = false;
+
+        FakeSubscription(Subscriber s, List<Control> controls) {
+            mSubscriber = s;
+            mControls = controls;
+        }
+
+        public void request(long n) {
+            int i = 0;
+            for (Control c : mControls) {
+                if (i++ < n) mSubscriber.onNext(c);
+                else break;
+            }
+            mSubscriber.onComplete();
+        }
+
+        public void cancel() {
+            mIsCancelled = true;
         }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 62b0799..0998e71 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -342,6 +342,7 @@
                 if (!mActiveLetterboxEduLayout.updateCompatInfo(taskInfo, taskListener,
                         showOnDisplay(mActiveLetterboxEduLayout.getDisplayId()))) {
                     // The layout is no longer eligible to be shown, clear active layout.
+                    mActiveLetterboxEduLayout.release();
                     mActiveLetterboxEduLayout = null;
                 }
                 return;
@@ -371,15 +372,9 @@
             ShellTaskOrganizer.TaskListener taskListener) {
         return new LetterboxEduWindowManager(context, taskInfo,
                 mSyncQueue, taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId),
-                mTransitionsLazy.get(), this::onLetterboxEduDismissed, mDockStateReader,
-                mCompatUIConfiguration);
-    }
-
-    private void onLetterboxEduDismissed(
-            Pair<TaskInfo, ShellTaskOrganizer.TaskListener> stateInfo) {
-        mActiveLetterboxEduLayout = null;
-        // We need to update the UI
-        createOrUpdateReachabilityEduLayout(stateInfo.first, stateInfo.second);
+                mTransitionsLazy.get(),
+                stateInfo -> createOrUpdateReachabilityEduLayout(stateInfo.first, stateInfo.second),
+                mDockStateReader, mCompatUIConfiguration);
     }
 
     private void createOrUpdateRestartDialogLayout(TaskInfo taskInfo,
@@ -448,6 +443,7 @@
                 if (!mActiveReachabilityEduLayout.updateCompatInfo(taskInfo, taskListener,
                         showOnDisplay(mActiveReachabilityEduLayout.getDisplayId()))) {
                     // The layout is no longer eligible to be shown, remove from active layouts.
+                    mActiveReachabilityEduLayout.release();
                     mActiveReachabilityEduLayout = null;
                 }
                 return;
diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp
index 8191f5e..a958a09 100644
--- a/libs/hwui/FrameInfo.cpp
+++ b/libs/hwui/FrameInfo.cpp
@@ -15,6 +15,8 @@
  */
 #include "FrameInfo.h"
 
+#include <gui/TraceUtils.h>
+
 #include <cstring>
 
 namespace android {
@@ -51,6 +53,30 @@
 
 void FrameInfo::importUiThreadInfo(int64_t* info) {
     memcpy(mFrameInfo, info, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t));
+    mSkippedFrameReason.reset();
+}
+
+const char* toString(SkippedFrameReason reason) {
+    switch (reason) {
+        case SkippedFrameReason::DrawingOff:
+            return "DrawingOff";
+        case SkippedFrameReason::ContextIsStopped:
+            return "ContextIsStopped";
+        case SkippedFrameReason::NothingToDraw:
+            return "NothingToDraw";
+        case SkippedFrameReason::NoOutputTarget:
+            return "NoOutputTarget";
+        case SkippedFrameReason::NoBuffer:
+            return "NoBuffer";
+        case SkippedFrameReason::AlreadyDrawn:
+            return "AlreadyDrawn";
+    }
+}
+
+void FrameInfo::setSkippedFrameReason(android::uirenderer::SkippedFrameReason reason) {
+    ATRACE_FORMAT_INSTANT("Frame skipped: %s", toString(reason));
+    addFlag(FrameInfoFlags::SkippedFrame);
+    mSkippedFrameReason = reason;
 }
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index b15b6cb..f7ad139 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -16,15 +16,17 @@
 #ifndef FRAMEINFO_H_
 #define FRAMEINFO_H_
 
-#include "utils/Macros.h"
-
 #include <cutils/compiler.h>
+#include <memory.h>
 #include <utils/Timers.h>
 
 #include <array>
-#include <memory.h>
+#include <optional>
 #include <string>
 
+#include "SkippedFrameInfo.h"
+#include "utils/Macros.h"
+
 namespace android {
 namespace uirenderer {
 
@@ -186,8 +188,14 @@
         return mFrameInfo[static_cast<int>(index)];
     }
 
+    void setSkippedFrameReason(SkippedFrameReason reason);
+    inline std::optional<SkippedFrameReason> getSkippedFrameReason() const {
+        return mSkippedFrameReason;
+    }
+
 private:
     int64_t mFrameInfo[static_cast<int>(FrameInfoIndex::NumIndexes)];
+    std::optional<SkippedFrameReason> mSkippedFrameReason;
 };
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp
index 687e4dd..59f2169 100644
--- a/libs/hwui/FrameInfoVisualizer.cpp
+++ b/libs/hwui/FrameInfoVisualizer.cpp
@@ -148,7 +148,7 @@
     int fast_i = 0, janky_i = 0;
     // Set the bottom of all the shapes to the baseline
     for (int fi = mFrameSource.size() - 1; fi >= 0; fi--) {
-        if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) {
+        if (mFrameSource[fi].getSkippedFrameReason()) {
             continue;
         }
         float lineWidth = baseLineWidth;
@@ -181,7 +181,7 @@
     int janky_i = (mNumJankyRects - 1) * 4;
 
     for (size_t fi = 0; fi < mFrameSource.size(); fi++) {
-        if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) {
+        if (mFrameSource[fi].getSkippedFrameReason()) {
             continue;
         }
 
diff --git a/libs/hwui/SkippedFrameInfo.h b/libs/hwui/SkippedFrameInfo.h
new file mode 100644
index 0000000..de56d9a
--- /dev/null
+++ b/libs/hwui/SkippedFrameInfo.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 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.
+ */
+
+#pragma once
+
+namespace android::uirenderer {
+
+enum class SkippedFrameReason {
+    DrawingOff,
+    ContextIsStopped,
+    NothingToDraw,
+    NoOutputTarget,
+    NoBuffer,
+    AlreadyDrawn,
+};
+
+} /* namespace android::uirenderer */
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index 2bff9cb..ea25f68 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -16,14 +16,16 @@
 
 #pragma once
 
-#include "Properties.h"
-#include "utils/Macros.h"
-
 #include <utils/Timers.h>
-#include "SkSize.h"
 
+#include <optional>
 #include <string>
 
+#include "Properties.h"
+#include "SkSize.h"
+#include "SkippedFrameInfo.h"
+#include "utils/Macros.h"
+
 namespace android {
 namespace uirenderer {
 
@@ -110,13 +112,13 @@
         // animate itself, such as if hasFunctors is true
         // This is only set if hasAnimations is true
         bool requiresUiRedraw = false;
-        // This is set to true if draw() can be called this frame
-        // false means that we must delay until the next vsync pulse as frame
+        // This is set to nullopt if draw() can be called this frame
+        // A value means that we must delay until the next vsync pulse as frame
         // production is outrunning consumption
-        // NOTE that if this is false CanvasContext will set either requiresUiRedraw
+        // NOTE that if this has a value CanvasContext will set either requiresUiRedraw
         // *OR* will post itself for the next vsync automatically, use this
         // only to avoid calling draw()
-        bool canDrawThisFrame = true;
+        std::optional<SkippedFrameReason> skippedFrameReason;
         // Sentinel for animatedImageDelay meaning there is no need to post such
         // a message.
         static constexpr nsecs_t kNoAnimatedImageDelay = -1;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 2ef7802..2e0de3f 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -357,8 +357,9 @@
     return true;
 }
 
-static bool wasSkipped(FrameInfo* info) {
-    return info && ((*info)[FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame);
+static std::optional<SkippedFrameReason> wasSkipped(FrameInfo* info) {
+    if (info) return info->getSkippedFrameReason();
+    return std::nullopt;
 }
 
 bool CanvasContext::isSwapChainStuffed() {
@@ -407,13 +408,26 @@
 
     // If the previous frame was dropped we don't need to hold onto it, so
     // just keep using the previous frame's structure instead
-    if (wasSkipped(mCurrentFrameInfo)) {
+    if (const auto reason = wasSkipped(mCurrentFrameInfo)) {
         // Use the oldest skipped frame in case we skip more than a single frame
         if (!mSkippedFrameInfo) {
-            mSkippedFrameInfo.emplace();
-            mSkippedFrameInfo->vsyncId =
-                mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId);
-            mSkippedFrameInfo->startTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime);
+            switch (*reason) {
+                case SkippedFrameReason::AlreadyDrawn:
+                case SkippedFrameReason::NoBuffer:
+                case SkippedFrameReason::NoOutputTarget:
+                    mSkippedFrameInfo.emplace();
+                    mSkippedFrameInfo->vsyncId =
+                            mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId);
+                    mSkippedFrameInfo->startTime =
+                            mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime);
+                    break;
+                case SkippedFrameReason::DrawingOff:
+                case SkippedFrameReason::ContextIsStopped:
+                case SkippedFrameReason::NothingToDraw:
+                    // Do not report those as skipped frames as there was no frame expected to be
+                    // drawn
+                    break;
+            }
         }
     } else {
         mCurrentFrameInfo = mJankTracker.startFrame();
@@ -427,7 +441,7 @@
     info.damageAccumulator = &mDamageAccumulator;
     info.layerUpdateQueue = &mLayerUpdateQueue;
     info.damageGenerationId = mDamageId++;
-    info.out.canDrawThisFrame = true;
+    info.out.skippedFrameReason = std::nullopt;
 
     mAnimationContext->startFrame(info.mode);
     for (const sp<RenderNode>& node : mRenderNodes) {
@@ -447,8 +461,8 @@
     mIsDirty = true;
 
     if (CC_UNLIKELY(!hasOutputTarget())) {
-        mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
-        info.out.canDrawThisFrame = false;
+        info.out.skippedFrameReason = SkippedFrameReason::NoOutputTarget;
+        mCurrentFrameInfo->setSkippedFrameReason(*info.out.skippedFrameReason);
         return;
     }
 
@@ -463,23 +477,23 @@
         if (vsyncDelta < 2_ms) {
             // Already drew for this vsync pulse, UI draw request missed
             // the deadline for RT animations
-            info.out.canDrawThisFrame = false;
+            info.out.skippedFrameReason = SkippedFrameReason::AlreadyDrawn;
         }
     } else {
-        info.out.canDrawThisFrame = true;
+        info.out.skippedFrameReason = std::nullopt;
     }
 
     // TODO: Do we need to abort out if the backdrop is added but not ready? Should that even
     // be an allowable combination?
     if (mRenderNodes.size() > 2 && !mRenderNodes[1]->isRenderable()) {
-        info.out.canDrawThisFrame = false;
+        info.out.skippedFrameReason = SkippedFrameReason::NothingToDraw;
     }
 
-    if (info.out.canDrawThisFrame) {
+    if (!info.out.skippedFrameReason) {
         int err = mNativeSurface->reserveNext();
         if (err != OK) {
-            mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
-            info.out.canDrawThisFrame = false;
+            info.out.skippedFrameReason = SkippedFrameReason::NoBuffer;
+            mCurrentFrameInfo->setSkippedFrameReason(*info.out.skippedFrameReason);
             ALOGW("reserveNext failed, error = %d (%s)", err, strerror(-err));
             if (err != TIMED_OUT) {
                 // A timed out surface can still recover, but assume others are permanently dead.
@@ -488,11 +502,11 @@
             }
         }
     } else {
-        mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
+        mCurrentFrameInfo->setSkippedFrameReason(*info.out.skippedFrameReason);
     }
 
     bool postedFrameCallback = false;
-    if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
+    if (info.out.hasAnimations || info.out.skippedFrameReason) {
         if (CC_UNLIKELY(!Properties::enableRTAnimations)) {
             info.out.requiresUiRedraw = true;
         }
@@ -558,9 +572,20 @@
     mSyncDelayDuration = 0;
     mIdleDuration = 0;
 
-    if (!Properties::isDrawingEnabled() ||
-        (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw())) {
-        mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
+    const auto skippedFrameReason = [&]() -> std::optional<SkippedFrameReason> {
+        if (!Properties::isDrawingEnabled()) {
+            return SkippedFrameReason::DrawingOff;
+        }
+
+        if (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw()) {
+            return SkippedFrameReason::NothingToDraw;
+        }
+
+        return std::nullopt;
+    }();
+    if (skippedFrameReason) {
+        mCurrentFrameInfo->setSkippedFrameReason(*skippedFrameReason);
+
         if (auto grContext = getGrContext()) {
             // Submit to ensure that any texture uploads complete and Skia can
             // free its staging buffers.
@@ -904,7 +929,7 @@
 
     TreeInfo info(TreeInfo::MODE_RT_ONLY, *this);
     prepareTree(info, frameInfo, systemTime(SYSTEM_TIME_MONOTONIC), node);
-    if (info.out.canDrawThisFrame) {
+    if (!info.out.skippedFrameReason) {
         draw(info.out.solelyTextureViewUpdates);
     } else {
         // wait on fences so tasks don't overlap next frame
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 53b43ba..1b333bf 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -104,7 +104,7 @@
         info.forceDrawFrame = mForceDrawFrame;
         mForceDrawFrame = false;
         canUnblockUiThread = syncFrameState(info);
-        canDrawThisFrame = info.out.canDrawThisFrame;
+        canDrawThisFrame = !info.out.skippedFrameReason.has_value();
         solelyTextureViewUpdates = info.out.solelyTextureViewUpdates;
 
         if (mFrameCommitCallback) {
@@ -192,11 +192,12 @@
     if (CC_UNLIKELY(!hasTarget || !canDraw)) {
         if (!hasTarget) {
             mSyncResult |= SyncResult::LostSurfaceRewardIfFound;
+            info.out.skippedFrameReason = SkippedFrameReason::NoOutputTarget;
         } else {
             // If we have a surface but can't draw we must be stopped
             mSyncResult |= SyncResult::ContextIsStopped;
+            info.out.skippedFrameReason = SkippedFrameReason::ContextIsStopped;
         }
-        info.out.canDrawThisFrame = false;
     }
 
     if (info.out.hasAnimations) {
@@ -204,7 +205,7 @@
             mSyncResult |= SyncResult::UIRedrawRequired;
         }
     }
-    if (!info.out.canDrawThisFrame) {
+    if (info.out.skippedFrameReason) {
         mSyncResult |= SyncResult::FrameDropped;
     }
     // If prepareTextures is false, we ran out of texture cache space
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 9234d37..147412d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -22,6 +22,7 @@
 import static android.media.MediaRoute2Info.TYPE_GROUP;
 import static android.media.MediaRoute2Info.TYPE_HDMI;
 import static android.media.MediaRoute2Info.TYPE_HEARING_AID;
+import static android.media.MediaRoute2Info.TYPE_REMOTE_AUDIO_VIDEO_RECEIVER;
 import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
 import static android.media.MediaRoute2Info.TYPE_REMOTE_TV;
 import static android.media.MediaRoute2Info.TYPE_UNKNOWN;
@@ -82,7 +83,8 @@
             MediaDeviceType.TYPE_FAST_PAIR_BLUETOOTH_DEVICE,
             MediaDeviceType.TYPE_BLUETOOTH_DEVICE,
             MediaDeviceType.TYPE_CAST_DEVICE,
-            MediaDeviceType.TYPE_CAST_GROUP_DEVICE})
+            MediaDeviceType.TYPE_CAST_GROUP_DEVICE,
+            MediaDeviceType.TYPE_REMOTE_AUDIO_VIDEO_RECEIVER})
     public @interface MediaDeviceType {
         int TYPE_UNKNOWN = 0;
         int TYPE_PHONE_DEVICE = 1;
@@ -92,6 +94,7 @@
         int TYPE_BLUETOOTH_DEVICE = 5;
         int TYPE_CAST_DEVICE = 6;
         int TYPE_CAST_GROUP_DEVICE = 7;
+        int TYPE_REMOTE_AUDIO_VIDEO_RECEIVER = 8;
     }
 
     @Retention(RetentionPolicy.SOURCE)
@@ -161,6 +164,9 @@
             case TYPE_BLE_HEADSET:
                 mType = MediaDeviceType.TYPE_BLUETOOTH_DEVICE;
                 break;
+            case TYPE_REMOTE_AUDIO_VIDEO_RECEIVER:
+                mType = MediaDeviceType.TYPE_REMOTE_AUDIO_VIDEO_RECEIVER;
+                break;
             case TYPE_UNKNOWN:
             case TYPE_REMOTE_TV:
             case TYPE_REMOTE_SPEAKER:
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
index 39d4e6e..412d1a3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
@@ -56,6 +56,7 @@
      * Update the endpoints of a content switching operation.
      * This method should be called before a switching operation, so the metric logger can track
      * source and target devices.
+     *
      * @param source the current connected media device
      * @param target the target media device for content switching to
      */
@@ -72,37 +73,9 @@
 
     /**
      * Do the metric logging of content switching success.
+     *
      * @param selectedDeviceType string representation of the target media device
-     * @param deviceList media device list for device count updating
-     */
-    public void logOutputSuccess(String selectedDeviceType, List<MediaDevice> deviceList) {
-        if (DEBUG) {
-            Log.d(TAG, "logOutputSuccess - selected device: " + selectedDeviceType);
-        }
-
-        if (mSourceDevice == null && mTargetDevice == null) {
-            return;
-        }
-
-        updateLoggingDeviceCount(deviceList);
-
-        SysUiStatsLog.write(
-                SysUiStatsLog.MEDIAOUTPUT_OP_SWITCH_REPORTED,
-                getLoggingDeviceType(mSourceDevice, true),
-                getLoggingDeviceType(mTargetDevice, false),
-                SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__RESULT__OK,
-                SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__NO_ERROR,
-                getLoggingPackageName(),
-                mWiredDeviceCount,
-                mConnectedBluetoothDeviceCount,
-                mRemoteDeviceCount,
-                mAppliedDeviceCountWithinRemoteGroup);
-    }
-
-    /**
-     * Do the metric logging of content switching success.
-     * @param selectedDeviceType string representation of the target media device
-     * @param deviceItemList media item list for device count updating
+     * @param deviceItemList     media item list for device count updating
      */
     public void logOutputItemSuccess(String selectedDeviceType, List<MediaItem> deviceItemList) {
         if (DEBUG) {
@@ -125,11 +98,14 @@
                 mWiredDeviceCount,
                 mConnectedBluetoothDeviceCount,
                 mRemoteDeviceCount,
-                mAppliedDeviceCountWithinRemoteGroup);
+                mAppliedDeviceCountWithinRemoteGroup,
+                mTargetDevice.isSuggestedDevice(),
+                mTargetDevice.hasOngoingSession());
     }
 
     /**
      * Do the metric logging of volume adjustment.
+     *
      * @param source the device been adjusted
      */
     public void logInteractionAdjustVolume(MediaDevice source) {
@@ -141,7 +117,8 @@
                 SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT,
                 SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__ADJUST_VOLUME,
                 getInteractionDeviceType(source),
-                getLoggingPackageName());
+                getLoggingPackageName(),
+                source.isSuggestedDevice());
     }
 
     /**
@@ -156,7 +133,8 @@
                 SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT,
                 SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__STOP_CASTING,
                 SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__UNKNOWN_TYPE,
-                getLoggingPackageName());
+                getLoggingPackageName(),
+                /*isSuggestedDevice = */false);
     }
 
     /**
@@ -171,42 +149,15 @@
                 SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT,
                 SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__EXPANSION,
                 getInteractionDeviceType(source),
-                getLoggingPackageName());
-    }
-
-    /**
-     * Do the metric logging of content switching failure.
-     * @param deviceList media device list for device count updating
-     * @param reason the reason of content switching failure
-     */
-    public void logOutputFailure(List<MediaDevice> deviceList, int reason) {
-        if (DEBUG) {
-            Log.e(TAG, "logRequestFailed - " + reason);
-        }
-
-        if (mSourceDevice == null && mTargetDevice == null) {
-            return;
-        }
-
-        updateLoggingDeviceCount(deviceList);
-
-        SysUiStatsLog.write(
-                SysUiStatsLog.MEDIAOUTPUT_OP_SWITCH_REPORTED,
-                getLoggingDeviceType(mSourceDevice, true),
-                getLoggingDeviceType(mTargetDevice, false),
-                SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__RESULT__ERROR,
-                getLoggingSwitchOpSubResult(reason),
                 getLoggingPackageName(),
-                mWiredDeviceCount,
-                mConnectedBluetoothDeviceCount,
-                mRemoteDeviceCount,
-                mAppliedDeviceCountWithinRemoteGroup);
+                source.isSuggestedDevice());
     }
 
     /**
      * Do the metric logging of content switching failure.
+     *
      * @param deviceItemList media item list for device count updating
-     * @param reason the reason of content switching failure
+     * @param reason         the reason of content switching failure
      */
     public void logOutputItemFailure(List<MediaItem> deviceItemList, int reason) {
         if (DEBUG) {
@@ -229,7 +180,9 @@
                 mWiredDeviceCount,
                 mConnectedBluetoothDeviceCount,
                 mRemoteDeviceCount,
-                mAppliedDeviceCountWithinRemoteGroup);
+                mAppliedDeviceCountWithinRemoteGroup,
+                mTargetDevice.isSuggestedDevice(),
+                mTargetDevice.hasOngoingSession());
     }
 
     private void updateLoggingDeviceCount(List<MediaDevice> deviceList) {
@@ -266,7 +219,7 @@
         mWiredDeviceCount = mConnectedBluetoothDeviceCount = mRemoteDeviceCount = 0;
         mAppliedDeviceCountWithinRemoteGroup = 0;
 
-        for (MediaItem mediaItem: deviceItemList) {
+        for (MediaItem mediaItem : deviceItemList) {
             if (mediaItem.getMediaDevice().isPresent()
                     && mediaItem.getMediaDevice().get().isConnected()) {
                 switch (mediaItem.getMediaDevice().get().getDeviceType()) {
@@ -326,6 +279,10 @@
                 return isSourceDevice
                         ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__REMOTE_GROUP
                         : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__REMOTE_GROUP;
+            case MediaDevice.MediaDeviceType.TYPE_REMOTE_AUDIO_VIDEO_RECEIVER:
+                return isSourceDevice
+                        ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__AVR
+                        : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__AVR;
             default:
                 return isSourceDevice
                         ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__UNKNOWN_TYPE
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index e97a12a..6f9b7d6 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -295,6 +295,8 @@
     private final PowerManager.WakeLock mDownloadPsdsWakeLock;
     @GuardedBy("mLock")
     private final Set<Integer> mPendingDownloadPsdsTypes = new HashSet<>();
+    @GuardedBy("mLock")
+    private final Set<Integer> mDownloadInProgressPsdsTypes = new HashSet<>();
 
     /**
      * Properties loaded from PROPERTIES_FILE.
@@ -767,8 +769,16 @@
             return;
         }
         synchronized (mLock) {
+            if (mDownloadInProgressPsdsTypes.contains(psdsType)) {
+                if (DEBUG) {
+                    Log.d(TAG,
+                            "PSDS type " + psdsType + " download in progress. Ignore the request.");
+                }
+                return;
+            }
             // hold wake lock while task runs
             mDownloadPsdsWakeLock.acquire(DOWNLOAD_PSDS_DATA_TIMEOUT_MS);
+            mDownloadInProgressPsdsTypes.add(psdsType);
         }
         Log.i(TAG, "WakeLock acquired by handleDownloadPsdsData()");
         Executors.newSingleThreadExecutor().execute(() -> {
@@ -818,6 +828,7 @@
                     Log.e(TAG, "WakeLock expired before release in "
                             + "handleDownloadPsdsData()");
                 }
+                mDownloadInProgressPsdsTypes.remove(psdsType);
             }
         });
     }
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index dd681aa..d77ceb2 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -130,7 +130,8 @@
             // Keep system and current user
             if (user.id != UserHandle.USER_SYSTEM &&
                     user.id != currentUser &&
-                    user.id != communalProfileId) {
+                    user.id != communalProfileId &&
+                    !user.isMain()) {
                 removeUser(user.id);
             }
         }
@@ -1105,16 +1106,16 @@
     public void testCreateProfileForUser_disallowAddManagedProfile() throws Exception {
         assumeManagedUsersSupported();
         final int mainUserId = mUserManager.getMainUser().getIdentifier();
-        final UserHandle mainUserHandle = asHandle(mainUserId);
+        final UserHandle currentUserHandle = asHandle(ActivityManager.getCurrentUser());
         mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, true,
-                mainUserHandle);
+                currentUserHandle);
         try {
             UserInfo userInfo = createProfileForUser("Managed",
                     UserManager.USER_TYPE_PROFILE_MANAGED, mainUserId);
             assertThat(userInfo).isNull();
         } finally {
             mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, false,
-                    mainUserHandle);
+                    currentUserHandle);
         }
     }
 
@@ -1190,6 +1191,7 @@
     @Test
     public void testGetManagedProfileCreationTime() throws Exception {
         assumeManagedUsersSupported();
+        assumeTrue("User does not have access to creation time", mUserManager.isMainUser());
         final int mainUserId = mUserManager.getMainUser().getIdentifier();
         final long startTime = System.currentTimeMillis();
         UserInfo profile = createProfileForUser("Managed 1",