Merge "Remove ability to inject aconfig flags." into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index c211b02..76377e7 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -29,6 +29,7 @@
     ":android.service.notification.flags-aconfig-java{.generated_srcjars}",
     ":android.view.flags-aconfig-java{.generated_srcjars}",
     ":android.view.accessibility.flags-aconfig-java{.generated_srcjars}",
+    ":audio-framework-aconfig",
     ":camera_platform_flags_core_java_lib{.generated_srcjars}",
     ":com.android.window.flags.window-aconfig-java{.generated_srcjars}",
     ":android.hardware.biometrics.flags-aconfig-java{.generated_srcjars}",
@@ -39,7 +40,6 @@
     ":android.companion.virtual.flags-aconfig-java{.generated_srcjars}",
     ":android.view.inputmethod.flags-aconfig-java{.generated_srcjars}",
     ":android.widget.flags-aconfig-java{.generated_srcjars}",
-    ":com.android.media.audio.flags-aconfig-java{.generated_srcjars}",
     ":com.android.media.flags.bettertogether-aconfig-java{.generated_srcjars}",
     ":sdk_sandbox_flags_lib{.generated_srcjars}",
     ":android.permission.flags-aconfig-java{.generated_srcjars}",
@@ -54,7 +54,6 @@
     ":android.view.contentprotection.flags-aconfig-java{.generated_srcjars}",
     ":android.service.voice.flags-aconfig-java{.generated_srcjars}",
     ":android.media.tv.flags-aconfig-java{.generated_srcjars}",
-    ":aconfig_midi_flags_java_lib{.generated_srcjars}",
     ":android.service.autofill.flags-aconfig-java{.generated_srcjars}",
     ":com.android.net.flags-aconfig-java{.generated_srcjars}",
     ":device_policy_aconfig_flags_lib{.generated_srcjars}",
@@ -393,13 +392,6 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
-// Media Audio
-java_aconfig_library {
-    name: "com.android.media.audio.flags-aconfig-java",
-    aconfig_declarations: "aconfig_audio_flags",
-    defaults: ["framework-minus-apex-aconfig-java-defaults"],
-}
-
 // Permissions
 aconfig_declarations {
     name: "android.permission.flags-aconfig",
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 646e05c..9961c4f 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -1791,7 +1791,10 @@
          * <ol>
          *     <li>Run as soon as possible</li>
          *     <li>Be less restricted during Doze and battery saver</li>
-         *     <li>Bypass Doze, app standby, and battery saver network restrictions</li>
+         *     <li>
+         *         Bypass Doze, app standby, and battery saver network restrictions (if the job
+         *         has a {@link #setRequiredNetwork(NetworkRequest) connectivity constraint})
+         *     </li>
          *     <li>Be less likely to be killed than regular jobs</li>
          *     <li>Be subject to background location throttling</li>
          *     <li>Be exempt from delay to optimize job execution</li>
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 23b36e2..bff4353 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -3859,10 +3859,16 @@
             // Only let the app use the higher runtime if it hasn't repeatedly timed out.
             final String timeoutTag = job.shouldTreatAsExpeditedJob()
                     ? QUOTA_TRACKER_TIMEOUT_EJ_TAG : QUOTA_TRACKER_TIMEOUT_REG_TAG;
+            // Developers are informed that expedited jobs can be stopped earlier than regular jobs
+            // and so shouldn't use them for long pieces of work. There's little reason to let
+            // them run longer than the normal 10 minutes.
+            final long normalUpperLimitMs = job.shouldTreatAsExpeditedJob()
+                    ? mConstants.RUNTIME_MIN_GUARANTEE_MS
+                    : mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS;
             final long upperLimitMs =
                     mQuotaTracker.isWithinQuota(job.getTimeoutBlameUserId(),
                             job.getTimeoutBlamePackageName(), timeoutTag)
-                            ? mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS
+                            ? normalUpperLimitMs
                             : mConstants.RUNTIME_MIN_GUARANTEE_MS;
             return Math.min(upperLimitMs,
                     mConstants.USE_TARE_POLICY
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 2d49cfb..721a8bd 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -427,34 +427,30 @@
             boolean binding = false;
             boolean startedWithForegroundFlag = false;
             try {
-                final Context.BindServiceFlags bindFlags;
+                long bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_APP_COMPONENT_USAGE;
                 if (job.shouldTreatAsUserInitiatedJob() && !job.isUserBgRestricted()) {
                     // If the user has bg restricted the app, don't give the job FG privileges
                     // such as bypassing data saver or getting the higher foreground proc state.
                     // If we've gotten to this point, the app is most likely in the foreground,
                     // so the job will run just fine while the user keeps the app in the foreground.
-                    bindFlags = Context.BindServiceFlags.of(
-                            Context.BIND_AUTO_CREATE
-                                    | Context.BIND_ALMOST_PERCEPTIBLE
-                                    | Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS
-                                    | Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS
-                                    | Context.BIND_NOT_APP_COMPONENT_USAGE);
+                    bindFlags |= Context.BIND_ALMOST_PERCEPTIBLE;
+                    if (job.hasConnectivityConstraint()) {
+                        // Only add network restriction bypass flags if the job requires network.
+                        bindFlags |= Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS
+                                | Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS;
+                    }
                     startedWithForegroundFlag = true;
                 } else if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) {
-                    bindFlags = Context.BindServiceFlags.of(
-                            Context.BIND_AUTO_CREATE
-                                    | Context.BIND_NOT_FOREGROUND
-                                    | Context.BIND_ALMOST_PERCEPTIBLE
-                                    | Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS
-                                    | Context.BIND_NOT_APP_COMPONENT_USAGE);
+                    bindFlags |= Context.BIND_NOT_FOREGROUND | Context.BIND_ALMOST_PERCEPTIBLE;
+                    if (job.hasConnectivityConstraint()) {
+                        // Only add network restriction bypass flags if the job requires network.
+                        bindFlags |= Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS;
+                    }
                 } else {
-                    bindFlags = Context.BindServiceFlags.of(
-                            Context.BIND_AUTO_CREATE
-                                    | Context.BIND_NOT_FOREGROUND
-                                    | Context.BIND_NOT_PERCEPTIBLE
-                                    | Context.BIND_NOT_APP_COMPONENT_USAGE);
+                    bindFlags |= Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_PERCEPTIBLE;
                 }
-                binding = mContext.bindServiceAsUser(intent, this, bindFlags,
+                binding = mContext.bindServiceAsUser(intent, this,
+                        Context.BindServiceFlags.of(bindFlags),
                         UserHandle.of(job.getUserId()));
             } catch (SecurityException e) {
                 // Some permission policy, for example INTERACT_ACROSS_USERS and
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
index a25af71..47d3fd5 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
@@ -18,13 +18,16 @@
 
 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.UserHandle;
+import android.provider.DeviceConfig;
 import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.StateControllerProto;
 import com.android.server.job.controllers.idle.CarIdlenessTracker;
@@ -89,6 +92,19 @@
         }
     }
 
+    @Override
+    public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
+            @NonNull String key) {
+        mIdleTracker.processConstant(properties, key);
+    }
+
+    @Override
+    @GuardedBy("mLock")
+    public void onBatteryStateChangedLocked() {
+        mIdleTracker.onBatteryStateChanged(
+                mService.isBatteryCharging(), mService.isBatteryNotLow());
+    }
+
     /**
      * State-change notifications from the idleness tracker
      */
@@ -119,7 +135,16 @@
         } else {
             mIdleTracker = new DeviceIdlenessTracker();
         }
-        mIdleTracker.startTracking(ctx, this);
+        mIdleTracker.startTracking(ctx, mService, this);
+    }
+
+    @Override
+    public void dumpConstants(IndentingPrintWriter pw) {
+        pw.println();
+        pw.println("IdleController:");
+        pw.increaseIndent();
+        mIdleTracker.dumpConstants(pw);
+        pw.decreaseIndent();
     }
 
     @Override
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java
index c458cae..ba0e633 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java
@@ -16,10 +16,13 @@
 
 package com.android.server.job.controllers.idle;
 
+import android.annotation.NonNull;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.provider.DeviceConfig;
+import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -73,7 +76,8 @@
     }
 
     @Override
-    public void startTracking(Context context, IdlenessListener listener) {
+    public void startTracking(Context context, JobSchedulerService service,
+            IdlenessListener listener) {
         mIdleListener = listener;
 
         IntentFilter filter = new IntentFilter();
@@ -94,6 +98,15 @@
         context.registerReceiver(this, filter, null, AppSchedulingModuleThread.getHandler());
     }
 
+    /** Process the specified constant and update internal constants if relevant. */
+    public void processConstant(@NonNull DeviceConfig.Properties properties,
+            @NonNull String key) {
+    }
+
+    @Override
+    public void onBatteryStateChanged(boolean isCharging, boolean isBatteryNotLow) {
+    }
+
     @Override
     public void dump(PrintWriter pw) {
         pw.print("  mIdle: "); pw.println(mIdle);
@@ -119,6 +132,10 @@
     }
 
     @Override
+    public void dumpConstants(IndentingPrintWriter pw) {
+    }
+
+    @Override
     public void onReceive(Context context, Intent intent) {
         final String action = intent.getAction();
         logIfDebug("Received action: " + action);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java
index c943e73..7dd3d13 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java
@@ -17,9 +17,12 @@
 package com.android.server.job.controllers.idle;
 
 import static android.app.UiModeManager.PROJECTION_TYPE_NONE;
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 
 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
 
+import android.annotation.NonNull;
 import android.app.AlarmManager;
 import android.app.UiModeManager;
 import android.content.BroadcastReceiver;
@@ -27,10 +30,13 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.PowerManager;
+import android.provider.DeviceConfig;
+import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.AppSchedulingModuleThread;
 import com.android.server.am.ActivityManagerService;
 import com.android.server.job.JobSchedulerService;
@@ -45,17 +51,38 @@
     private static final boolean DEBUG = JobSchedulerService.DEBUG
             || Log.isLoggable(TAG, Log.DEBUG);
 
+    /** Prefix to use with all constant keys in order to "sub-namespace" the keys. */
+    private static final String IC_DIT_CONSTANT_PREFIX = "ic_dit_";
+    @VisibleForTesting
+    static final String KEY_INACTIVITY_IDLE_THRESHOLD_MS =
+            IC_DIT_CONSTANT_PREFIX + "inactivity_idle_threshold_ms";
+    @VisibleForTesting
+    static final String KEY_INACTIVITY_STABLE_POWER_IDLE_THRESHOLD_MS =
+            IC_DIT_CONSTANT_PREFIX + "inactivity_idle_stable_power_threshold_ms";
+    private static final String KEY_IDLE_WINDOW_SLOP_MS =
+            IC_DIT_CONSTANT_PREFIX + "idle_window_slop_ms";
+
     private AlarmManager mAlarm;
     private PowerManager mPowerManager;
 
     // After construction, mutations of idle/screen-on/projection states will only happen
     // on the JobScheduler thread, either in onReceive(), in an alarm callback, or in on.*Changed.
     private long mInactivityIdleThreshold;
+    private long mInactivityStablePowerIdleThreshold;
     private long mIdleWindowSlop;
+    /** Stable power is defined as "charging + battery not low." */
+    private boolean mIsStablePower;
     private boolean mIdle;
     private boolean mScreenOn;
     private boolean mDockIdle;
     private boolean mProjectionActive;
+
+    /**
+     * Time (in the elapsed realtime timebase) when the idleness check was scheduled. This should
+     * be a negative value if the device is not in state to be considered idle.
+     */
+    private long mIdlenessCheckScheduledElapsed = -1;
+
     private IdlenessListener mIdleListener;
     private final UiModeManager.OnProjectionStateChangedListener mOnProjectionStateChangedListener =
             this::onProjectionStateChanged;
@@ -76,10 +103,14 @@
     }
 
     @Override
-    public void startTracking(Context context, IdlenessListener listener) {
+    public void startTracking(Context context, JobSchedulerService service,
+            IdlenessListener listener) {
         mIdleListener = listener;
         mInactivityIdleThreshold = context.getResources().getInteger(
                 com.android.internal.R.integer.config_jobSchedulerInactivityIdleThreshold);
+        mInactivityStablePowerIdleThreshold = context.getResources().getInteger(
+                com.android.internal.R.integer
+                        .config_jobSchedulerInactivityIdleThresholdOnStablePower);
         mIdleWindowSlop = context.getResources().getInteger(
                 com.android.internal.R.integer.config_jobSchedulerIdleWindowSlop);
         mAlarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
@@ -107,6 +138,46 @@
         context.getSystemService(UiModeManager.class).addOnProjectionStateChangedListener(
                 UiModeManager.PROJECTION_TYPE_ALL, AppSchedulingModuleThread.getExecutor(),
                 mOnProjectionStateChangedListener);
+
+        mIsStablePower = service.isBatteryCharging() && service.isBatteryNotLow();
+    }
+
+    /** Process the specified constant and update internal constants if relevant. */
+    public void processConstant(@NonNull DeviceConfig.Properties properties,
+            @NonNull String key) {
+        switch (key) {
+            case KEY_INACTIVITY_IDLE_THRESHOLD_MS:
+                // Keep the threshold in the range [1 minute, 4 hours].
+                mInactivityIdleThreshold = Math.max(MINUTE_IN_MILLIS, Math.min(4 * HOUR_IN_MILLIS,
+                        properties.getLong(key, mInactivityIdleThreshold)));
+                // Don't bother updating any pending alarms. Just wait until the next time we
+                // attempt to check for idle state to use the new value.
+                break;
+            case KEY_INACTIVITY_STABLE_POWER_IDLE_THRESHOLD_MS:
+                // Keep the threshold in the range [1 minute, 4 hours].
+                mInactivityStablePowerIdleThreshold = Math.max(MINUTE_IN_MILLIS,
+                        Math.min(4 * HOUR_IN_MILLIS,
+                                properties.getLong(key, mInactivityStablePowerIdleThreshold)));
+                // Don't bother updating any pending alarms. Just wait until the next time we
+                // attempt to check for idle state to use the new value.
+                break;
+            case KEY_IDLE_WINDOW_SLOP_MS:
+                // Keep the slop in the range [1 minute, 15 minutes].
+                mIdleWindowSlop = Math.max(MINUTE_IN_MILLIS, Math.min(15 * MINUTE_IN_MILLIS,
+                        properties.getLong(key, mIdleWindowSlop)));
+                // Don't bother updating any pending alarms. Just wait until the next time we
+                // attempt to check for idle state to use the new value.
+                break;
+        }
+    }
+
+    @Override
+    public void onBatteryStateChanged(boolean isCharging, boolean isBatteryNotLow) {
+        final boolean isStablePower = isCharging && isBatteryNotLow;
+        if (mIsStablePower != isStablePower) {
+            mIsStablePower = isStablePower;
+            maybeScheduleIdlenessCheck("stable power changed");
+        }
     }
 
     private void onProjectionStateChanged(@UiModeManager.ProjectionType int activeProjectionTypes,
@@ -134,8 +205,10 @@
     public void dump(PrintWriter pw) {
         pw.print("  mIdle: "); pw.println(mIdle);
         pw.print("  mScreenOn: "); pw.println(mScreenOn);
+        pw.print("  mIsStablePower: "); pw.println(mIsStablePower);
         pw.print("  mDockIdle: "); pw.println(mDockIdle);
         pw.print("  mProjectionActive: "); pw.println(mProjectionActive);
+        pw.print("  mIdlenessCheckScheduledElapsed: "); pw.println(mIdlenessCheckScheduledElapsed);
     }
 
     @Override
@@ -162,6 +235,17 @@
     }
 
     @Override
+    public void dumpConstants(IndentingPrintWriter pw) {
+        pw.println("DeviceIdlenessTracker:");
+        pw.increaseIndent();
+        pw.print(KEY_INACTIVITY_IDLE_THRESHOLD_MS, mInactivityIdleThreshold).println();
+        pw.print(KEY_INACTIVITY_STABLE_POWER_IDLE_THRESHOLD_MS, mInactivityStablePowerIdleThreshold)
+                .println();
+        pw.print(KEY_IDLE_WINDOW_SLOP_MS, mIdleWindowSlop).println();
+        pw.decreaseIndent();
+    }
+
+    @Override
     public void onReceive(Context context, Intent intent) {
         final String action = intent.getAction();
         if (DEBUG) {
@@ -220,9 +304,24 @@
     private void maybeScheduleIdlenessCheck(String reason) {
         if ((!mScreenOn || mDockIdle) && !mProjectionActive) {
             final long nowElapsed = sElapsedRealtimeClock.millis();
-            final long when = nowElapsed + mInactivityIdleThreshold;
+            final long inactivityThresholdMs = mIsStablePower
+                    ? mInactivityStablePowerIdleThreshold : mInactivityIdleThreshold;
+            if (mIdlenessCheckScheduledElapsed >= 0) {
+                if (mIdlenessCheckScheduledElapsed + inactivityThresholdMs <= nowElapsed) {
+                    if (DEBUG) {
+                        Slog.v(TAG, "Previous idle check @ " + mIdlenessCheckScheduledElapsed
+                                + " allows device to be idle now");
+                    }
+                    handleIdleTrigger();
+                    return;
+                }
+            } else {
+                mIdlenessCheckScheduledElapsed = nowElapsed;
+            }
+            final long when = mIdlenessCheckScheduledElapsed + inactivityThresholdMs;
             if (DEBUG) {
-                Slog.v(TAG, "Scheduling idle : " + reason + " now:" + nowElapsed + " when=" + when);
+                Slog.v(TAG, "Scheduling idle : " + reason + " now:" + nowElapsed
+                        + " checkElapsed=" + mIdlenessCheckScheduledElapsed + " when=" + when);
             }
             mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                     when, mIdleWindowSlop, "JS idleness",
@@ -232,6 +331,7 @@
 
     private void cancelIdlenessCheck() {
         mAlarm.cancel(mIdleAlarmListener);
+        mIdlenessCheckScheduledElapsed = -1;
     }
 
     private void handleIdleTrigger() {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/IdlenessTracker.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/IdlenessTracker.java
index cdab7e5..92ad4df 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/IdlenessTracker.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/IdlenessTracker.java
@@ -16,9 +16,14 @@
 
 package com.android.server.job.controllers.idle;
 
+import android.annotation.NonNull;
 import android.content.Context;
+import android.provider.DeviceConfig;
+import android.util.IndentingPrintWriter;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.server.job.JobSchedulerService;
+
 import java.io.PrintWriter;
 
 public interface IdlenessTracker {
@@ -29,7 +34,7 @@
      * non-interacting state.  When the idle state changes thereafter, the given
      * listener must be called to report the new state.
      */
-    void startTracking(Context context, IdlenessListener listener);
+    void startTracking(Context context, JobSchedulerService service, IdlenessListener listener);
 
     /**
      * Report whether the device is currently considered "idle" for purposes of
@@ -40,6 +45,12 @@
      */
     boolean isIdle();
 
+    /** Process the specified constant and update internal constants if relevant. */
+    void processConstant(@NonNull DeviceConfig.Properties properties, @NonNull String key);
+
+    /** Called when the battery state changes. */
+    void onBatteryStateChanged(boolean isCharging, boolean isBatteryNotLow);
+
     /**
      * Dump useful information about tracked idleness-related state in plaintext.
      */
@@ -49,4 +60,7 @@
      * Dump useful information about tracked idleness-related state to proto.
      */
     void dump(ProtoOutputStream proto, long fieldId);
+
+    /** Dump any internal constants the tracker may have. */
+    void dumpConstants(IndentingPrintWriter pw);
 }
diff --git a/core/api/current.txt b/core/api/current.txt
index e02803d..013010b 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9321,6 +9321,21 @@
     field public static final int USER_INTERACTION = 7; // 0x7
   }
 
+  @FlaggedApi("android.app.usage.filter_based_event_query_api") public final class UsageEventsQuery implements android.os.Parcelable {
+    method public int describeContents();
+    method public long getBeginTimeMillis();
+    method public long getEndTimeMillis();
+    method @NonNull public java.util.Set<java.lang.Integer> getEventTypes();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.usage.UsageEventsQuery> CREATOR;
+  }
+
+  public static final class UsageEventsQuery.Builder {
+    ctor public UsageEventsQuery.Builder(long, long);
+    method @NonNull public android.app.usage.UsageEventsQuery.Builder addEventTypes(@NonNull int...);
+    method @NonNull public android.app.usage.UsageEventsQuery build();
+  }
+
   public final class UsageStats implements android.os.Parcelable {
     ctor public UsageStats(android.app.usage.UsageStats);
     method public void add(android.app.usage.UsageStats);
@@ -9345,6 +9360,7 @@
     method public java.util.List<android.app.usage.ConfigurationStats> queryConfigurations(int, long, long);
     method public java.util.List<android.app.usage.EventStats> queryEventStats(int, long, long);
     method public android.app.usage.UsageEvents queryEvents(long, long);
+    method @FlaggedApi("android.app.usage.filter_based_event_query_api") @NonNull @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public android.app.usage.UsageEvents queryEvents(@NonNull android.app.usage.UsageEventsQuery);
     method public android.app.usage.UsageEvents queryEventsForSelf(long, long);
     method public java.util.List<android.app.usage.UsageStats> queryUsageStats(int, long, long);
     field public static final int INTERVAL_BEST = 4; // 0x4
@@ -18617,6 +18633,7 @@
     method @Deprecated @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
     method @FlaggedApi("android.hardware.biometrics.add_key_agreement_crypto_object") @Nullable public javax.crypto.KeyAgreement getKeyAgreement();
     method @Nullable public javax.crypto.Mac getMac();
+    method @FlaggedApi("android.hardware.biometrics.get_op_id_crypto_object") public long getOpId();
     method @Nullable public android.security.identity.PresentationSession getPresentationSession();
     method @Nullable public java.security.Signature getSignature();
   }
@@ -25850,15 +25867,15 @@
     method public abstract void onDisconnect(android.media.midi.MidiReceiver);
   }
 
-  @FlaggedApi("com.android.media.midi.flags.virtual_ump") public abstract class MidiUmpDeviceService extends android.app.Service {
+  @FlaggedApi("android.media.midi.virtual_ump") public abstract class MidiUmpDeviceService extends android.app.Service {
     ctor public MidiUmpDeviceService();
-    method @FlaggedApi("com.android.media.midi.flags.virtual_ump") @Nullable public final android.media.midi.MidiDeviceInfo getDeviceInfo();
-    method @FlaggedApi("com.android.media.midi.flags.virtual_ump") @NonNull public final java.util.List<android.media.midi.MidiReceiver> getOutputPortReceivers();
-    method @FlaggedApi("com.android.media.midi.flags.virtual_ump") @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
-    method @FlaggedApi("com.android.media.midi.flags.virtual_ump") public void onClose();
-    method @FlaggedApi("com.android.media.midi.flags.virtual_ump") public void onDeviceStatusChanged(@NonNull android.media.midi.MidiDeviceStatus);
-    method @FlaggedApi("com.android.media.midi.flags.virtual_ump") @NonNull public abstract java.util.List<android.media.midi.MidiReceiver> onGetInputPortReceivers();
-    field @FlaggedApi("com.android.media.midi.flags.virtual_ump") public static final String SERVICE_INTERFACE = "android.media.midi.MidiUmpDeviceService";
+    method @FlaggedApi("android.media.midi.virtual_ump") @Nullable public final android.media.midi.MidiDeviceInfo getDeviceInfo();
+    method @FlaggedApi("android.media.midi.virtual_ump") @NonNull public final java.util.List<android.media.midi.MidiReceiver> getOutputPortReceivers();
+    method @FlaggedApi("android.media.midi.virtual_ump") @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
+    method @FlaggedApi("android.media.midi.virtual_ump") public void onClose();
+    method @FlaggedApi("android.media.midi.virtual_ump") public void onDeviceStatusChanged(@NonNull android.media.midi.MidiDeviceStatus);
+    method @FlaggedApi("android.media.midi.virtual_ump") @NonNull public abstract java.util.List<android.media.midi.MidiReceiver> onGetInputPortReceivers();
+    field @FlaggedApi("android.media.midi.virtual_ump") public static final String SERVICE_INTERFACE = "android.media.midi.MidiUmpDeviceService";
   }
 
 }
@@ -49813,6 +49830,7 @@
     method public default void removeOnBufferTransformHintChangedListener(@NonNull android.view.AttachedSurfaceControl.OnBufferTransformHintChangedListener);
     method public default void setChildBoundingInsets(@NonNull android.graphics.Rect);
     method public default void setTouchableRegion(@Nullable android.graphics.Region);
+    method @FlaggedApi("com.android.window.flags.transfer_gesture_to_embedded") public default boolean transferHostTouchGestureToEmbedded(@NonNull android.view.SurfaceControlViewHost.SurfacePackage);
   }
 
   @UiThread public static interface AttachedSurfaceControl.OnBufferTransformHintChangedListener {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 6a5d07b..346d62b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -261,6 +261,7 @@
     field public static final String PROVIDE_RESOLVER_RANKER_SERVICE = "android.permission.PROVIDE_RESOLVER_RANKER_SERVICE";
     field public static final String PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT";
     field public static final String PROVISION_DEMO_DEVICE = "android.permission.PROVISION_DEMO_DEVICE";
+    field @FlaggedApi("android.content.pm.quarantined_enabled") public static final String QUARANTINE_APPS = "android.permission.QUARANTINE_APPS";
     field public static final String QUERY_ADMIN_POLICY = "android.permission.QUERY_ADMIN_POLICY";
     field public static final String QUERY_CLONED_APPS = "android.permission.QUERY_CLONED_APPS";
     field @Deprecated public static final String QUERY_TIME_ZONE_RULES = "android.permission.QUERY_TIME_ZONE_RULES";
@@ -4089,7 +4090,7 @@
     field public static final int PROTECTION_FLAG_MODULE = 4194304; // 0x400000
     field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
     field public static final int PROTECTION_FLAG_RECENTS = 33554432; // 0x2000000
-    field public static final int PROTECTION_FLAG_RETAIL_DEMO = 16777216; // 0x1000000
+    field @Deprecated public static final int PROTECTION_FLAG_RETAIL_DEMO = 16777216; // 0x1000000
     field public static final int PROTECTION_FLAG_ROLE = 67108864; // 0x4000000
     field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
     field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
@@ -6750,7 +6751,7 @@
     method public boolean setUidDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>);
     method public boolean setUserIdDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>);
     method public String toLogFriendlyString();
-    method @FlaggedApi("com.android.media.audio.flags.audio_policy_update_mixing_rules_api") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int updateMixingRules(@NonNull java.util.List<android.util.Pair<android.media.audiopolicy.AudioMix,android.media.audiopolicy.AudioMixingRule>>);
+    method @FlaggedApi("android.media.audiopolicy.audio_policy_update_mixing_rules_api") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int updateMixingRules(@NonNull java.util.List<android.util.Pair<android.media.audiopolicy.AudioMix,android.media.audiopolicy.AudioMixingRule>>);
     field public static final int FOCUS_POLICY_DUCKING_DEFAULT = 0; // 0x0
     field public static final int FOCUS_POLICY_DUCKING_IN_APP = 0; // 0x0
     field public static final int FOCUS_POLICY_DUCKING_IN_POLICY = 1; // 0x1
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index b5468dc..6643663 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -575,7 +575,6 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwnerOnly(@NonNull android.content.ComponentName, int);
     method public void setDeviceOwnerType(@NonNull android.content.ComponentName, int);
     method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public void setNextOperationSafety(int, int);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setOverrideKeepProfilesRunning(boolean);
     method @RequiresPermission(anyOf={android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}, conditional=true) public void setProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName, boolean);
     method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean triggerDevicePolicyEngineMigration(boolean);
     field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
@@ -1802,8 +1801,8 @@
 
   public class AudioManager {
     method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int abandonAudioFocusForTest(@NonNull android.media.AudioFocusRequest, @NonNull String);
-    method @FlaggedApi("com.android.media.audio.flags.focus_freeze_test_api") @RequiresPermission("Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED") public boolean enterAudioFocusFreezeForTest(@NonNull java.util.List<java.lang.Integer>);
-    method @FlaggedApi("com.android.media.audio.flags.focus_freeze_test_api") @RequiresPermission("Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED") public boolean exitAudioFocusFreezeForTest();
+    method @FlaggedApi("android.media.audio.focus_freeze_test_api") @RequiresPermission("Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED") public boolean enterAudioFocusFreezeForTest(@NonNull java.util.List<java.lang.Integer>);
+    method @FlaggedApi("android.media.audio.focus_freeze_test_api") @RequiresPermission("Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED") public boolean exitAudioFocusFreezeForTest();
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public void forceComputeCsdOnAllDevices(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public void forceUseFrameworkMel(boolean);
     method @NonNull @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public android.media.AudioRecord getCallDownlinkExtractionAudioRecord(@NonNull android.media.AudioFormat);
@@ -1811,9 +1810,9 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public float getCsd();
     method @Nullable public static android.media.AudioDeviceInfo getDeviceInfoFromType(int);
     method @IntRange(from=0) @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFadeOutDurationOnFocusLossMillis(@NonNull android.media.AudioAttributes);
-    method @FlaggedApi("com.android.media.audio.flags.focus_freeze_test_api") @NonNull @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public java.util.List<java.lang.Integer> getFocusDuckedUidsForTest();
-    method @FlaggedApi("com.android.media.audio.flags.focus_freeze_test_api") @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFocusFadeOutDurationForTest();
-    method @FlaggedApi("com.android.media.audio.flags.focus_freeze_test_api") @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFocusUnmuteDelayAfterFadeOutForTest();
+    method @FlaggedApi("android.media.audio.focus_freeze_test_api") @NonNull @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public java.util.List<java.lang.Integer> getFocusDuckedUidsForTest();
+    method @FlaggedApi("android.media.audio.focus_freeze_test_api") @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFocusFadeOutDurationForTest();
+    method @FlaggedApi("android.media.audio.focus_freeze_test_api") @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFocusUnmuteDelayAfterFadeOutForTest();
     method @Nullable public static android.media.AudioHalVersionInfo getHalVersion();
     method public static final int[] getPublicStreamTypes();
     method @NonNull public java.util.List<java.lang.Integer> getReportedSurroundFormats();
@@ -3573,8 +3572,8 @@
     method public default void holdLock(android.os.IBinder, int);
     method public default boolean isGlobalKey(int);
     method public default boolean isTaskSnapshotSupported();
-    method @FlaggedApi("REPLACE_CONTENT_WITH_MIRROR") @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public default boolean replaceContentOnDisplayWithMirror(int, @NonNull android.view.Window);
-    method @FlaggedApi("REPLACE_CONTENT_WITH_MIRROR") @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public default boolean replaceContentOnDisplayWithSc(int, @NonNull android.view.SurfaceControl);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public default boolean replaceContentOnDisplayWithMirror(int, @NonNull android.view.Window);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public default boolean replaceContentOnDisplayWithSc(int, @NonNull android.view.SurfaceControl);
     method public default void setDisplayImePolicy(int, int);
     method public default void setShouldShowSystemDecors(int, boolean);
     method public default void setShouldShowWithInsecureKeyguard(int, boolean);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a46c100..4c70c91 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -17048,23 +17048,6 @@
     }
 
     /**
-     * Overrides the effective cached value of enable_keep_profiles_running for testing purposes.
-     *
-     * @hide
-     */
-    @TestApi
-    @RequiresPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
-    public void setOverrideKeepProfilesRunning(boolean enabled) {
-        if (mService != null) {
-            try {
-                mService.setOverrideKeepProfilesRunning(enabled);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-    }
-
-    /**
      * Triggers the data migration of device policies for existing DPCs to the Device Policy Engine.
      * If {@code forceMigration} is set to {@code true} it skips the prerequisite checks before
      * triggering the migration.
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index 8dd50f0..304359b 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -312,21 +312,11 @@
             int targetUserId);
 
     /**
-     * Returns whether new "turn off work" behavior is enabled via feature flag.
-     */
-    public abstract boolean isKeepProfilesRunningEnabled();
-
-    /**
      * True if either the entire device or the user is organization managed.
      */
     public abstract boolean isUserOrganizationManaged(@UserIdInt int userId);
 
     /**
-     * Returns the list of packages suspended by admin on a given user.
-     */
-    public abstract Set<String> getPackagesSuspendedByAdmin(@UserIdInt int userId);
-
-    /**
      * Returns whether the application exemptions feature flag is enabled.
      */
     public abstract boolean isApplicationExemptionsFlagEnabled();
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 58f9d57..6fe40be 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -603,8 +603,6 @@
 
     DevicePolicyState getDevicePolicyState();
 
-    void setOverrideKeepProfilesRunning(boolean enabled);
-
     boolean triggerDevicePolicyEngineMigration(boolean forceMigration);
 
     boolean isDeviceFinanced(String callerPackageName);
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 49543a1..ebd5d64 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -20,10 +20,9 @@
 import android.app.usage.BroadcastResponseStats;
 import android.app.usage.BroadcastResponseStatsList;
 import android.app.usage.UsageEvents;
+import android.app.usage.UsageEventsQuery;
 import android.content.pm.ParceledListSlice;
 
-import java.util.Map;
-
 /**
  * System private API for talking with the UsageStatsManagerService.
  *
@@ -42,6 +41,8 @@
     UsageEvents queryEventsForPackage(long beginTime, long endTime, String callingPackage);
     UsageEvents queryEventsForUser(long beginTime, long endTime, int userId, String callingPackage);
     UsageEvents queryEventsForPackageForUser(long beginTime, long endTime, int userId, String pkg, String callingPackage);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)")
+    UsageEvents queryEventsWithFilter(in UsageEventsQuery query, String callingPackage);
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     void setAppInactive(String packageName, boolean inactive, int userId);
     boolean isAppStandbyEnabled();
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index c188686..1eb452c 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -349,6 +349,47 @@
          */
         public static final int MAX_EVENT_TYPE = 31;
 
+        /**
+         * Keep in sync with the event types defined above.
+         * @hide
+         */
+        @IntDef(flag = false, value = {
+                NONE,
+                ACTIVITY_RESUMED,
+                ACTIVITY_PAUSED,
+                END_OF_DAY,
+                CONTINUE_PREVIOUS_DAY,
+                CONFIGURATION_CHANGE,
+                SYSTEM_INTERACTION,
+                USER_INTERACTION,
+                SHORTCUT_INVOCATION,
+                CHOOSER_ACTION,
+                NOTIFICATION_SEEN,
+                STANDBY_BUCKET_CHANGED,
+                NOTIFICATION_INTERRUPTION,
+                SLICE_PINNED_PRIV,
+                SLICE_PINNED,
+                SCREEN_INTERACTIVE,
+                SCREEN_NON_INTERACTIVE,
+                KEYGUARD_SHOWN,
+                KEYGUARD_HIDDEN,
+                FOREGROUND_SERVICE_START,
+                FOREGROUND_SERVICE_STOP,
+                CONTINUING_FOREGROUND_SERVICE,
+                ROLLOVER_FOREGROUND_SERVICE,
+                ACTIVITY_STOPPED,
+                ACTIVITY_DESTROYED,
+                FLUSH_TO_DISK,
+                DEVICE_SHUTDOWN,
+                DEVICE_STARTUP,
+                USER_UNLOCKED,
+                USER_STOPPED,
+                LOCUS_ID_SET,
+                APP_COMPONENT_USED,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface EventType {}
+
         /** @hide */
         public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
 
diff --git a/core/java/android/app/usage/UsageEventsQuery.aidl b/core/java/android/app/usage/UsageEventsQuery.aidl
new file mode 100644
index 0000000..5ed370d
--- /dev/null
+++ b/core/java/android/app/usage/UsageEventsQuery.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.app.usage;
+
+parcelable UsageEventsQuery;
\ No newline at end of file
diff --git a/core/java/android/app/usage/UsageEventsQuery.java b/core/java/android/app/usage/UsageEventsQuery.java
new file mode 100644
index 0000000..8c63d18
--- /dev/null
+++ b/core/java/android/app/usage/UsageEventsQuery.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.app.usage;
+
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.app.usage.UsageEvents.Event;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArraySet;
+
+import com.android.internal.util.ArrayUtils;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * An Object-Oriented representation for a {@link UsageEvents} query.
+ * Used by {@link UsageStatsManager#queryEvents(UsageEventsQuery)} call.
+ */
+@FlaggedApi(Flags.FLAG_FILTER_BASED_EVENT_QUERY_API)
+public final class UsageEventsQuery implements Parcelable {
+    private final @CurrentTimeMillisLong long mBeginTimeMillis;
+    private final @CurrentTimeMillisLong long mEndTimeMillis;
+    private final @Event.EventType int[] mEventTypes;
+
+    private UsageEventsQuery(@NonNull Builder builder) {
+        mBeginTimeMillis = builder.mBeginTimeMillis;
+        mEndTimeMillis = builder.mEndTimeMillis;
+        mEventTypes = ArrayUtils.convertToIntArray(builder.mEventTypes);
+    }
+
+    private UsageEventsQuery(Parcel in) {
+        mBeginTimeMillis = in.readLong();
+        mEndTimeMillis = in.readLong();
+        int eventTypesLength = in.readInt();
+        mEventTypes = new int[eventTypesLength];
+        in.readIntArray(mEventTypes);
+    }
+
+    /**
+     * Returns the inclusive timestamp to indicate the beginning of the range of events.
+     * Defined in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}.
+     */
+    public @CurrentTimeMillisLong long getBeginTimeMillis() {
+        return mBeginTimeMillis;
+    }
+
+    /**
+     * Returns the exclusive timpstamp to indicate the end of the range of events.
+     * Defined in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}.
+     */
+    public @CurrentTimeMillisLong long getEndTimeMillis() {
+        return mEndTimeMillis;
+    }
+
+    /**
+     * Returns the set of usage event types for the query.
+     * <em>Note: An empty set indicates query for all usage events. </em>
+     */
+    public @NonNull Set<Integer> getEventTypes() {
+        if (ArrayUtils.isEmpty(mEventTypes)) {
+            return Collections.emptySet();
+        }
+
+        HashSet<Integer> eventTypeSet = new HashSet<>();
+        for (int eventType : mEventTypes) {
+            eventTypeSet.add(eventType);
+        }
+        return eventTypeSet;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeLong(mBeginTimeMillis);
+        dest.writeLong(mEndTimeMillis);
+        dest.writeInt(mEventTypes.length);
+        dest.writeIntArray(mEventTypes);
+    }
+
+    @NonNull
+    public static final Creator<UsageEventsQuery> CREATOR =
+            new Creator<UsageEventsQuery>() {
+                @Override
+                public UsageEventsQuery createFromParcel(Parcel in) {
+                    return new UsageEventsQuery(in);
+                }
+
+                @Override
+                public UsageEventsQuery[] newArray(int size) {
+                    return new UsageEventsQuery[size];
+                }
+            };
+
+    /** @hide */
+    public int[] getEventTypeFilter() {
+        return Arrays.copyOf(mEventTypes, mEventTypes.length);
+    }
+
+    /**
+     * Builder for UsageEventsQuery.
+     */
+    public static final class Builder {
+        private final @CurrentTimeMillisLong long mBeginTimeMillis;
+        private final @CurrentTimeMillisLong long mEndTimeMillis;
+        private final ArraySet<Integer> mEventTypes = new ArraySet<>();
+
+        /**
+         * Constructor that specifies the period for which to return events.
+         * @param beginTimeMillis Inclusive beginning timestamp, as per
+         *                        {@link java.lang.System#currentTimeMillis()}
+         * @param endTimeMillis Exclusive ending timestamp, as per
+         *                        {@link java.lang.System#currentTimeMillis()}
+         *
+         * @throws IllegalArgumentException if {@code beginTimeMillis} &lt;
+         *                                  {@code endTimeMillis}
+         */
+        public Builder(@CurrentTimeMillisLong long beginTimeMillis,
+                @CurrentTimeMillisLong long endTimeMillis) {
+            if (beginTimeMillis < 0 || endTimeMillis < beginTimeMillis) {
+                throw new IllegalArgumentException("Invalid period");
+            }
+            mBeginTimeMillis = beginTimeMillis;
+            mEndTimeMillis = endTimeMillis;
+        }
+
+        /**
+         * Builds a read-only UsageEventsQuery object.
+         */
+        public @NonNull UsageEventsQuery build() {
+            return new UsageEventsQuery(this);
+        }
+
+        /**
+         * Specifies the list of usage event types to be included in the query.
+         * @param eventTypes List of the usage event types. See {@link UsageEvents.Event}
+         *
+         * @throws llegalArgumentException if the event type is not valid.
+         */
+        public @NonNull Builder addEventTypes(@NonNull @Event.EventType int... eventTypes) {
+            for (int i = 0; i < eventTypes.length; i++) {
+                final int eventType = eventTypes[i];
+                if (eventType < Event.NONE || eventType > Event.MAX_EVENT_TYPE) {
+                    throw new IllegalArgumentException("Invalid usage event type: " + eventType);
+                }
+                mEventTypes.add(eventType);
+            }
+            return this;
+        }
+    }
+}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 2a10ed1..4f1c993 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -18,6 +18,7 @@
 
 import android.Manifest;
 import android.annotation.CurrentTimeMillisLong;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -581,6 +582,29 @@
     }
 
     /**
+     * Query for events with specific UsageEventsQuery object.
+     * <em>Note: if the user's device is not in an unlocked state (as defined by
+     * {@link UserManager#isUserUnlocked()}), then {@code null} will be returned.</em>
+     *
+     * @param query The query object used to specify the query parameters.
+     * @return A {@link UsageEvents}.
+     */
+    @FlaggedApi(Flags.FLAG_FILTER_BASED_EVENT_QUERY_API)
+    @NonNull
+    @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
+    public UsageEvents queryEvents(@NonNull UsageEventsQuery query) {
+        try {
+            UsageEvents iter = mService.queryEventsWithFilter(query, mContext.getOpPackageName());
+            if (iter != null) {
+                return iter;
+            }
+        } catch (RemoteException e) {
+            // fallthrough and return empty result.
+        }
+        return sEmptyResults;
+    }
+
+    /**
      * Like {@link #queryEvents(long, long)}, but only returns events for the calling package.
      * <em>Note: Starting from {@link android.os.Build.VERSION_CODES#R Android R}, if the user's
      * device is not in an unlocked state (as defined by {@link UserManager#isUserUnlocked()}),
diff --git a/core/java/android/app/usage/flags.aconfig b/core/java/android/app/usage/flags.aconfig
index 0b8e29f..a611255 100644
--- a/core/java/android/app/usage/flags.aconfig
+++ b/core/java/android/app/usage/flags.aconfig
@@ -28,3 +28,10 @@
     description: "Flag for parcelable usage event list"
     bug: "301254110"
 }
+
+flag {
+    name: "filter_based_event_query_api"
+    namespace: "backstage_power"
+    description: " Feature flag to support filter based event query API"
+    bug: "194321117"
+}
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index cdda12e..012b6c4 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -273,6 +273,9 @@
      * to the <code>retailDemo</code> value of
      * {@link android.R.attr#protectionLevel}.
      *
+     * @deprecated This flag has been replaced by the retail demo role and is a no-op since Android
+     *             V.
+     *
      * @hide
      */
     @SystemApi
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 7a43286..97bbfbb 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -21,6 +21,7 @@
 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 import static android.hardware.biometrics.BiometricManager.Authenticators;
 import static android.hardware.biometrics.Flags.FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT;
+import static android.hardware.biometrics.Flags.FLAG_GET_OP_ID_CRYPTO_OBJECT;
 
 import android.annotation.CallbackExecutor;
 import android.annotation.FlaggedApi;
@@ -851,6 +852,14 @@
         public @Nullable KeyAgreement getKeyAgreement() {
             return super.getKeyAgreement();
         }
+
+        /**
+         * Get the operation handle associated with this object or 0 if none.
+         */
+        @FlaggedApi(FLAG_GET_OP_ID_CRYPTO_OBJECT)
+        public long getOpId() {
+            return super.getOpId();
+        }
     }
 
     /**
diff --git a/core/java/android/hardware/biometrics/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java
index 39fbe83..8d3ea3f 100644
--- a/core/java/android/hardware/biometrics/CryptoObject.java
+++ b/core/java/android/hardware/biometrics/CryptoObject.java
@@ -154,7 +154,7 @@
      * @hide
      * @return the opId associated with this object or 0 if none
      */
-    public final long getOpId() {
+    public long getOpId() {
         if (mCrypto == null) {
             return 0;
         } else if (mCrypto instanceof IdentityCredential) {
diff --git a/core/java/android/hardware/biometrics/flags.aconfig b/core/java/android/hardware/biometrics/flags.aconfig
index 979f570..56ec763 100644
--- a/core/java/android/hardware/biometrics/flags.aconfig
+++ b/core/java/android/hardware/biometrics/flags.aconfig
@@ -14,3 +14,10 @@
   bug: "282058146"
 }
 
+flag {
+  name: "get_op_id_crypto_object"
+  namespace: "biometrics"
+  description: "Feature flag for adding a get operation id api to CryptoObject."
+  bug: "307601768"
+}
+
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index a3b836a..d4688f8 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -933,6 +933,12 @@
         rebootWipeUserData(context, shutdown, reason, force, false /* wipeEuicc */);
     }
 
+    /** {@hide} */
+    public static void rebootWipeUserData(Context context, boolean shutdown, String reason,
+            boolean force, boolean wipeEuicc) throws IOException {
+        rebootWipeUserData(context, shutdown, reason, force, wipeEuicc, false /* keepMemtagMode */);
+    }
+
     /**
      * Reboots the device and wipes the user data and cache
      * partitions.  This is sometimes called a "factory reset", which
@@ -948,6 +954,7 @@
      * @param force     whether the {@link UserManager.DISALLOW_FACTORY_RESET} user restriction
      *                  should be ignored
      * @param wipeEuicc whether wipe the euicc data
+     * @param keepMemtagMode whether to tell recovery to keep currently configured memtag mode
      *
      * @throws IOException  if writing the recovery command file
      * fails, or if the reboot itself fails.
@@ -956,7 +963,7 @@
      * @hide
      */
     public static void rebootWipeUserData(Context context, boolean shutdown, String reason,
-            boolean force, boolean wipeEuicc) throws IOException {
+            boolean force, boolean wipeEuicc, boolean keepMemtagMode) throws IOException {
         UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
         if (!force && um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) {
             throw new SecurityException("Wiping data is not allowed for this user.");
@@ -996,8 +1003,13 @@
             reasonArg = "--reason=" + sanitizeArg(reason + "," + timeStamp);
         }
 
+        String memtagArg = null;
+        if (keepMemtagMode) {
+            memtagArg = "--keep_memtag_mode";
+        }
+
         final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ;
-        bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);
+        bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg, memtagArg);
     }
 
     /**
diff --git a/core/java/android/service/notification/flags.aconfig b/core/java/android/service/notification/flags.aconfig
index 52d4d47..2a05c84 100644
--- a/core/java/android/service/notification/flags.aconfig
+++ b/core/java/android/service/notification/flags.aconfig
@@ -4,7 +4,7 @@
   name: "ranking_update_ashmem"
   namespace: "systemui"
   description: "This flag controls moving ranking update contents into ashmem"
-  bug: "284297289"
+  bug: "249848655"
 }
 
 flag {
diff --git a/core/java/android/view/AttachedSurfaceControl.java b/core/java/android/view/AttachedSurfaceControl.java
index d545751..fd5517d 100644
--- a/core/java/android/view/AttachedSurfaceControl.java
+++ b/core/java/android/view/AttachedSurfaceControl.java
@@ -231,4 +231,19 @@
     default void removeTrustedPresentationCallback(@NonNull SurfaceControl.Transaction t,
             @NonNull Consumer<Boolean> listener) {
     }
+
+    /**
+     * Transfer the currently in progress touch gesture from the host to the requested
+     * {@link SurfaceControlViewHost.SurfacePackage}. This requires that the
+     * SurfaceControlViewHost was created with the current host's inputToken.
+     *
+     * @param surfacePackage The SurfacePackage to transfer the gesture to.
+     * @return Whether the touch stream was transferred.
+     */
+    @FlaggedApi(Flags.FLAG_TRANSFER_GESTURE_TO_EMBEDDED)
+    default boolean transferHostTouchGestureToEmbedded(
+            @NonNull SurfaceControlViewHost.SurfacePackage surfacePackage) {
+        throw new UnsupportedOperationException(
+                "transferHostTouchGestureToEmbedded is unimplemented");
+    }
 }
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 7acf2f8..02e97da 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -357,4 +357,6 @@
     boolean cancelDraw(IWindow window);
 
     boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow);
+
+    boolean transferHostTouchGestureToEmbedded(IWindow hostWindow, IBinder transferTouchToken);
 }
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 9d88af9..451a71b 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -2671,7 +2671,7 @@
          *
          * @param minAlpha               The min alpha the {@link SurfaceControl} is required to
          *                               have to be considered inside the threshold.
-         * @param minFractionRendered    The min fraction of the SurfaceControl that was resented
+         * @param minFractionRendered    The min fraction of the SurfaceControl that was presented
          *                               to the user to be considered inside the threshold.
          * @param stabilityRequirementMs The time in milliseconds required for the
          *                               {@link SurfaceControl} to be in the threshold.
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 57b19a8..4056531 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -287,7 +287,8 @@
         }
 
         /**
-         * Returns an input token used which can be used to request focus on the embedded surface.
+         * Returns an input token used which can be used to request focus on the embedded surface
+         * or to transfer touch gesture to the embedded surface.
          *
          * @hide
          */
@@ -526,7 +527,8 @@
     }
 
     /**
-     * Returns an input token used which can be used to request focus on the embedded surface.
+     * Returns an input token used which can be used to request focus on the embedded surface
+     * or to transfer touch gesture to the embedded surface.
      *
      * @hide
      */
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 2422369..1ee303c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -11893,4 +11893,17 @@
         }
         Log.d(mTag, msg);
     }
+
+    @Override
+    public boolean transferHostTouchGestureToEmbedded(
+            @NonNull SurfaceControlViewHost.SurfacePackage surfacePackage) {
+        final IWindowSession realWm = WindowManagerGlobal.getWindowSession();
+        try {
+            return realWm.transferHostTouchGestureToEmbedded(mWindow,
+                    surfacePackage.getInputToken());
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+        return false;
+    }
 }
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 4f03ce9..cfec081 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -90,6 +90,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
@@ -5872,7 +5873,7 @@
      *
      * @hide
      */
-    @FlaggedApi("REPLACE_CONTENT_WITH_MIRROR")
+    @SuppressLint("UnflaggedApi") // The API is only used for tests.
     @TestApi
     @RequiresPermission(permission.ACCESS_SURFACE_FLINGER)
     default boolean replaceContentOnDisplayWithMirror(int displayId, @NonNull Window window) {
@@ -5888,7 +5889,7 @@
      *
      * @hide
      */
-    @FlaggedApi("REPLACE_CONTENT_WITH_MIRROR")
+    @SuppressLint("UnflaggedApi") // The API is only used for tests.
     @TestApi
     @RequiresPermission(permission.ACCESS_SURFACE_FLINGER)
     default boolean replaceContentOnDisplayWithSc(int displayId, @NonNull SurfaceControl sc) {
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 7c3b6ae..652fe24 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -656,6 +656,14 @@
         return false;
     }
 
+    @Override
+    public boolean transferHostTouchGestureToEmbedded(IWindow hostWindow,
+            IBinder embeddedInputToken) {
+        Log.e(TAG, "Received request to transferHostTouchGestureToEmbedded on"
+                + " WindowlessWindowManager. We shouldn't get here!");
+        return false;
+    }
+
     void setParentInterface(@Nullable ISurfaceControlViewHostParent parentInterface) {
         IBinder oldInterface = mParentInterface == null ? null : mParentInterface.asBinder();
         IBinder newInterface = parentInterface == null ? null : parentInterface.asBinder();
diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig
index 5ad5c79..68eddff 100644
--- a/core/java/android/window/flags/window_surfaces.aconfig
+++ b/core/java/android/window/flags/window_surfaces.aconfig
@@ -25,3 +25,10 @@
     is_fixed_read_only: true
     bug: "304508760"
 }
+
+flag {
+    namespace: "window_surfaces"
+    name: "transfer_gesture_to_embedded"
+    description: "Enable public API for Window Surfaces"
+    bug: "287076178"
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c75f996..ec302e7 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2358,6 +2358,15 @@
     <permission android:name="android.permission.SUSPEND_APPS"
         android:protectionLevel="signature|role" />
 
+    <!-- @SystemApi
+         @hide
+         @FlaggedApi("android.content.pm.quarantined_enabled")
+         Allows an application to quarantine other apps, which will prevent
+         them from running without explicit user action.
+    -->
+    <permission android:name="android.permission.QUARANTINE_APPS"
+        android:protectionLevel="internal|verifier" />
+
     <!-- Allows applications to discover and pair bluetooth devices.
          <p>Protection level: normal
     -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 95f1493..9f99dc9 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -301,7 +301,9 @@
             granted to the system companion device manager service -->
         <flag name="companion" value="0x800000" />
         <!-- Additional flag from base permission type: this permission will be granted to the
-             retail demo app, as defined by the OEM. -->
+             retail demo app, as defined by the OEM.
+             This flag has been replaced by the retail demo role and is a no-op since Android V.
+          -->
         <flag name="retailDemo" value="0x1000000" />
         <!-- Additional flag from base permission type: this permission will be granted to the
              recents app. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index dbce054..862e537 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4177,6 +4177,10 @@
     <!-- Inactivity threshold (in milliseconds) used in JobScheduler. JobScheduler will consider
          the device to be "idle" after being inactive for this long. -->
     <integer name="config_jobSchedulerInactivityIdleThreshold">1860000</integer>
+    <!-- Inactivity threshold (in milliseconds) used in JobScheduler. JobScheduler will consider
+         the device to be "idle" after being inactive for this long if the device is on stable
+         power. Stable power is defined as "charging + battery not low". -->
+    <integer name="config_jobSchedulerInactivityIdleThresholdOnStablePower">1860000</integer>
     <!-- The alarm window (in milliseconds) that JobScheduler uses to enter the idle state -->
     <integer name="config_jobSchedulerIdleWindowSlop">300000</integer>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a2f0086..e646548 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2882,6 +2882,7 @@
   <java-symbol type="integer" name="config_defaultNightMode" />
 
   <java-symbol type="integer" name="config_jobSchedulerInactivityIdleThreshold" />
+  <java-symbol type="integer" name="config_jobSchedulerInactivityIdleThresholdOnStablePower" />
   <java-symbol type="integer" name="config_jobSchedulerIdleWindowSlop" />
   <java-symbol type="bool" name="config_jobSchedulerRestrictBackgroundUser" />
   <java-symbol type="integer" name="config_jobSchedulerUserGracePeriod" />
diff --git a/core/tests/coretests/src/android/app/usage/UsageEventsQueryTest.java b/core/tests/coretests/src/android/app/usage/UsageEventsQueryTest.java
new file mode 100644
index 0000000..839b645
--- /dev/null
+++ b/core/tests/coretests/src/android/app/usage/UsageEventsQueryTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 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.
+ */
+package android.app.usage;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.app.usage.UsageEvents.Event;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Random;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class UsageEventsQueryTest {
+    @Test
+    public void testQueryDuration() {
+        // Test with negative beginTimeMillis.
+        long beginTimeMillis = -100;
+        long endTimeMillis = 100;
+        try {
+            UsageEventsQuery query = new UsageEventsQuery.Builder(beginTimeMillis, endTimeMillis)
+                    .build();
+            fail("beginTimeMillis should be a non-negative timestamp measured as the number of"
+                    + " milliseconds since 1970-01-01T00:00:00Z.");
+        } catch (IllegalArgumentException e) {
+            // Expected, fall through;
+        }
+
+        // Test with negative endTimeMillis.
+        beginTimeMillis = 1001;
+        endTimeMillis = -1;
+        try {
+            UsageEventsQuery query = new UsageEventsQuery.Builder(beginTimeMillis, endTimeMillis)
+                    .build();
+            fail("endTimeMillis should be a non-negative timestamp measured as the number of"
+                    + " milliseconds since 1970-01-01T00:00:00Z.");
+        } catch (IllegalArgumentException e) {
+            // Expected, fall through;
+        }
+
+        // Test with beginTimeMillis < endTimeMillis;
+        beginTimeMillis = 2001;
+        endTimeMillis = 1000;
+        try {
+            UsageEventsQuery query = new UsageEventsQuery.Builder(beginTimeMillis, endTimeMillis)
+                    .build();
+            fail("beginTimeMillis should be smaller than endTimeMillis");
+        } catch (IllegalArgumentException e) {
+            // Expected, fall through;
+        }
+
+        // Test with beginTimeMillis == endTimeMillis, valid.
+        beginTimeMillis = 1001;
+        endTimeMillis = 1001;
+        try {
+            UsageEventsQuery query = new UsageEventsQuery.Builder(beginTimeMillis, endTimeMillis)
+                    .build();
+            assertEquals(query.getBeginTimeMillis(), query.getEndTimeMillis());
+        } catch (IllegalArgumentException e) {
+            // Not expected for valid duration.
+            fail("Valid duration for beginTimeMillis=" + beginTimeMillis
+                    + ", endTimeMillis=" + endTimeMillis);
+        }
+
+        beginTimeMillis = 2001;
+        endTimeMillis = 3001;
+        try {
+            UsageEventsQuery query = new UsageEventsQuery.Builder(beginTimeMillis, endTimeMillis)
+                    .build();
+            assertEquals(query.getBeginTimeMillis(), 2001);
+            assertEquals(query.getEndTimeMillis(), 3001);
+        } catch (IllegalArgumentException e) {
+            // Not expected for valid duration.
+            fail("Valid duration for beginTimeMillis=" + beginTimeMillis
+                    + ", endTimeMillis=" + endTimeMillis);
+        }
+    }
+
+    @Test
+    public void testQueryEventTypes() {
+        Random rnd = new Random();
+        UsageEventsQuery.Builder queryBuilder = new UsageEventsQuery.Builder(1000, 2000);
+
+        // Test with invalid event type.
+        int eventType = Event.NONE - 1;
+        try {
+            queryBuilder.addEventTypes(eventType);
+            fail("Invalid event type: " + eventType);
+        } catch (IllegalArgumentException e) {
+            // Expected, fall through.
+        }
+
+        eventType = Event.MAX_EVENT_TYPE + 1;
+        try {
+            queryBuilder.addEventTypes(eventType);
+            fail("Invalid event type: " + eventType);
+        } catch (IllegalArgumentException e) {
+            // Expected, fall through.
+        }
+
+        // Test with valid and duplicate event types.
+        eventType = rnd.nextInt(Event.MAX_EVENT_TYPE + 1);
+        try {
+            UsageEventsQuery query = queryBuilder.addEventTypes(eventType, eventType, eventType)
+                    .build();
+            Set<Integer> eventTypeSet = query.getEventTypes();
+            assertEquals(eventTypeSet.size(), 1);
+            int type = eventTypeSet.iterator().next();
+            assertEquals(type, eventType);
+        } catch (IllegalArgumentException e) {
+            fail("Valid event type: " + eventType);
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
index 1df1136..5b2ffec 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
@@ -95,14 +95,6 @@
         <option name="pull-pattern-keys" value="perfetto_file_path"/>
         <option name="directory-keys"
                 value="/data/user/0/com.android.wm.shell.flicker/files"/>
-        <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
-        <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker.pip/files"/>
-        <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
-        <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
         <option name="collect-on-run-ended-only" value="true"/>
         <option name="clean-up" value="true"/>
     </metrics_collector>
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
index 1df1136..9f7d9fc 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
@@ -94,15 +94,7 @@
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
         <option name="pull-pattern-keys" value="perfetto_file_path"/>
         <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker/files"/>
-        <option name="directory-keys"
                 value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
-        <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker.pip/files"/>
-        <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
-        <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
         <option name="collect-on-run-ended-only" value="true"/>
         <option name="clean-up" value="true"/>
     </metrics_collector>
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
index 1df1136..882b200 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
@@ -94,15 +94,9 @@
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
         <option name="pull-pattern-keys" value="perfetto_file_path"/>
         <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker/files"/>
-        <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
-        <option name="directory-keys"
                 value="/data/user/0/com.android.wm.shell.flicker.pip/files"/>
         <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
-        <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
+                value="/data/user/0/com.android.wm.shell.flicker.pip.apps/files"/>
         <option name="collect-on-run-ended-only" value="true"/>
         <option name="clean-up" value="true"/>
     </metrics_collector>
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
index ca182fa..6df6539 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
@@ -57,6 +57,17 @@
         <option name="test-file-name" value="WMShellFlickerTestsPipAppsCSuite.apk"/>
         <option name="test-file-name" value="FlickerTestApp.apk"/>
     </target_preparer>
+
+    <!-- Needed for installing apk's from Play Store -->
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="aapt-version" value="AAPT2"/>
+        <option name="throw-if-not-found" value="false"/>
+        <option name="install-arg" value="-d"/>
+        <option name="install-arg" value="-g"/>
+        <option name="install-arg" value="-r"/>
+        <option name="test-file-name" value="pstash://com.netflix.mediaclient"/>
+    </target_preparer>
+
     <!-- Enable mocking GPS location by the test app -->
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command"
@@ -94,28 +105,8 @@
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
         <option name="pull-pattern-keys" value="perfetto_file_path"/>
         <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker/files"/>
-        <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
-        <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker.pip/files"/>
-        <option name="directory-keys"
                 value="/data/user/0/com.android.wm.shell.flicker.pip.apps/files"/>
-        <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
-        <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
         <option name="collect-on-run-ended-only" value="true"/>
         <option name="clean-up" value="true"/>
     </metrics_collector>
-
-    <!-- Needed for installing apk's from Play Store -->
-    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
-        <option name="aapt-version" value="AAPT2"/>
-        <option name="throw-if-not-found" value="false"/>
-        <option name="install-arg" value="-d"/>
-        <option name="install-arg" value="-g"/>
-        <option name="install-arg" value="-r"/>
-        <option name="test-file-name" value="pstash://com.netflix.mediaclient"/>
-    </target_preparer>
 </configuration>
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
index be5a27a..bd8b005 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
@@ -20,6 +20,8 @@
 import android.tools.common.Rotation
 import android.tools.common.traces.component.ComponentNameMatcher
 import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.flicker.junit.FlickerBuilderProvider
+import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.LegacyFlickerTest
 import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
 import com.android.wm.shell.flicker.pip.common.EnterPipTransition
@@ -29,6 +31,15 @@
 abstract class AppsEnterPipTransition(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) {
     protected abstract val standardAppHelper: StandardAppHelper
 
+    @FlickerBuilderProvider
+    override fun buildFlicker(): FlickerBuilder {
+        return FlickerBuilder(instrumentation).apply {
+            withoutScreenRecorder()
+            setup { flicker.scenario.setIsTablet(tapl.isTablet) }
+            transition()
+        }
+    }
+
     /** Checks [standardAppHelper] window remains visible throughout the animation */
     @Postsubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
index 1df1136..51a55e35 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
@@ -94,14 +94,6 @@
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
         <option name="pull-pattern-keys" value="perfetto_file_path"/>
         <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker/files"/>
-        <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
-        <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker.pip/files"/>
-        <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
-        <option name="directory-keys"
                 value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
         <option name="collect-on-run-ended-only" value="true"/>
         <option name="clean-up" value="true"/>
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
index 1df1136..fdda597 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
@@ -94,15 +94,7 @@
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
         <option name="pull-pattern-keys" value="perfetto_file_path"/>
         <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker/files"/>
-        <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
-        <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker.pip/files"/>
-        <option name="directory-keys"
                 value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
-        <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
         <option name="collect-on-run-ended-only" value="true"/>
         <option name="clean-up" value="true"/>
     </metrics_collector>
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt
index 0f3e0f5..e9363f7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt
@@ -38,7 +38,7 @@
      * executions
      */
     @FlickerBuilderProvider
-    fun buildFlicker(): FlickerBuilder {
+    open fun buildFlicker(): FlickerBuilder {
         return FlickerBuilder(instrumentation).apply {
             setup { flicker.scenario.setIsTablet(tapl.isTablet) }
             transition()
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index abd9284..576ebc1 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -24,6 +24,7 @@
 #include <SkColor.h>
 #include <android-base/stringprintf.h>
 #include <android-base/thread_annotations.h>
+#include <com_android_input_flags.h>
 #include <ftl/enum.h>
 
 #include <mutex>
@@ -34,6 +35,8 @@
 #define INDENT2 "    "
 #define INDENT3 "      "
 
+namespace input_flags = com::android::input::flags;
+
 namespace android {
 
 namespace {
@@ -63,10 +66,20 @@
 
 std::shared_ptr<PointerController> PointerController::create(
         const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
-        SpriteController& spriteController, bool enabled) {
+        SpriteController& spriteController, bool enabled, ControllerType type) {
     // using 'new' to access non-public constructor
-    std::shared_ptr<PointerController> controller = std::shared_ptr<PointerController>(
-            new PointerController(policy, looper, spriteController, enabled));
+    std::shared_ptr<PointerController> controller;
+    switch (type) {
+        case ControllerType::MOUSE:
+            controller = std::shared_ptr<PointerController>(
+                    new MousePointerController(policy, looper, spriteController, enabled));
+            break;
+        case ControllerType::LEGACY:
+        default:
+            controller = std::shared_ptr<PointerController>(
+                    new PointerController(policy, looper, spriteController, enabled));
+            break;
+    }
 
     /*
      * Now we need to hook up the constructed PointerController object to its callbacks.
@@ -375,4 +388,13 @@
     return dump;
 }
 
+// --- MousePointerController ---
+
+MousePointerController::MousePointerController(const sp<PointerControllerPolicyInterface>& policy,
+                                               const sp<Looper>& looper,
+                                               SpriteController& spriteController, bool enabled)
+      : PointerController(policy, looper, spriteController, enabled) {
+    PointerController::setPresentation(Presentation::POINTER);
+}
+
 } // namespace android
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index aa7ca3c..08e19a0 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -47,7 +47,8 @@
 public:
     static std::shared_ptr<PointerController> create(
             const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
-            SpriteController& spriteController, bool enabled);
+            SpriteController& spriteController, bool enabled,
+            ControllerType type = ControllerType::LEGACY);
 
     ~PointerController() override;
 
@@ -75,7 +76,7 @@
     void onDisplayInfosChangedLocked(const std::vector<gui::DisplayInfo>& displayInfos)
             REQUIRES(getLock());
 
-    std::string dump();
+    std::string dump() override;
 
 protected:
     using WindowListenerConsumer =
@@ -87,10 +88,10 @@
                       WindowListenerConsumer registerListener,
                       WindowListenerConsumer unregisterListener);
 
-private:
     PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
                       SpriteController& spriteController, bool enabled);
 
+private:
     friend PointerControllerContext::LooperCallback;
     friend PointerControllerContext::MessageHandler;
 
@@ -135,6 +136,24 @@
     void clearSpotsLocked() REQUIRES(getLock());
 };
 
+class MousePointerController : public PointerController {
+public:
+    /** A version of PointerController that controls one mouse pointer. */
+    MousePointerController(const sp<PointerControllerPolicyInterface>& policy,
+                           const sp<Looper>& looper, SpriteController& spriteController,
+                           bool enabled);
+
+    void setPresentation(Presentation) override {
+        LOG_ALWAYS_FATAL("Should not be called");
+    }
+    void setSpots(const PointerCoords*, const uint32_t*, BitSet32, int32_t) override {
+        LOG_ALWAYS_FATAL("Should not be called");
+    }
+    void clearSpots() override {
+        LOG_ALWAYS_FATAL("Should not be called");
+    }
+};
+
 } // namespace android
 
 #endif // _UI_POINTER_CONTROLLER_H
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index eea6357..5d211f4 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -20,8 +20,8 @@
 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
 import static android.content.Context.DEVICE_ID_DEFAULT;
 
-import static com.android.media.audio.flags.Flags.autoPublicVolumeApiHardening;
-import static com.android.media.audio.flags.Flags.FLAG_FOCUS_FREEZE_TEST_API;
+import static android.media.audio.Flags.autoPublicVolumeApiHardening;
+import static android.media.audio.Flags.FLAG_FOCUS_FREEZE_TEST_API;
 
 import android.Manifest;
 import android.annotation.CallbackExecutor;
@@ -686,6 +686,7 @@
             FLAG_ABSOLUTE_VOLUME,
     })
     @Retention(RetentionPolicy.SOURCE)
+    // TODO(308698465) remove due to potential conflict with the new flags class
     public @interface Flags {}
 
     /**
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 9ced2a4..e168498 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -49,7 +49,6 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.media.audio.flags.Flags;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/media/java/android/media/midi/MidiUmpDeviceService.java b/media/java/android/media/midi/MidiUmpDeviceService.java
index c54bfce..bbbef47 100644
--- a/media/java/android/media/midi/MidiUmpDeviceService.java
+++ b/media/java/android/media/midi/MidiUmpDeviceService.java
@@ -16,8 +16,6 @@
 
 package android.media.midi;
 
-import static com.android.media.midi.flags.Flags.FLAG_VIRTUAL_UMP;
-
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -57,11 +55,11 @@
  *             android:resource="@xml/device_info" />
  * &lt;/service></pre>
  */
-@FlaggedApi(FLAG_VIRTUAL_UMP)
+@FlaggedApi(Flags.FLAG_VIRTUAL_UMP)
 public abstract class MidiUmpDeviceService extends Service {
     private static final String TAG = "MidiUmpDeviceService";
 
-    @FlaggedApi(FLAG_VIRTUAL_UMP)
+    @FlaggedApi(Flags.FLAG_VIRTUAL_UMP)
     public static final String SERVICE_INTERFACE = "android.media.midi.MidiUmpDeviceService";
 
     private IMidiManager mMidiManager;
@@ -80,7 +78,7 @@
         }
     };
 
-    @FlaggedApi(FLAG_VIRTUAL_UMP)
+    @FlaggedApi(Flags.FLAG_VIRTUAL_UMP)
     @Override
     public void onCreate() {
         mMidiManager = IMidiManager.Stub.asInterface(
@@ -118,7 +116,7 @@
      * The number of input and output ports must be equal and non-zero.
      * @return list of MidiReceivers
      */
-    @FlaggedApi(FLAG_VIRTUAL_UMP)
+    @FlaggedApi(Flags.FLAG_VIRTUAL_UMP)
     public abstract @NonNull List<MidiReceiver> onGetInputPortReceivers();
 
     /**
@@ -127,7 +125,7 @@
      * The number of input and output ports must be equal and non-zero.
      * @return the list of MidiReceivers
      */
-    @FlaggedApi(FLAG_VIRTUAL_UMP)
+    @FlaggedApi(Flags.FLAG_VIRTUAL_UMP)
     public final @NonNull List<MidiReceiver> getOutputPortReceivers() {
         if (mServer == null) {
             return new ArrayList<MidiReceiver>();
@@ -140,7 +138,7 @@
      * Returns the {@link MidiDeviceInfo} instance for this service
      * @return the MidiDeviceInfo of the virtual MIDI device if it was successfully created
      */
-    @FlaggedApi(FLAG_VIRTUAL_UMP)
+    @FlaggedApi(Flags.FLAG_VIRTUAL_UMP)
     public final @Nullable MidiDeviceInfo getDeviceInfo() {
         return mDeviceInfo;
     }
@@ -149,7 +147,7 @@
      * Called to notify when the {@link MidiDeviceStatus} has changed
      * @param status the current status of the MIDI device
      */
-    @FlaggedApi(FLAG_VIRTUAL_UMP)
+    @FlaggedApi(Flags.FLAG_VIRTUAL_UMP)
     public void onDeviceStatusChanged(@NonNull MidiDeviceStatus status) {
     }
 
@@ -157,11 +155,11 @@
      * Called to notify when the virtual MIDI device running in this service has been closed by
      * all its clients
      */
-    @FlaggedApi(FLAG_VIRTUAL_UMP)
+    @FlaggedApi(Flags.FLAG_VIRTUAL_UMP)
     public void onClose() {
     }
 
-    @FlaggedApi(FLAG_VIRTUAL_UMP)
+    @FlaggedApi(Flags.FLAG_VIRTUAL_UMP)
     @Override
     public @Nullable IBinder onBind(@NonNull Intent intent) {
         if (SERVICE_INTERFACE.equals(intent.getAction()) && mServer != null) {
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index c274911..069ba6c 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -53,6 +53,14 @@
 }
 
 flag {
+    name: "keyguard_bottom_area_refactor"
+    namespace: "systemui"
+    description: "Bottom area of keyguard refactor move into KeyguardRootView. Includes "
+        "lock icon and others."
+    bug: "290652751"
+}
+
+flag {
     name: "visual_interruptions_refactor"
     namespace: "systemui"
     description: "Enables the refactored version of the code to decide when notifications "
@@ -66,3 +74,11 @@
     description: "Adds haptic feedback to the brightness slider."
     bug: "296467915"
 }
+
+flag {
+    name: "keyguard_shade_migration_nssl"
+    namespace: "systemui"
+    description: "Moves NSSL into a shared element between the notification_panel and "
+        "keyguard_root_view."
+    bug: "278054201"
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index f2b7b32..56970d7 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -19,6 +19,7 @@
 import android.app.AlertDialog
 import android.app.Dialog
 import android.content.DialogInterface
+import android.content.res.Configuration
 import androidx.compose.animation.Crossfade
 import androidx.compose.animation.core.animateFloatAsState
 import androidx.compose.animation.core.snap
@@ -50,6 +51,7 @@
 import androidx.compose.material3.Icon
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
+import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass
 import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.collectAsState
@@ -63,6 +65,7 @@
 import androidx.compose.ui.graphics.asImageBitmap
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalConfiguration
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.res.stringResource
@@ -136,7 +139,7 @@
     modifier: Modifier = Modifier,
 ) {
     val backgroundColor = MaterialTheme.colorScheme.surface
-    val windowSizeClass = LocalWindowSizeClass.current
+    val layout = calculateLayout()
 
     Box(modifier) {
         Canvas(Modifier.element(Bouncer.Elements.Background).fillMaxSize()) {
@@ -146,22 +149,30 @@
         val childModifier = Modifier.element(Bouncer.Elements.Content).fillMaxSize()
         val isFullScreenUserSwitcherEnabled = viewModel.isUserSwitcherVisible
 
-        when {
-            windowSizeClass.widthSizeClass == WindowWidthSizeClass.Expanded ->
+        when (layout) {
+            Layout.STANDARD ->
+                Bouncer(
+                    viewModel = viewModel,
+                    dialogFactory = dialogFactory,
+                    isUserInputAreaVisible = true,
+                    modifier = childModifier,
+                )
+            Layout.SIDE_BY_SIDE ->
                 SideBySide(
                     viewModel = viewModel,
                     dialogFactory = dialogFactory,
+                    isUserSwitcherVisible = isFullScreenUserSwitcherEnabled,
                     modifier = childModifier,
                 )
-            isFullScreenUserSwitcherEnabled &&
-                windowSizeClass.widthSizeClass == WindowWidthSizeClass.Medium ->
+            Layout.STACKED ->
                 Stacked(
                     viewModel = viewModel,
                     dialogFactory = dialogFactory,
+                    isUserSwitcherVisible = isFullScreenUserSwitcherEnabled,
                     modifier = childModifier,
                 )
-            else ->
-                Bouncer(
+            Layout.SPLIT ->
+                Split(
                     viewModel = viewModel,
                     dialogFactory = dialogFactory,
                     modifier = childModifier,
@@ -178,11 +189,10 @@
 private fun Bouncer(
     viewModel: BouncerViewModel,
     dialogFactory: BouncerSceneDialogFactory,
+    isUserInputAreaVisible: Boolean,
     modifier: Modifier = Modifier,
 ) {
     val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState()
-    val authMethodViewModel: AuthMethodBouncerViewModel? by
-        viewModel.authMethodViewModel.collectAsState()
     val dialogMessage: String? by viewModel.throttlingDialogMessage.collectAsState()
     var dialog: Dialog? by remember { mutableStateOf(null) }
 
@@ -204,25 +214,11 @@
         }
 
         Box(Modifier.weight(1f)) {
-            when (val nonNullViewModel = authMethodViewModel) {
-                is PinBouncerViewModel ->
-                    PinBouncer(
-                        viewModel = nonNullViewModel,
-                        modifier = Modifier.align(Alignment.Center),
-                    )
-                is PasswordBouncerViewModel ->
-                    PasswordBouncer(
-                        viewModel = nonNullViewModel,
-                        modifier = Modifier.align(Alignment.Center),
-                    )
-                is PatternBouncerViewModel ->
-                    PatternBouncer(
-                        viewModel = nonNullViewModel,
-                        modifier =
-                            Modifier.aspectRatio(1f, matchHeightConstraintsFirst = false)
-                                .align(Alignment.BottomCenter),
-                    )
-                else -> Unit
+            if (isUserInputAreaVisible) {
+                UserInputArea(
+                    viewModel = viewModel,
+                    modifier = Modifier.align(Alignment.Center),
+                )
             }
         }
 
@@ -265,6 +261,40 @@
     }
 }
 
+/**
+ * Renders the user input area, where the user interacts with the UI to enter their credentials.
+ *
+ * For example, this can be the pattern input area, the password text box, or pin pad.
+ */
+@Composable
+private fun UserInputArea(
+    viewModel: BouncerViewModel,
+    modifier: Modifier = Modifier,
+) {
+    val authMethodViewModel: AuthMethodBouncerViewModel? by
+        viewModel.authMethodViewModel.collectAsState()
+
+    when (val nonNullViewModel = authMethodViewModel) {
+        is PinBouncerViewModel ->
+            PinBouncer(
+                viewModel = nonNullViewModel,
+                modifier = modifier,
+            )
+        is PasswordBouncerViewModel ->
+            PasswordBouncer(
+                viewModel = nonNullViewModel,
+                modifier = modifier,
+            )
+        is PatternBouncerViewModel ->
+            PatternBouncer(
+                viewModel = nonNullViewModel,
+                modifier =
+                    Modifier.aspectRatio(1f, matchHeightConstraintsFirst = false).then(modifier)
+            )
+        else -> Unit
+    }
+}
+
 /** Renders the UI of the user switcher that's displayed on large screens next to the bouncer UI. */
 @Composable
 private fun UserSwitcher(
@@ -287,62 +317,57 @@
             )
         }
 
-        UserSwitcherDropdown(
-            items = dropdownItems,
-        )
-    }
-}
+        val (isDropdownExpanded, setDropdownExpanded) = remember { mutableStateOf(false) }
 
-@Composable
-private fun UserSwitcherDropdown(
-    items: List<BouncerViewModel.UserSwitcherDropdownItemViewModel>,
-) {
-    val (isDropdownExpanded, setDropdownExpanded) = remember { mutableStateOf(false) }
+        dropdownItems.firstOrNull()?.let { firstDropdownItem ->
+            Spacer(modifier = Modifier.height(40.dp))
 
-    items.firstOrNull()?.let { firstDropdownItem ->
-        Spacer(modifier = Modifier.height(40.dp))
+            Box {
+                PlatformButton(
+                    modifier =
+                        Modifier
+                            // Remove the built-in padding applied inside PlatformButton:
+                            .padding(vertical = 0.dp)
+                            .width(UserSwitcherDropdownWidth)
+                            .height(UserSwitcherDropdownHeight),
+                    colors =
+                        ButtonDefaults.buttonColors(
+                            containerColor = MaterialTheme.colorScheme.surfaceContainerHighest,
+                            contentColor = MaterialTheme.colorScheme.onSurface,
+                        ),
+                    onClick = { setDropdownExpanded(!isDropdownExpanded) },
+                ) {
+                    val context = LocalContext.current
+                    Text(
+                        text = checkNotNull(firstDropdownItem.text.loadText(context)),
+                        style = MaterialTheme.typography.headlineSmall,
+                        maxLines = 1,
+                        overflow = TextOverflow.Ellipsis,
+                    )
 
-        Box {
-            PlatformButton(
-                modifier =
-                    Modifier
-                        // Remove the built-in padding applied inside PlatformButton:
-                        .padding(vertical = 0.dp)
-                        .width(UserSwitcherDropdownWidth)
-                        .height(UserSwitcherDropdownHeight),
-                colors =
-                    ButtonDefaults.buttonColors(
-                        containerColor = MaterialTheme.colorScheme.surfaceContainerHighest,
-                        contentColor = MaterialTheme.colorScheme.onSurface,
-                    ),
-                onClick = { setDropdownExpanded(!isDropdownExpanded) },
-            ) {
-                val context = LocalContext.current
-                Text(
-                    text = checkNotNull(firstDropdownItem.text.loadText(context)),
-                    style = MaterialTheme.typography.headlineSmall,
-                    maxLines = 1,
-                    overflow = TextOverflow.Ellipsis,
-                )
+                    Spacer(modifier = Modifier.weight(1f))
 
-                Spacer(modifier = Modifier.weight(1f))
+                    Icon(
+                        imageVector = Icons.Default.KeyboardArrowDown,
+                        contentDescription = null,
+                        modifier = Modifier.size(32.dp),
+                    )
+                }
 
-                Icon(
-                    imageVector = Icons.Default.KeyboardArrowDown,
-                    contentDescription = null,
-                    modifier = Modifier.size(32.dp),
+                UserSwitcherDropdownMenu(
+                    isExpanded = isDropdownExpanded,
+                    items = dropdownItems,
+                    onDismissed = { setDropdownExpanded(false) },
                 )
             }
-
-            UserSwitcherDropdownMenu(
-                isExpanded = isDropdownExpanded,
-                items = items,
-                onDismissed = { setDropdownExpanded(false) },
-            )
         }
     }
 }
 
+/**
+ * Renders the dropdowm menu that displays the actual users and/or user actions that can be
+ * selected.
+ */
 @Composable
 private fun UserSwitcherDropdownMenu(
     isExpanded: Boolean,
@@ -396,19 +421,47 @@
 }
 
 /**
- * Arranges the bouncer contents and user switcher contents side-by-side, supporting a double tap
- * anywhere on the background to flip their positions.
+ * Renders the bouncer UI in split mode, with half on one side and half on the other side, swappable
+ * by double-tapping on the side.
  */
 @Composable
-private fun SideBySide(
+private fun Split(
     viewModel: BouncerViewModel,
     dialogFactory: BouncerSceneDialogFactory,
     modifier: Modifier = Modifier,
 ) {
+    SwappableLayout(
+        startContent = { startContentModifier ->
+            Bouncer(
+                viewModel = viewModel,
+                dialogFactory = dialogFactory,
+                isUserInputAreaVisible = false,
+                modifier = startContentModifier,
+            )
+        },
+        endContent = { endContentModifier ->
+            UserInputArea(
+                viewModel = viewModel,
+                modifier = endContentModifier,
+            )
+        },
+        modifier = modifier
+    )
+}
+
+/**
+ * Arranges the given two contents side-by-side, supporting a double tap anywhere on the background
+ * to flip their positions.
+ */
+@Composable
+private fun SwappableLayout(
+    startContent: @Composable (Modifier) -> Unit,
+    endContent: @Composable (Modifier) -> Unit,
+    modifier: Modifier = Modifier,
+) {
     val layoutDirection = LocalLayoutDirection.current
     val isLeftToRight = layoutDirection == LayoutDirection.Ltr
-    val (isUserSwitcherFirst, setUserSwitcherFirst) =
-        rememberSaveable(isLeftToRight) { mutableStateOf(isLeftToRight) }
+    val (isSwapped, setSwapped) = rememberSaveable(isLeftToRight) { mutableStateOf(!isLeftToRight) }
 
     Row(
         modifier =
@@ -416,9 +469,8 @@
                 detectTapGestures(
                     onDoubleTap = { offset ->
                         // Depending on where the user double tapped, switch the elements such that
-                        // the bouncer contents element is closer to the side that was double
-                        // tapped.
-                        setUserSwitcherFirst(offset.x > size.width / 2)
+                        // the endContent is closer to the side that was double tapped.
+                        setSwapped(offset.x < size.width / 2)
                     }
                 )
             },
@@ -426,39 +478,30 @@
         val animatedOffset by
             animateFloatAsState(
                 targetValue =
-                    if (isUserSwitcherFirst) {
-                        // When the user switcher is first, both elements have their natural
-                        // placement so they are not offset in any way.
+                    if (!isSwapped) {
+                        // When startContent is first, both elements have their natural placement so
+                        // they are not offset in any way.
                         0f
                     } else if (isLeftToRight) {
-                        // Since the user switcher is not first, the elements have to be swapped
-                        // horizontally. In the case of LTR locales, this means pushing the user
-                        // switcher to the right, hence the positive number.
+                        // Since startContent is not first, the elements have to be swapped
+                        // horizontally. In the case of LTR locales, this means pushing startContent
+                        // to the right, hence the positive number.
                         1f
                     } else {
-                        // Since the user switcher is not first, the elements have to be swapped
-                        // horizontally. In the case of RTL locale, this means pushing the user
-                        // switcher to the left, hence the negative number.
+                        // Since startContent is not first, the elements have to be swapped
+                        // horizontally. In the case of RTL locales, this means pushing startContent
+                        // to the left, hence the negative number.
                         -1f
                     },
                 label = "offset",
             )
 
-        val userSwitcherModifier =
+        startContent(
             Modifier.fillMaxHeight().weight(1f).graphicsLayer {
                 translationX = size.width * animatedOffset
                 alpha = animatedAlpha(animatedOffset)
             }
-        if (viewModel.isUserSwitcherVisible) {
-            UserSwitcher(
-                viewModel = viewModel,
-                modifier = userSwitcherModifier,
-            )
-        } else {
-            Box(
-                modifier = userSwitcherModifier,
-            )
-        }
+        )
 
         Box(
             modifier =
@@ -469,41 +512,124 @@
                     alpha = animatedAlpha(animatedOffset)
                 }
         ) {
-            Bouncer(
-                viewModel = viewModel,
-                dialogFactory = dialogFactory,
-                modifier = Modifier.widthIn(max = 400.dp).align(Alignment.BottomCenter),
-            )
+            endContent(Modifier.widthIn(max = 400.dp).align(Alignment.BottomCenter))
         }
     }
 }
 
-/** Arranges the bouncer contents and user switcher contents one on top of the other. */
+/**
+ * Arranges the bouncer contents and user switcher contents side-by-side, supporting a double tap
+ * anywhere on the background to flip their positions.
+ */
+@Composable
+private fun SideBySide(
+    viewModel: BouncerViewModel,
+    dialogFactory: BouncerSceneDialogFactory,
+    isUserSwitcherVisible: Boolean,
+    modifier: Modifier = Modifier,
+) {
+    SwappableLayout(
+        startContent = { startContentModifier ->
+            if (isUserSwitcherVisible) {
+                UserSwitcher(
+                    viewModel = viewModel,
+                    modifier = startContentModifier,
+                )
+            } else {
+                Box(
+                    modifier = startContentModifier,
+                )
+            }
+        },
+        endContent = { endContentModifier ->
+            Bouncer(
+                viewModel = viewModel,
+                dialogFactory = dialogFactory,
+                isUserInputAreaVisible = true,
+                modifier = endContentModifier,
+            )
+        },
+        modifier = modifier,
+    )
+}
+
+/** Arranges the bouncer contents and user switcher contents one on top of the other, vertically. */
 @Composable
 private fun Stacked(
     viewModel: BouncerViewModel,
     dialogFactory: BouncerSceneDialogFactory,
+    isUserSwitcherVisible: Boolean,
     modifier: Modifier = Modifier,
 ) {
     Column(
         modifier = modifier,
     ) {
-        UserSwitcher(
-            viewModel = viewModel,
-            modifier = Modifier.fillMaxWidth().weight(1f),
-        )
+        if (isUserSwitcherVisible) {
+            UserSwitcher(
+                viewModel = viewModel,
+                modifier = Modifier.fillMaxWidth().weight(1f),
+            )
+        }
+
         Bouncer(
             viewModel = viewModel,
             dialogFactory = dialogFactory,
+            isUserInputAreaVisible = true,
             modifier = Modifier.fillMaxWidth().weight(1f),
         )
     }
 }
 
+@Composable
+private fun calculateLayout(): Layout {
+    val windowSizeClass = LocalWindowSizeClass.current
+    val width = windowSizeClass.widthSizeClass
+    val height = windowSizeClass.heightSizeClass
+    val isLarge = width > WindowWidthSizeClass.Compact && height > WindowHeightSizeClass.Compact
+    val isTall =
+        when (height) {
+            WindowHeightSizeClass.Expanded -> width < WindowWidthSizeClass.Expanded
+            WindowHeightSizeClass.Medium -> width < WindowWidthSizeClass.Medium
+            else -> false
+        }
+    val isSquare =
+        when (width) {
+            WindowWidthSizeClass.Compact -> height == WindowHeightSizeClass.Compact
+            WindowWidthSizeClass.Medium -> height == WindowHeightSizeClass.Medium
+            WindowWidthSizeClass.Expanded -> height == WindowHeightSizeClass.Expanded
+            else -> false
+        }
+    val isLandscape = LocalConfiguration.current.orientation == Configuration.ORIENTATION_LANDSCAPE
+
+    return when {
+        // Small and tall devices (i.e. phone/folded in portrait) or square device not in landscape
+        // mode (unfolded with hinge along horizontal plane).
+        (!isLarge && isTall) || (isSquare && !isLandscape) -> Layout.STANDARD
+        // Small and wide devices (i.e. phone/folded in landscape).
+        !isLarge -> Layout.SPLIT
+        // Large and tall devices (i.e. tablet in portrait).
+        isTall -> Layout.STACKED
+        // Large and wide/square devices (i.e. tablet in landscape, unfolded).
+        else -> Layout.SIDE_BY_SIDE
+    }
+}
+
 interface BouncerSceneDialogFactory {
     operator fun invoke(): AlertDialog
 }
 
+/** Enumerates all known adaptive layout configurations. */
+private enum class Layout {
+    /** The default UI with the bouncer laid out normally. */
+    STANDARD,
+    /** The bouncer is displayed vertically stacked with the user switcher. */
+    STACKED,
+    /** The bouncer is displayed side-by-side with the user switcher or an empty space. */
+    SIDE_BY_SIDE,
+    /** The bouncer is split in two with both sides shown side-by-side. */
+    SPLIT,
+}
+
 /**
  * Calculates an alpha for the user switcher and bouncer such that it's at `1` when the offset of
  * the two reaches a stopping point but `0` in the middle of the transition.
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index 2e93a09..0da562b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -161,7 +161,8 @@
                 fromScene = fromScene.toModel().key,
                 toScene = toScene.toModel().key,
                 progress = progress,
-                isUserInputDriven = isUserInputDriven,
+                isInitiatedByUserInput = isInitiatedByUserInput,
+                isUserInputOngoing = isUserInputOngoing,
             )
     }
 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
index 88944f10..199832b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
@@ -108,7 +108,7 @@
 ) {
     val fromScene = layoutImpl.state.transitionState.currentScene
     val isUserInput =
-        (layoutImpl.state.transitionState as? TransitionState.Transition)?.isUserInputDriven
+        (layoutImpl.state.transitionState as? TransitionState.Transition)?.isInitiatedByUserInput
             ?: false
 
     val animationSpec = layoutImpl.transitions.transitionSpec(fromScene, target).spec
@@ -119,9 +119,23 @@
     val targetProgress = if (reversed) 0f else 1f
     val transition =
         if (reversed) {
-            OneOffTransition(target, fromScene, currentScene = target, isUserInput, animatable)
+            OneOffTransition(
+                fromScene = target,
+                toScene = fromScene,
+                currentScene = target,
+                isInitiatedByUserInput = isUserInput,
+                isUserInputOngoing = false,
+                animatable = animatable,
+            )
         } else {
-            OneOffTransition(fromScene, target, currentScene = target, isUserInput, animatable)
+            OneOffTransition(
+                fromScene = fromScene,
+                toScene = target,
+                currentScene = target,
+                isInitiatedByUserInput = isUserInput,
+                isUserInputOngoing = false,
+                animatable = animatable,
+            )
         }
 
     // Change the current layout state to use this new transition.
@@ -142,7 +156,8 @@
     override val fromScene: SceneKey,
     override val toScene: SceneKey,
     override val currentScene: SceneKey,
-    override val isUserInputDriven: Boolean,
+    override val isInitiatedByUserInput: Boolean,
+    override val isUserInputOngoing: Boolean,
     private val animatable: Animatable<Float, AnimationVector1D>,
 ) : TransitionState.Transition {
     override val progress: Float
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
index ccdec6e..1b79dbd 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
@@ -52,7 +52,14 @@
          * scene, this value will remain true after the pointer is no longer touching the screen and
          * will be true in any transition created to animate back to the original position.
          */
-        val isUserInputDriven: Boolean,
+        val isInitiatedByUserInput: Boolean,
+
+        /**
+         * Whether user input is currently driving the transition. For example, if a user is
+         * dragging a pointer, this emits true. Once they lift their finger, this emits false while
+         * the transition completes/settles.
+         */
+        val isUserInputOngoing: Flow<Boolean>,
     ) : ObservableTransitionState()
 }
 
@@ -73,7 +80,8 @@
                             fromScene = state.fromScene,
                             toScene = state.toScene,
                             progress = snapshotFlow { state.progress },
-                            isUserInputDriven = state.isUserInputDriven,
+                            isInitiatedByUserInput = state.isInitiatedByUserInput,
+                            isUserInputOngoing = snapshotFlow { state.isUserInputOngoing },
                         )
                     }
                 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index 7a21211..b9f83c5 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -70,6 +70,9 @@
         val progress: Float
 
         /** Whether the transition was triggered by user input rather than being programmatic. */
-        val isUserInputDriven: Boolean
+        val isInitiatedByUserInput: Boolean
+
+        /** Whether user input is currently driving the transition. */
+        val isUserInputOngoing: Boolean
     }
 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
index ee1f133..877ac09 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -307,12 +307,6 @@
     )
 
     private fun Scene.findTargetSceneAndDistance(directionOffset: Float): TargetScene {
-        val maxDistance =
-            when (orientation) {
-                Orientation.Horizontal -> layoutImpl.size.width
-                Orientation.Vertical -> layoutImpl.size.height
-            }.toFloat()
-
         val upOrLeft = swipeTransition.upOrLeft(this)
         val downOrRight = swipeTransition.downOrRight(this)
 
@@ -321,13 +315,13 @@
             directionOffset < 0f && upOrLeft != null -> {
                 TargetScene(
                     sceneKey = upOrLeft,
-                    distance = -maxDistance,
+                    distance = -swipeTransition.absoluteDistance,
                 )
             }
             directionOffset > 0f && downOrRight != null -> {
                 TargetScene(
                     sceneKey = downOrRight,
-                    distance = maxDistance,
+                    distance = swipeTransition.absoluteDistance,
                 )
             }
             else -> {
@@ -514,7 +508,7 @@
                 return offset / distance
             }
 
-        override val isUserInputDriven = true
+        override val isInitiatedByUserInput = true
 
         /** The current offset caused by the drag gesture. */
         var dragOffset by mutableFloatStateOf(0f)
@@ -525,6 +519,10 @@
          */
         var isAnimatingOffset by mutableStateOf(false)
 
+        // If we are not animating offset, it means the offset is being driven by the user's finger.
+        override val isUserInputOngoing: Boolean
+            get() = !isAnimatingOffset
+
         /** The animatable used to animate the offset once the user lifted its finger. */
         val offsetAnimatable = Animatable(0f, OffsetVisibilityThreshold)
 
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index 4a6066f..58d853e 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -136,7 +136,7 @@
         assertThat(transition.toScene).isEqualTo(TestScenes.SceneB)
         assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA)
         assertThat(transition.progress).isEqualTo(55.dp / LayoutWidth)
-        assertThat(transition.isUserInputDriven).isTrue()
+        assertThat(transition.isInitiatedByUserInput).isTrue()
 
         // Release the finger. We should now be animating back to A (currentScene = SceneA) given
         // that 55dp < positional threshold.
@@ -148,7 +148,7 @@
         assertThat(transition.toScene).isEqualTo(TestScenes.SceneB)
         assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA)
         assertThat(transition.progress).isEqualTo(55.dp / LayoutWidth)
-        assertThat(transition.isUserInputDriven).isTrue()
+        assertThat(transition.isInitiatedByUserInput).isTrue()
 
         // Wait for the animation to finish. We should now be in scene A.
         rule.waitForIdle()
@@ -170,7 +170,7 @@
         assertThat(transition.toScene).isEqualTo(TestScenes.SceneC)
         assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA)
         assertThat(transition.progress).isEqualTo(56.dp / LayoutHeight)
-        assertThat(transition.isUserInputDriven).isTrue()
+        assertThat(transition.isInitiatedByUserInput).isTrue()
 
         // Release the finger. We should now be animating to C (currentScene = SceneC) given
         // that 56dp >= positional threshold.
@@ -182,7 +182,7 @@
         assertThat(transition.toScene).isEqualTo(TestScenes.SceneC)
         assertThat(transition.currentScene).isEqualTo(TestScenes.SceneC)
         assertThat(transition.progress).isEqualTo(56.dp / LayoutHeight)
-        assertThat(transition.isUserInputDriven).isTrue()
+        assertThat(transition.isInitiatedByUserInput).isTrue()
 
         // Wait for the animation to finish. We should now be in scene C.
         rule.waitForIdle()
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 4c520444..12bff4a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1045,8 +1045,8 @@
     <!-- Indication on the keyguard that is shown when the device is dock charging. [CHAR LIMIT=80]-->
     <string name="keyguard_indication_charging_time_dock"><xliff:g id="percentage" example="20%">%2$s</xliff:g> • Charging • Full in <xliff:g id="charging_time_left" example="4 hr, 2 min">%1$s</xliff:g></string>
 
-    <!-- Indicator shown to start the communal tutorial. [CHAR LIMIT=100] -->
-    <string name="communal_tutorial_indicator_text">Click on the arrow button to start the communal tutorial</string>
+    <!-- Indicator on keyguard to start the communal tutorial. [CHAR LIMIT=100] -->
+    <string name="communal_tutorial_indicator_text">Swipe left to start the communal tutorial</string>
 
     <!-- Related to user switcher --><skip/>
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index e69eced..1ac4163 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -489,6 +489,10 @@
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
 
+        if (mContainerState == STATE_ANIMATING_OUT) {
+            return;
+        }
+
         mWakefulnessLifecycle.addObserver(this);
         mPanelInteractionDetector.enable(
                 () -> animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED));
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
index 2bb19cd..f513799 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
@@ -26,8 +26,6 @@
 import com.android.systemui.biometrics.shared.model.FingerprintSensorType
 import com.android.systemui.biometrics.shared.model.isDefaultOrientation
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
 import com.android.systemui.log.SideFpsLogger
 import com.android.systemui.res.R
 import java.util.Optional
@@ -48,7 +46,6 @@
     fingerprintPropertyRepository: FingerprintPropertyRepository,
     windowManager: WindowManager,
     displayStateInteractor: DisplayStateInteractor,
-    featureFlags: FeatureFlagsClassic,
     fingerprintInteractiveToAuthProvider: Optional<FingerprintInteractiveToAuthProvider>,
     private val logger: SideFpsLogger,
 ) {
@@ -65,14 +62,11 @@
     val isAvailable: Flow<Boolean> =
         fingerprintPropertyRepository.sensorType.map { it == FingerprintSensorType.POWER_BUTTON }
 
-    val authenticationDuration: Flow<Long> =
-        flowOf(context.resources?.getInteger(R.integer.config_restToUnlockDuration)?.toLong() ?: 0L)
+    val authenticationDuration: Long =
+        context.resources?.getInteger(R.integer.config_restToUnlockDuration)?.toLong() ?: 0L
 
     val isProlongedTouchRequiredForAuthentication: Flow<Boolean> =
-        if (
-            fingerprintInteractiveToAuthProvider.isEmpty ||
-                !featureFlags.isEnabled(Flags.REST_TO_UNLOCK)
-        ) {
+        if (fingerprintInteractiveToAuthProvider.isEmpty) {
             flowOf(false)
         } else {
             combine(
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
index 276df4e..7f43eb5 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
@@ -19,12 +19,23 @@
 import android.provider.Settings
 import com.android.systemui.communal.data.repository.CommunalTutorialRepository
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.scene.shared.model.SceneKey
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
 
 /** Encapsulates business-logic related to communal tutorial state. */
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -32,8 +43,12 @@
 class CommunalTutorialInteractor
 @Inject
 constructor(
-    communalTutorialRepository: CommunalTutorialRepository,
+    @Application private val scope: CoroutineScope,
+    private val communalTutorialRepository: CommunalTutorialRepository,
     keyguardInteractor: KeyguardInteractor,
+    private val communalInteractor: CommunalInteractor,
+    private val sceneContainerFlags: SceneContainerFlags,
+    private val sceneInteractor: SceneInteractor,
 ) {
     /** An observable for whether the tutorial is available. */
     val isTutorialAvailable: Flow<Boolean> =
@@ -45,4 +60,63 @@
                     tutorialSettingState != Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
             }
             .distinctUntilChanged()
+
+    /**
+     * A flow of the new tutorial state after transitioning. The new state will be calculated based
+     * on the current tutorial state and transition state as following:
+     * HUB_MODE_TUTORIAL_NOT_STARTED + communal scene -> HUB_MODE_TUTORIAL_STARTED
+     * HUB_MODE_TUTORIAL_STARTED + non-communal scene -> HUB_MODE_TUTORIAL_COMPLETED
+     * HUB_MODE_TUTORIAL_COMPLETED + any scene -> won't emit
+     */
+    private val tutorialStateToUpdate: Flow<Int> =
+        communalTutorialRepository.tutorialSettingState
+            .flatMapLatest { tutorialSettingState ->
+                if (tutorialSettingState == Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) {
+                    return@flatMapLatest flowOf(null)
+                }
+                if (sceneContainerFlags.isEnabled()) {
+                    sceneInteractor.desiredScene.map { sceneModel ->
+                        nextStateAfterTransition(
+                            tutorialSettingState,
+                            sceneModel.key == SceneKey.Communal
+                        )
+                    }
+                } else {
+                    communalInteractor.isCommunalShowing.map {
+                        nextStateAfterTransition(tutorialSettingState, it)
+                    }
+                }
+            }
+            .filterNotNull()
+            .distinctUntilChanged()
+
+    private fun nextStateAfterTransition(tutorialState: Int, isCommunalShowing: Boolean): Int? {
+        if (tutorialState == Settings.Secure.HUB_MODE_TUTORIAL_NOT_STARTED && isCommunalShowing) {
+            return Settings.Secure.HUB_MODE_TUTORIAL_STARTED
+        }
+        if (tutorialState == Settings.Secure.HUB_MODE_TUTORIAL_STARTED && !isCommunalShowing) {
+            return Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
+        }
+        return null
+    }
+
+    private var job: Job? = null
+    private fun listenForTransitionToUpdateTutorialState() {
+        if (!communalInteractor.isCommunalEnabled) {
+            return
+        }
+        job =
+            scope.launch {
+                tutorialStateToUpdate.collect {
+                    communalTutorialRepository.setTutorialState(it)
+                    if (it == Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) {
+                        job?.cancel()
+                    }
+                }
+            }
+    }
+
+    init {
+        listenForTransitionToUpdateTutorialState()
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalTutorialIndicatorViewBinder.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalTutorialIndicatorViewBinder.kt
index dab6819..4dfc371 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalTutorialIndicatorViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalTutorialIndicatorViewBinder.kt
@@ -44,6 +44,8 @@
                             )
                         }
                     }
+
+                    launch { viewModel.alpha.collect { view.alpha = it } }
                 }
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTutorialIndicatorViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTutorialIndicatorViewModel.kt
index eaf9550..274e61a 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTutorialIndicatorViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTutorialIndicatorViewModel.kt
@@ -17,15 +17,21 @@
 package com.android.systemui.communal.ui.viewmodel
 
 import com.android.systemui.communal.domain.interactor.CommunalTutorialInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
 
 /** View model for communal tutorial indicator on keyguard */
 class CommunalTutorialIndicatorViewModel
 @Inject
 constructor(
     communalTutorialInteractor: CommunalTutorialInteractor,
+    bottomAreaInteractor: KeyguardBottomAreaInteractor,
 ) {
     /** An observable for whether the tutorial indicator view should be visible. */
     val showIndicator: Flow<Boolean> = communalTutorialInteractor.isTutorialAvailable
+
+    /** An observable for the alpha level for the tutorial indicator. */
+    val alpha: Flow<Float> = bottomAreaInteractor.alpha.distinctUntilChanged()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index 9e54b5c..26c5ea6 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -23,9 +23,9 @@
 import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
 import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_REMOVED
 import android.os.Handler
-import android.os.Trace
 import android.util.Log
 import android.view.Display
+import com.android.app.tracing.FlowTracing.traceEach
 import com.android.app.tracing.traceSection
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
@@ -43,10 +43,9 @@
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterIsInstance
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.shareIn
 import kotlinx.coroutines.flow.stateIn
@@ -97,7 +96,6 @@
     @Application applicationScope: CoroutineScope,
     @Background backgroundCoroutineDispatcher: CoroutineDispatcher
 ) : DisplayRepository {
-    // Displays are enabled only after receiving them in [onDisplayAdded]
     private val allDisplayEvents: Flow<DisplayEvent> =
         conflatedCallbackFlow {
                 val callback =
@@ -124,16 +122,22 @@
                 awaitClose { displayManager.unregisterDisplayListener(callback) }
             }
             .onStart { emit(DisplayEvent.Changed(Display.DEFAULT_DISPLAY)) }
+            .debugLog("allDisplayEvents")
             .flowOn(backgroundCoroutineDispatcher)
 
     override val displayChangeEvent: Flow<Int> =
-        allDisplayEvents.filter { it is DisplayEvent.Changed }.map { it.displayId }
+        allDisplayEvents.filterIsInstance<DisplayEvent.Changed>().map { event -> event.displayId }
 
     override val displayAdditionEvent: Flow<Display?> =
-        allDisplayEvents
-            .filter { it is DisplayEvent.Added }
-            .map { displayManager.getDisplay(it.displayId) }
+        allDisplayEvents.filterIsInstance<DisplayEvent.Added>().map {
+            displayManager.getDisplay(it.displayId)
+        }
 
+    /**
+     * Represents displays that went though the [DisplayListener.onDisplayAdded] callback.
+     *
+     * Those are commonly the ones provided by [DisplayManager.getDisplays] by default.
+     */
     private val enabledDisplays =
         allDisplayEvents
             .map { getDisplays() }
@@ -143,9 +147,7 @@
     override val displays: Flow<Set<Display>> = enabledDisplays
 
     private fun getDisplays(): Set<Display> =
-        traceSection("DisplayRepository#getDisplays()") {
-            displayManager.displays?.toSet() ?: emptySet()
-        }
+        traceSection("$TAG#getDisplays()") { displayManager.displays?.toSet() ?: emptySet() }
 
     /** Propagate to the listeners only enabled displays */
     private val enabledDisplayIds: Flow<Set<Int>> =
@@ -153,7 +155,8 @@
             .map { enabledDisplaysSet -> enabledDisplaysSet.map { it.displayId }.toSet() }
             .debugLog("enabledDisplayIds")
 
-    private val ignoredDisplayIds = MutableStateFlow<Set<Int>>(emptySet())
+    private val _ignoredDisplayIds = MutableStateFlow<Set<Int>>(emptySet())
+    private val ignoredDisplayIds: Flow<Set<Int>> = _ignoredDisplayIds.debugLog("ignoredDisplayIds")
 
     private fun getInitialConnectedDisplays(): Set<Int> =
         displayManager
@@ -177,7 +180,7 @@
                                 Log.d(TAG, "display with id=$id connected.")
                             }
                             connectedIds += id
-                            ignoredDisplayIds.value -= id
+                            _ignoredDisplayIds.value -= id
                             trySend(connectedIds.toSet())
                         }
 
@@ -186,7 +189,7 @@
                             if (DEBUG) {
                                 Log.d(TAG, "display with id=$id disconnected.")
                             }
-                            ignoredDisplayIds.value -= id
+                            _ignoredDisplayIds.value -= id
                             trySend(connectedIds.toSet())
                         }
                     }
@@ -214,17 +217,23 @@
     private val connectedExternalDisplayIds: Flow<Set<Int>> =
         connectedDisplayIds
             .map { connectedDisplayIds ->
-                connectedDisplayIds
-                    .filter { id -> displayManager.getDisplay(id)?.type == Display.TYPE_EXTERNAL }
-                    .toSet()
+                traceSection("$TAG#filteringExternalDisplays") {
+                    connectedDisplayIds
+                        .filter { id -> getDisplayType(id) == Display.TYPE_EXTERNAL }
+                        .toSet()
+                }
             }
             .flowOn(backgroundCoroutineDispatcher)
             .debugLog("connectedExternalDisplayIds")
 
+    private fun getDisplayType(displayId: Int): Int? =
+        traceSection("$TAG#getDisplayType") { displayManager.getDisplay(displayId)?.type }
+
     /**
-     * Pending displays are the ones connected, but not enabled and not ignored. A connected display
-     * is ignored after the user makes the decision to use it or not. For now, the initial decision
-     * from the user is final and not reversible.
+     * Pending displays are the ones connected, but not enabled and not ignored.
+     *
+     * A connected display is ignored after the user makes the decision to use it or not. For now,
+     * the initial decision from the user is final and not reversible.
      */
     private val pendingDisplayIds: Flow<Set<Int>> =
         combine(enabledDisplayIds, connectedExternalDisplayIds, ignoredDisplayIds) {
@@ -241,12 +250,16 @@
                 }
                 connectedExternalDisplayIds - enabledDisplaysIds - ignoredDisplayIds
             }
-            .debugLog("pendingDisplayIds")
+            .debugLog("allPendingDisplayIds")
+
+    /** Which display id should be enabled among the pending ones. */
+    private val pendingDisplayId: Flow<Int?> =
+        pendingDisplayIds.map { it.maxOrNull() }.distinctUntilChanged().debugLog("pendingDisplayId")
 
     override val pendingDisplay: Flow<DisplayRepository.PendingDisplay?> =
-        pendingDisplayIds
-            .map { pendingDisplayIds ->
-                val id = pendingDisplayIds.maxOrNull() ?: return@map null
+        pendingDisplayId
+            .map { displayId ->
+                val id = displayId ?: return@map null
                 object : DisplayRepository.PendingDisplay {
                     override val id = id
 
@@ -263,7 +276,7 @@
 
                     override suspend fun ignore() {
                         traceSection("DisplayRepository#ignore($id)") {
-                            ignoredDisplayIds.value += id
+                            _ignoredDisplayIds.value += id
                         }
                     }
 
@@ -282,11 +295,7 @@
 
     private fun <T> Flow<T>.debugLog(flowName: String): Flow<T> {
         return if (DEBUG) {
-            this.onEach {
-                Log.d(TAG, "$flowName: $it")
-                Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, "$TAG#$flowName", 0)
-                Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, "$TAG#$flowName", "$it", 0)
-            }
+            traceEach(flowName, logcat = true)
         } else {
             this
         }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index fa98661..49b8ee6 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -258,7 +258,7 @@
     // TODO(b/290652751): Tracking bug.
     @JvmField
     val MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA =
-        unreleasedFlag("migrate_split_keyguard_bottom_area", teamfood = true)
+        unreleasedFlag("migrate_split_keyguard_bottom_area")
 
     // TODO(b/297037052): Tracking bug.
     @JvmField
@@ -273,7 +273,7 @@
 
     /** Migrate the lock icon view to the new keyguard root view. */
     // TODO(b/286552209): Tracking bug.
-    @JvmField val MIGRATE_LOCK_ICON = unreleasedFlag("migrate_lock_icon", teamfood = true)
+    @JvmField val MIGRATE_LOCK_ICON = unreleasedFlag("migrate_lock_icon")
 
     // TODO(b/288276738): Tracking bug.
     @JvmField val WIDGET_ON_KEYGUARD = unreleasedFlag("widget_on_keyguard")
@@ -296,11 +296,6 @@
     @JvmField val MIGRATE_CLOCKS_TO_BLUEPRINT =
             unreleasedFlag("migrate_clocks_to_blueprint")
 
-    /** Migrate KeyguardRootView to use composables. */
-    // TODO(b/301969856): Tracking Bug.
-    @JvmField val KEYGUARD_ROOT_VIEW_USE_COMPOSE =
-        unreleasedFlag("keyguard_root_view_use_compose")
-
     /** Enables preview loading animation in the wallpaper picker. */
     // TODO(b/274443705): Tracking Bug
     @JvmField
@@ -532,10 +527,9 @@
     @Keep
     @JvmField
     val WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES =
-        unreleasedFlag(
-            name = "screen_record_enterprise_policies",
+        releasedFlag(
+            name = "enable_screen_record_enterprise_policies",
             namespace = DeviceConfig.NAMESPACE_WINDOW_MANAGER,
-            teamfood = false
         )
 
     // TODO(b/293252410) : Tracking Bug
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt
index ba4876f..0bee48a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt
@@ -22,6 +22,8 @@
 import com.android.systemui.biometrics.SideFpsController
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.ui.view.SideFpsProgressBar
 import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel
 import com.android.systemui.log.SideFpsLogger
@@ -31,6 +33,7 @@
 import java.io.PrintWriter
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
@@ -47,15 +50,23 @@
     private val sfpsController: dagger.Lazy<SideFpsController>,
     private val logger: SideFpsLogger,
     private val commandRegistry: CommandRegistry,
+    private val featureFlagsClassic: FeatureFlagsClassic,
 ) : CoreStartable {
 
     override fun start() {
+        if (!featureFlagsClassic.isEnabled(Flags.REST_TO_UNLOCK)) {
+            return
+        }
+        // When the rest to unlock feature is disabled by the user, stop any coroutines that are
+        // not required.
+        var layoutJob: Job? = null
+        var progressJob: Job? = null
         commandRegistry.registerCommand(spfsProgressBarCommand) { SfpsProgressBarCommand() }
         applicationScope.launch {
             viewModel.isProlongedTouchRequiredForAuthentication.collectLatest { enabled ->
                 logger.isProlongedTouchRequiredForAuthenticationChanged(enabled)
                 if (enabled) {
-                    launch {
+                    layoutJob = launch {
                         combine(
                                 viewModel.isVisible,
                                 viewModel.progressBarLocation,
@@ -76,9 +87,13 @@
                                 )
                             }
                     }
-                    launch { viewModel.progress.collectLatest { view.setProgress(it) } }
+                    progressJob = launch {
+                        viewModel.progress.collectLatest { view.setProgress(it) }
+                    }
                 } else {
                     view.hide()
+                    layoutJob?.cancel()
+                    progressJob?.cancel()
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
index f8996b7..a0f5baf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
@@ -26,6 +26,8 @@
 import com.android.systemui.biometrics.shared.model.isDefaultOrientation
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
@@ -34,13 +36,17 @@
 import com.android.systemui.res.R
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onCompletion
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.launch
 
 @SysUISingleton
@@ -52,10 +58,12 @@
     private val sfpsSensorInteractor: SideFpsSensorInteractor,
     displayStateInteractor: DisplayStateInteractor,
     @Application private val applicationScope: CoroutineScope,
+    private val featureFlagsClassic: FeatureFlagsClassic,
 ) {
     private val _progress = MutableStateFlow(0.0f)
     private val _visible = MutableStateFlow(false)
     private var _animator: ValueAnimator? = null
+    private var animatorJob: Job? = null
 
     private fun onFingerprintCaptureCompleted() {
         _visible.value = false
@@ -147,26 +155,32 @@
         sfpsSensorInteractor.isProlongedTouchRequiredForAuthentication
 
     init {
-        applicationScope.launch {
-            combine(
-                    sfpsSensorInteractor.isProlongedTouchRequiredForAuthentication,
-                    sfpsSensorInteractor.authenticationDuration,
-                    ::Pair
-                )
-                .collectLatest { (enabled, authDuration) ->
-                    if (!enabled) return@collectLatest
+        if (featureFlagsClassic.isEnabled(Flags.REST_TO_UNLOCK)) {
+            launchAnimator()
+        }
+    }
 
-                    launch {
-                        fpAuthRepository.authenticationStatus.collectLatest { authStatus ->
+    private fun launchAnimator() {
+        applicationScope.launch {
+            sfpsSensorInteractor.isProlongedTouchRequiredForAuthentication.collectLatest { enabled
+                ->
+                if (!enabled) {
+                    animatorJob?.cancel()
+                    return@collectLatest
+                }
+                animatorJob =
+                    fpAuthRepository.authenticationStatus
+                        .onEach { authStatus ->
                             when (authStatus) {
                                 is AcquiredFingerprintAuthenticationStatus -> {
                                     if (authStatus.fingerprintCaptureStarted) {
-
                                         _visible.value = true
                                         _animator?.cancel()
                                         _animator =
                                             ValueAnimator.ofFloat(0.0f, 1.0f)
-                                                .setDuration(authDuration)
+                                                .setDuration(
+                                                    sfpsSensorInteractor.authenticationDuration
+                                                )
                                                 .apply {
                                                     addUpdateListener {
                                                         _progress.value = it.animatedValue as Float
@@ -196,8 +210,9 @@
                                 else -> Unit
                             }
                         }
-                    }
-                }
+                        .onCompletion { _animator?.cancel() }
+                        .launchIn(applicationScope)
+            }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt
new file mode 100644
index 0000000..6d7d88f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.systemui.qs.tiles.impl.custom.data.repository
+
+import android.os.UserHandle
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import com.android.systemui.qs.external.TileServiceManager
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileBoundScope
+import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileUser
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.shareIn
+
+interface CustomTilePackageUpdatesRepository {
+
+    val packageChanges: Flow<Unit>
+}
+
+@CustomTileBoundScope
+class CustomTilePackageUpdatesRepositoryImpl
+@Inject
+constructor(
+    tileSpec: TileSpec.CustomTileSpec,
+    @CustomTileUser user: UserHandle,
+    serviceManager: TileServiceManager,
+    defaultsRepository: CustomTileDefaultsRepository,
+    @CustomTileBoundScope boundScope: CoroutineScope,
+) : CustomTilePackageUpdatesRepository {
+
+    override val packageChanges: Flow<Unit> =
+        ConflatedCallbackFlow.conflatedCallbackFlow {
+                serviceManager.setTileChangeListener { changedComponentName ->
+                    if (changedComponentName == tileSpec.componentName) {
+                        trySend(Unit)
+                    }
+                }
+
+                awaitClose { serviceManager.setTileChangeListener(null) }
+            }
+            .onEach { defaultsRepository.requestNewDefaults(user, tileSpec.componentName, true) }
+            .shareIn(boundScope, SharingStarted.WhileSubscribed())
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundComponent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundComponent.kt
index e33b3e9..d382d20 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundComponent.kt
@@ -23,7 +23,7 @@
 
 /** @see CustomTileBoundScope */
 @CustomTileBoundScope
-@Subcomponent
+@Subcomponent(modules = [CustomTileBoundModule::class])
 interface CustomTileBoundComponent {
 
     @Subcomponent.Builder
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundModule.kt
new file mode 100644
index 0000000..889424a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundModule.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.systemui.qs.tiles.impl.custom.di.bound
+
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepository
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepositoryImpl
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface CustomTileBoundModule {
+
+    @Binds
+    fun bindCustomTilePackageUpdatesRepository(
+        impl: CustomTilePackageUpdatesRepositoryImpl
+    ): CustomTilePackageUpdatesRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index 7353379..b144003 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -26,9 +26,12 @@
 import com.android.systemui.scene.shared.model.SceneModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
@@ -105,6 +108,26 @@
                 initialValue = null,
             )
 
+    /**
+     * Whether user input is ongoing for the current transition. For example, if the user is swiping
+     * their finger to transition between scenes, this value will be true while their finger is on
+     * the screen, then false for the rest of the transition.
+     */
+    @OptIn(ExperimentalCoroutinesApi::class)
+    val isTransitionUserInputOngoing: StateFlow<Boolean> =
+        transitionState
+            .flatMapLatest {
+                when (it) {
+                    is ObservableTransitionState.Transition -> it.isUserInputOngoing
+                    is ObservableTransitionState.Idle -> flowOf(false)
+                }
+            }
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = false
+            )
+
     /** Whether the scene container is visible. */
     val isVisible: StateFlow<Boolean> = repository.isVisible
 
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
index 3927873..f704894 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
@@ -42,6 +42,13 @@
          * scene, this value will remain true after the pointer is no longer touching the screen and
          * will be true in any transition created to animate back to the original position.
          */
-        val isUserInputDriven: Boolean,
+        val isInitiatedByUserInput: Boolean,
+
+        /**
+         * Whether user input is currently driving the transition. For example, if a user is
+         * dragging a pointer, this emits true. Once they lift their finger, this emits false while
+         * the transition completes/settles.
+         */
+        val isUserInputOngoing: Flow<Boolean>,
     ) : ObservableTransitionState()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index a2627ed..a2ca49d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -113,6 +113,7 @@
     private final SysUIKeyEventHandler mSysUIKeyEventHandler;
     private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
     private final AlternateBouncerInteractor mAlternateBouncerInteractor;
+    private final QuickSettingsController mQuickSettingsController;
     private GestureDetector mPulsingWakeupGestureHandler;
     private GestureDetector mDreamingWakeupGestureHandler;
     private View mBrightnessMirror;
@@ -188,6 +189,7 @@
             BouncerMessageInteractor bouncerMessageInteractor,
             BouncerLogger bouncerLogger,
             SysUIKeyEventHandler sysUIKeyEventHandler,
+            QuickSettingsController quickSettingsController,
             PrimaryBouncerInteractor primaryBouncerInteractor,
             AlternateBouncerInteractor alternateBouncerInteractor,
             SelectedUserInteractor selectedUserInteractor) {
@@ -220,6 +222,7 @@
         mSysUIKeyEventHandler = sysUIKeyEventHandler;
         mPrimaryBouncerInteractor = primaryBouncerInteractor;
         mAlternateBouncerInteractor = alternateBouncerInteractor;
+        mQuickSettingsController = quickSettingsController;
 
         // This view is not part of the newly inflated expanded status bar.
         mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
@@ -454,6 +457,16 @@
                         && !bouncerShowing
                         && !mStatusBarStateController.isDozing()) {
                     if (mDragDownHelper.isDragDownEnabled()) {
+                        if (mFeatureFlagsClassic.isEnabled(Flags.MIGRATE_NSSL)) {
+                            // When on lockscreen, if the touch originates at the top of the screen
+                            // go directly to QS and not the shade
+                            if (mQuickSettingsController.shouldQuickSettingsIntercept(
+                                    ev.getX(), ev.getY(), 0)) {
+                                mShadeLogger.d("NSWVC: QS intercepted");
+                                return true;
+                            }
+                        }
+
                         // This handles drag down over lockscreen
                         boolean result = mDragDownHelper.onInterceptTouchEvent(ev);
                         if (mFeatureFlagsClassic.isEnabled(Flags.MIGRATE_NSSL)) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index a4c4503..b2ffeb3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -275,7 +275,7 @@
                 when (state) {
                     is ObservableTransitionState.Idle -> false
                     is ObservableTransitionState.Transition ->
-                        state.isUserInputDriven &&
+                        state.isInitiatedByUserInput &&
                             (state.toScene == sceneKey || state.fromScene == sceneKey)
                 }
             }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index fda4133..225f125 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -810,6 +810,7 @@
                     SceneKey.Bouncer,
                     flowOf(.5f),
                     false,
+                    isUserInputOngoing = flowOf(false),
                 )
             runCurrent()
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason")
@@ -825,7 +826,8 @@
                     SceneKey.Bouncer,
                     SceneKey.Gone,
                     flowOf(.5f),
-                    false
+                    false,
+                    isUserInputOngoing = flowOf(false),
                 )
             runCurrent()
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
@@ -842,7 +844,8 @@
                     SceneKey.Gone,
                     SceneKey.Bouncer,
                     flowOf(.5f),
-                    false
+                    false,
+                    isUserInputOngoing = flowOf(false),
                 )
             runCurrent()
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason")
@@ -860,7 +863,8 @@
                     SceneKey.Bouncer,
                     SceneKey.Gone,
                     flowOf(.5f),
-                    false
+                    false,
+                    isUserInputOngoing = flowOf(false),
                 )
             runCurrent()
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
@@ -876,6 +880,7 @@
                     SceneKey.Lockscreen,
                     flowOf(.5f),
                     false,
+                    isUserInputOngoing = flowOf(false),
                 )
             runCurrent()
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen, null), "reason")
@@ -893,6 +898,7 @@
                     SceneKey.Gone,
                     flowOf(.5f),
                     false,
+                    isUserInputOngoing = flowOf(false),
                 )
             runCurrent()
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 5e7b857..2d95b09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -54,6 +54,7 @@
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.statusbar.events.ANIMATING_OUT
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
@@ -211,6 +212,16 @@
     }
 
     @Test
+    fun testIgnoresAnimatedInWhenDialogAnimatingOut() {
+        val container = initializeFingerprintContainer(addToView = false)
+        container.mContainerState = ANIMATING_OUT
+        container.addToView()
+        waitForIdleSync()
+
+        verify(callback, never()).onDialogAnimatedIn(anyLong(), anyBoolean())
+    }
+
+    @Test
     fun testDismissBeforeIntroEnd() {
         val container = initializeFingerprintContainer()
         waitForIdleSync()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt
index 3fbdeec..67d3a20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt
@@ -37,8 +37,6 @@
 import com.android.systemui.biometrics.shared.model.SensorStrength
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.dump.logcatLogBuffer
-import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.flags.Flags.REST_TO_UNLOCK
 import com.android.systemui.log.SideFpsLogger
 import com.android.systemui.res.R
 import com.android.systemui.util.mockito.whenever
@@ -94,7 +92,6 @@
         whenever(displayStateInteractor.currentRotation).thenReturn(currentRotation)
 
         contextDisplayInfo.uniqueId = "current-display"
-        val featureFlags = FakeFeatureFlagsClassic().apply { set(REST_TO_UNLOCK, true) }
         whenever(fingerprintInteractiveToAuthProvider.enabledForCurrentUser)
             .thenReturn(isRestToUnlockEnabled)
         underTest =
@@ -103,7 +100,6 @@
                 fingerprintRepository,
                 windowManager,
                 displayStateInteractor,
-                featureFlags,
                 Optional.of(fingerprintInteractiveToAuthProvider),
                 SideFpsLogger(logcatLogBuffer("SfpsLogger"))
             )
@@ -136,7 +132,7 @@
     @Test
     fun authenticationDurationIsAvailableWhenSFPSSensorIsAvailable() =
         testScope.runTest {
-            assertThat(collectLastValue(underTest.authenticationDuration)())
+            assertThat(underTest.authenticationDuration)
                 .isEqualTo(context.resources.getInteger(R.integer.config_restToUnlockDuration))
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
index 0a9a15e..61d1502 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
@@ -21,16 +21,23 @@
 import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_STARTED
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.FakeCommunalRepository
 import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
+import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
+import com.android.systemui.communal.shared.model.CommunalSceneKey
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
+import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
+import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.SceneModel
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -46,18 +53,28 @@
 
     @Mock private lateinit var userTracker: UserTracker
 
-    private val testDispatcher = StandardTestDispatcher()
-    private val testScope = TestScope(testDispatcher)
-
+    private lateinit var testScope: TestScope
     private lateinit var underTest: CommunalTutorialInteractor
     private lateinit var keyguardRepository: FakeKeyguardRepository
     private lateinit var keyguardInteractor: KeyguardInteractor
     private lateinit var communalTutorialRepository: FakeCommunalTutorialRepository
+    private lateinit var sceneContainerFlags: FakeSceneContainerFlags
+    private lateinit var communalInteractor: CommunalInteractor
+    private lateinit var communalRepository: FakeCommunalRepository
+
+    private val utils = SceneTestUtils(this)
+    private lateinit var sceneInteractor: SceneInteractor
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
+        sceneInteractor = utils.sceneInteractor()
+        testScope = utils.testScope
+        sceneContainerFlags = utils.sceneContainerFlags.apply { enabled = false }
+        communalRepository = FakeCommunalRepository(isCommunalEnabled = true)
+        communalInteractor = CommunalInteractor(communalRepository, FakeCommunalWidgetRepository())
+
         val withDeps = KeyguardInteractorFactory.create()
         keyguardInteractor = withDeps.keyguardInteractor
         keyguardRepository = withDeps.repository
@@ -65,8 +82,12 @@
 
         underTest =
             CommunalTutorialInteractor(
-                keyguardInteractor = keyguardInteractor,
+                scope = testScope.backgroundScope,
                 communalTutorialRepository = communalTutorialRepository,
+                keyguardInteractor = keyguardInteractor,
+                communalInteractor = communalInteractor,
+                sceneContainerFlags = sceneContainerFlags,
+                sceneInteractor = sceneInteractor,
             )
 
         whenever(userTracker.userHandle).thenReturn(mock())
@@ -87,6 +108,7 @@
             val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
             keyguardRepository.setKeyguardShowing(true)
             keyguardRepository.setKeyguardOccluded(false)
+            communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
             communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
             assertThat(isTutorialAvailable).isFalse()
         }
@@ -97,6 +119,7 @@
             val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
             keyguardRepository.setKeyguardShowing(true)
             keyguardRepository.setKeyguardOccluded(false)
+            communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
             communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
             assertThat(isTutorialAvailable).isTrue()
         }
@@ -107,7 +130,188 @@
             val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
             keyguardRepository.setKeyguardShowing(true)
             keyguardRepository.setKeyguardOccluded(false)
+            communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
             communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
             assertThat(isTutorialAvailable).isTrue()
         }
+
+    /* Testing tutorial states with transitions when flexiglass off */
+    @Test
+    fun tutorialState_notStartedAndCommunalSceneShowing_tutorialStarted() =
+        testScope.runTest {
+            val tutorialSettingState by
+                collectLastValue(communalTutorialRepository.tutorialSettingState)
+            val currentScene by collectLastValue(communalInteractor.desiredScene)
+            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
+
+            communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
+
+            assertThat(currentScene).isEqualTo(CommunalSceneKey.Communal)
+            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
+        }
+
+    @Test
+    fun tutorialState_startedAndCommunalSceneShowing_stateWillNotUpdate() =
+        testScope.runTest {
+            val tutorialSettingState by
+                collectLastValue(communalTutorialRepository.tutorialSettingState)
+            val currentScene by collectLastValue(communalInteractor.desiredScene)
+            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
+
+            communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
+
+            assertThat(currentScene).isEqualTo(CommunalSceneKey.Communal)
+            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
+        }
+
+    @Test
+    fun tutorialState_completedAndCommunalSceneShowing_stateWillNotUpdate() =
+        testScope.runTest {
+            val tutorialSettingState by
+                collectLastValue(communalTutorialRepository.tutorialSettingState)
+            val currentScene by collectLastValue(communalInteractor.desiredScene)
+            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+            communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
+
+            assertThat(currentScene).isEqualTo(CommunalSceneKey.Communal)
+            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
+        }
+
+    @Test
+    fun tutorialState_notStartedAndCommunalSceneNotShowing_stateWillNotUpdate() =
+        testScope.runTest {
+            val tutorialSettingState by
+                collectLastValue(communalTutorialRepository.tutorialSettingState)
+            val currentScene by collectLastValue(communalInteractor.desiredScene)
+            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
+
+            communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
+
+            assertThat(currentScene).isEqualTo(CommunalSceneKey.Blank)
+            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_NOT_STARTED)
+        }
+
+    @Test
+    fun tutorialState_startedAndCommunalSceneNotShowing_tutorialCompleted() =
+        testScope.runTest {
+            val tutorialSettingState by
+                collectLastValue(communalTutorialRepository.tutorialSettingState)
+            val currentScene by collectLastValue(communalInteractor.desiredScene)
+            communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
+            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
+
+            communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
+
+            assertThat(currentScene).isEqualTo(CommunalSceneKey.Blank)
+            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
+        }
+
+    @Test
+    fun tutorialState_completedAndCommunalSceneNotShowing_stateWillNotUpdate() =
+        testScope.runTest {
+            val tutorialSettingState by
+                collectLastValue(communalTutorialRepository.tutorialSettingState)
+            val currentScene by collectLastValue(communalInteractor.desiredScene)
+            communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
+            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+            communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
+
+            assertThat(currentScene).isEqualTo(CommunalSceneKey.Blank)
+            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
+        }
+
+    /* Testing tutorial states with transitions when flexiglass on */
+    @Test
+    fun tutorialState_notStartedCommunalSceneShowingAndFlexiglassOn_tutorialStarted() =
+        testScope.runTest {
+            sceneContainerFlags.enabled = true
+            val tutorialSettingState by
+                collectLastValue(communalTutorialRepository.tutorialSettingState)
+            val currentScene by collectLastValue(sceneInteractor.desiredScene)
+            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
+
+            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Communal), "reason")
+
+            assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Communal))
+            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
+        }
+
+    @Test
+    fun tutorialState_startedCommunalSceneShowingAndFlexiglassOn_stateWillNotUpdate() =
+        testScope.runTest {
+            sceneContainerFlags.enabled = true
+            val tutorialSettingState by
+                collectLastValue(communalTutorialRepository.tutorialSettingState)
+            val currentScene by collectLastValue(sceneInteractor.desiredScene)
+            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
+
+            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Communal), "reason")
+
+            assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Communal))
+            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
+        }
+
+    @Test
+    fun tutorialState_completedCommunalSceneShowingAndFlexiglassOn_stateWillNotUpdate() =
+        testScope.runTest {
+            sceneContainerFlags.enabled = true
+            val tutorialSettingState by
+                collectLastValue(communalTutorialRepository.tutorialSettingState)
+            val currentScene by collectLastValue(sceneInteractor.desiredScene)
+            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Communal), "reason")
+
+            assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Communal))
+            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
+        }
+
+    @Test
+    fun tutorialState_notStartedCommunalSceneNotShowingAndFlexiglassOn_stateWillNotUpdate() =
+        testScope.runTest {
+            sceneContainerFlags.enabled = true
+            val tutorialSettingState by
+                collectLastValue(communalTutorialRepository.tutorialSettingState)
+            val currentScene by collectLastValue(sceneInteractor.desiredScene)
+            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
+
+            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen), "reason")
+
+            assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
+            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_NOT_STARTED)
+        }
+
+    @Test
+    fun tutorialState_startedCommunalSceneNotShowingAndFlexiglassOn_tutorialCompleted() =
+        testScope.runTest {
+            sceneContainerFlags.enabled = true
+            val tutorialSettingState by
+                collectLastValue(communalTutorialRepository.tutorialSettingState)
+            val currentScene by collectLastValue(sceneInteractor.desiredScene)
+            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Communal), "reason")
+            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
+
+            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen), "reason")
+
+            assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
+            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
+        }
+
+    @Test
+    fun tutorialState_completedCommunalSceneNotShowingAndFlexiglassOn_stateWillNotUpdate() =
+        testScope.runTest {
+            sceneContainerFlags.enabled = true
+            val tutorialSettingState by
+                collectLastValue(communalTutorialRepository.tutorialSettingState)
+            val currentScene by collectLastValue(sceneInteractor.desiredScene)
+            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Communal), "reason")
+            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen), "reason")
+
+            assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
+            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
+        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
index d80dd76..806930d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
@@ -380,6 +380,32 @@
         }
 
     @Test
+    fun pendingDisplay_afterConfigChanged_doesNotChange() =
+        testScope.runTest {
+            val pendingDisplay by lastPendingDisplay()
+
+            sendOnDisplayConnected(1, TYPE_EXTERNAL)
+            val initialPendingDisplay: DisplayRepository.PendingDisplay? = pendingDisplay
+            assertThat(pendingDisplay).isNotNull()
+            sendOnDisplayChanged(1)
+
+            assertThat(initialPendingDisplay).isEqualTo(pendingDisplay)
+        }
+
+    @Test
+    fun pendingDisplay_afterNewHigherDisplayConnected_changes() =
+        testScope.runTest {
+            val pendingDisplay by lastPendingDisplay()
+
+            sendOnDisplayConnected(1, TYPE_EXTERNAL)
+            val initialPendingDisplay: DisplayRepository.PendingDisplay? = pendingDisplay
+            assertThat(pendingDisplay).isNotNull()
+            sendOnDisplayConnected(2, TYPE_EXTERNAL)
+
+            assertThat(initialPendingDisplay).isNotEqualTo(pendingDisplay)
+        }
+
+    @Test
     fun onPendingDisplay_OneInternalAndOneExternalDisplay_internalIgnored() =
         testScope.runTest {
             val pendingDisplay by lastPendingDisplay()
@@ -466,6 +492,10 @@
         connectedDisplayListener.value.onDisplayConnected(id)
     }
 
+    private fun sendOnDisplayChanged(id: Int) {
+        connectedDisplayListener.value.onDisplayChanged(id)
+    }
+
     private fun setDisplays(displays: List<Display>) {
         whenever(displayManager.displays).thenReturn(displays.toTypedArray())
         displays.forEach { display ->
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 27325d3..ad2ec72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -203,7 +203,8 @@
                     fromScene = SceneKey.Gone,
                     toScene = SceneKey.Lockscreen,
                     progress = flowOf(0f),
-                    isUserInputDriven = false,
+                    isInitiatedByUserInput = false,
+                    isUserInputOngoing = flowOf(false),
                 )
             runCurrent()
             assertThat(isAnimate).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt
new file mode 100644
index 0000000..4a22113
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.systemui.qs.tiles.impl.custom
+
+import android.content.ComponentName
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.external.TileLifecycleManager
+import com.android.systemui.qs.external.TileServiceManager
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepository
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepositoryImpl
+import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTileDefaultsRepository
+import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTileDefaultsRepository.DefaultsRequest
+import com.android.systemui.util.mockito.capture
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CustomTilePackageUpdatesRepositoryTest : SysuiTestCase() {
+
+    @Mock private lateinit var tileServiceManager: TileServiceManager
+
+    @Captor
+    private lateinit var listenerCaptor: ArgumentCaptor<TileLifecycleManager.TileChangeListener>
+
+    private val defaultsRepository = FakeCustomTileDefaultsRepository()
+    private val testDispatcher = StandardTestDispatcher()
+    private val testScope = TestScope(testDispatcher)
+
+    private lateinit var underTest: CustomTilePackageUpdatesRepository
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+
+        underTest =
+            CustomTilePackageUpdatesRepositoryImpl(
+                TileSpec.create(COMPONENT_1),
+                USER,
+                tileServiceManager,
+                defaultsRepository,
+                testScope.backgroundScope,
+            )
+    }
+
+    @Test
+    fun packageChangesUpdatesDefaults() =
+        testScope.runTest {
+            val events = mutableListOf<Unit>()
+            underTest.packageChanges.onEach { events.add(it) }.launchIn(backgroundScope)
+            runCurrent()
+            verify(tileServiceManager).setTileChangeListener(capture(listenerCaptor))
+
+            emitPackageChange()
+            runCurrent()
+
+            assertThat(events).hasSize(1)
+            assertThat(defaultsRepository.defaultsRequests).isNotEmpty()
+            assertThat(defaultsRepository.defaultsRequests.last())
+                .isEqualTo(DefaultsRequest(USER, COMPONENT_1, true))
+        }
+
+    @Test
+    fun packageChangesEmittedOnlyForTheTile() =
+        testScope.runTest {
+            val events = mutableListOf<Unit>()
+            underTest.packageChanges.onEach { events.add(it) }.launchIn(backgroundScope)
+            runCurrent()
+            verify(tileServiceManager).setTileChangeListener(capture(listenerCaptor))
+
+            emitPackageChange(COMPONENT_2)
+            runCurrent()
+
+            assertThat(events).isEmpty()
+        }
+
+    private fun emitPackageChange(componentName: ComponentName = COMPONENT_1) {
+        listenerCaptor.value.onTileChanged(componentName)
+    }
+
+    private companion object {
+        val USER = UserHandle(0)
+        val COMPONENT_1 = ComponentName("pkg.test.1", "cls.test")
+        val COMPONENT_2 = ComponentName("pkg.test.2", "cls.test")
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 88a5c17..9c0456c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -54,6 +54,7 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
@@ -466,7 +467,8 @@
                 fromScene = getCurrentSceneInUi(),
                 toScene = to.key,
                 progress = progressFlow,
-                isUserInputDriven = false,
+                isInitiatedByUserInput = false,
+                isUserInputOngoing = flowOf(false),
             )
         runCurrent()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
index 432bd0f..d669006 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
@@ -29,6 +29,7 @@
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -51,6 +52,7 @@
                     SceneKey.Lockscreen,
                     SceneKey.Bouncer,
                     SceneKey.Gone,
+                    SceneKey.Communal,
                 )
             )
     }
@@ -119,7 +121,8 @@
                     fromScene = SceneKey.Lockscreen,
                     toScene = SceneKey.Shade,
                     progress = progress,
-                    isUserInputDriven = false,
+                    isInitiatedByUserInput = false,
+                    isUserInputOngoing = flowOf(false),
                 )
             assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index 8b23d18..3f032a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -83,7 +83,8 @@
                     fromScene = SceneKey.Lockscreen,
                     toScene = SceneKey.Shade,
                     progress = progress,
-                    isUserInputDriven = false,
+                    isInitiatedByUserInput = false,
+                    isUserInputOngoing = flowOf(false),
                 )
             assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
 
@@ -121,7 +122,8 @@
                     fromScene = underTest.desiredScene.value.key,
                     toScene = SceneKey.Shade,
                     progress = progress,
-                    isUserInputDriven = false,
+                    isInitiatedByUserInput = false,
+                    isUserInputOngoing = flowOf(false),
                 )
             assertThat(transitionTo).isEqualTo(SceneKey.Shade)
 
@@ -158,7 +160,8 @@
                         fromScene = SceneKey.Gone,
                         toScene = SceneKey.Lockscreen,
                         progress = flowOf(0.5f),
-                        isUserInputDriven = false,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             val transitioning by
@@ -177,7 +180,8 @@
                         fromScene = SceneKey.Shade,
                         toScene = SceneKey.QuickSettings,
                         progress = flowOf(0.5f),
-                        isUserInputDriven = false,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             underTest.setTransitionState(transitionState)
@@ -194,7 +198,8 @@
                         fromScene = SceneKey.Shade,
                         toScene = SceneKey.Lockscreen,
                         progress = flowOf(0.5f),
-                        isUserInputDriven = false,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             val transitioning by
@@ -222,7 +227,8 @@
                     fromScene = SceneKey.Shade,
                     toScene = SceneKey.Lockscreen,
                     progress = flowOf(0.5f),
-                    isUserInputDriven = false,
+                    isInitiatedByUserInput = false,
+                    isUserInputOngoing = flowOf(false),
                 )
             assertThat(transitioning).isTrue()
 
@@ -231,6 +237,95 @@
         }
 
     @Test
+    fun isTransitionUserInputOngoing_idle_false() =
+        testScope.runTest {
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Idle(SceneKey.Shade)
+                )
+            val isTransitionUserInputOngoing by
+                collectLastValue(underTest.isTransitionUserInputOngoing)
+            underTest.setTransitionState(transitionState)
+
+            assertThat(isTransitionUserInputOngoing).isFalse()
+        }
+
+    @Test
+    fun isTransitionUserInputOngoing_transition_true() =
+        testScope.runTest {
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = SceneKey.Shade,
+                        toScene = SceneKey.Lockscreen,
+                        progress = flowOf(0.5f),
+                        isInitiatedByUserInput = true,
+                        isUserInputOngoing = flowOf(true),
+                    )
+                )
+            val isTransitionUserInputOngoing by
+                collectLastValue(underTest.isTransitionUserInputOngoing)
+            underTest.setTransitionState(transitionState)
+
+            assertThat(isTransitionUserInputOngoing).isTrue()
+        }
+
+    @Test
+    fun isTransitionUserInputOngoing_updateMidTransition_false() =
+        testScope.runTest {
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = SceneKey.Shade,
+                        toScene = SceneKey.Lockscreen,
+                        progress = flowOf(0.5f),
+                        isInitiatedByUserInput = true,
+                        isUserInputOngoing = flowOf(true),
+                    )
+                )
+            val isTransitionUserInputOngoing by
+                collectLastValue(underTest.isTransitionUserInputOngoing)
+            underTest.setTransitionState(transitionState)
+
+            assertThat(isTransitionUserInputOngoing).isTrue()
+
+            transitionState.value =
+                ObservableTransitionState.Transition(
+                    fromScene = SceneKey.Shade,
+                    toScene = SceneKey.Lockscreen,
+                    progress = flowOf(0.6f),
+                    isInitiatedByUserInput = true,
+                    isUserInputOngoing = flowOf(false),
+                )
+
+            assertThat(isTransitionUserInputOngoing).isFalse()
+        }
+
+    @Test
+    fun isTransitionUserInputOngoing_updateOnIdle_false() =
+        testScope.runTest {
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = SceneKey.Shade,
+                        toScene = SceneKey.Lockscreen,
+                        progress = flowOf(0.5f),
+                        isInitiatedByUserInput = true,
+                        isUserInputOngoing = flowOf(true),
+                    )
+                )
+            val isTransitionUserInputOngoing by
+                collectLastValue(underTest.isTransitionUserInputOngoing)
+            underTest.setTransitionState(transitionState)
+
+            assertThat(isTransitionUserInputOngoing).isTrue()
+
+            transitionState.value = ObservableTransitionState.Idle(scene = SceneKey.Lockscreen)
+
+            assertThat(isTransitionUserInputOngoing).isFalse()
+        }
+
+    @Test
     fun isVisible() =
         testScope.runTest {
             val isVisible by collectLastValue(underTest.isVisible)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index c1f2d0c..c0b5861 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -115,7 +115,8 @@
                     fromScene = SceneKey.Gone,
                     toScene = SceneKey.Shade,
                     progress = flowOf(0.5f),
-                    isUserInputDriven = false,
+                    isInitiatedByUserInput = false,
+                    isUserInputOngoing = flowOf(false),
                 )
             assertThat(isVisible).isTrue()
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Shade), "reason")
@@ -128,7 +129,8 @@
                     fromScene = SceneKey.Shade,
                     toScene = SceneKey.Gone,
                     progress = flowOf(0.5f),
-                    isUserInputDriven = false,
+                    isInitiatedByUserInput = false,
+                    isUserInputOngoing = flowOf(false),
                 )
             assertThat(isVisible).isTrue()
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone), "reason")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 4e3e165..5459779 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -97,6 +97,7 @@
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
+import java.util.Optional
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.test.TestScope
@@ -111,9 +112,8 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-import java.util.Optional
 import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -140,6 +140,7 @@
     @Mock private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
     @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
     @Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController
+    @Mock private lateinit var quickSettingsController: QuickSettingsController
     @Mock
     private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
     @Mock private lateinit var lockIconViewController: LockIconViewController
@@ -166,7 +167,7 @@
     @Mock lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
     private val notificationLaunchAnimationRepository = NotificationLaunchAnimationRepository()
     private val notificationLaunchAnimationInteractor =
-            NotificationLaunchAnimationInteractor(notificationLaunchAnimationRepository)
+        NotificationLaunchAnimationInteractor(notificationLaunchAnimationRepository)
 
     private lateinit var fakeClock: FakeSystemClock
     private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
@@ -274,6 +275,7 @@
                 ),
                 BouncerLogger(logcatLogBuffer("BouncerLog")),
                 sysUIKeyEventHandler,
+                quickSettingsController,
                 primaryBouncerInteractor,
                 alternateBouncerInteractor,
                 mSelectedUserInteractor,
@@ -460,9 +462,11 @@
         // AND alternate bouncer doesn't want the touch
         whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
             .thenReturn(false)
+        // AND quick settings controller doesn't want it
+        whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any()))
+            .thenReturn(false)
         // AND the lock icon wants the touch
-        whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT))
-                .thenReturn(true)
+        whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(true)
 
         featureFlagsClassic.set(MIGRATE_NSSL, true)
 
@@ -476,10 +480,31 @@
         whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
         // AND alternate bouncer doesn't want the touch
         whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
-                .thenReturn(false)
+            .thenReturn(false)
         // AND the lock icon does NOT want the touch
-        whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT))
-                .thenReturn(false)
+        whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(false)
+        // AND quick settings controller doesn't want it
+        whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any()))
+            .thenReturn(false)
+
+        featureFlagsClassic.set(MIGRATE_NSSL, true)
+
+        // THEN touch should NOT be intercepted by NotificationShade
+        assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue()
+    }
+
+    @Test
+    fun shouldInterceptTouchEvent_dozing_touchInStatusBar_touchIntercepted() {
+        // GIVEN dozing
+        whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
+        // AND alternate bouncer doesn't want the touch
+        whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
+            .thenReturn(false)
+        // AND the lock icon does NOT want the touch
+        whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(false)
+        // AND quick settings controller DOES want it
+        whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any()))
+            .thenReturn(true)
 
         featureFlagsClassic.set(MIGRATE_NSSL, true)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 3d5d26a..a6ab6a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -121,6 +121,7 @@
     @Mock private lateinit var notificationStackScrollLayout: NotificationStackScrollLayout
     @Mock private lateinit var notificationShadeDepthController: NotificationShadeDepthController
     @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
+    @Mock private lateinit var quickSettingsController: QuickSettingsController
     @Mock
     private lateinit var notificationStackScrollLayoutController:
         NotificationStackScrollLayoutController
@@ -264,6 +265,7 @@
                 ),
                 BouncerLogger(logcatLogBuffer("BouncerLog")),
                 Mockito.mock(SysUIKeyEventHandler::class.java),
+                quickSettingsController,
                 primaryBouncerInteractor,
                 alternateBouncerInteractor,
                 mSelectedUserInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
index 3a260ae..8b8a625 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
@@ -60,6 +60,7 @@
 import dagger.Component
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
@@ -495,7 +496,8 @@
                         fromScene = SceneKey.Lockscreen,
                         toScene = key,
                         progress = progress,
-                        isUserInputDriven = false,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             sceneInteractor.setTransitionState(transitionState)
@@ -532,7 +534,8 @@
                         fromScene = key,
                         toScene = SceneKey.Lockscreen,
                         progress = progress,
-                        isUserInputDriven = false,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             sceneInteractor.setTransitionState(transitionState)
@@ -568,7 +571,8 @@
                         fromScene = SceneKey.Lockscreen,
                         toScene = SceneKey.Shade,
                         progress = progress,
-                        isUserInputDriven = false,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             sceneInteractor.setTransitionState(transitionState)
@@ -844,7 +848,8 @@
                         fromScene = SceneKey.Lockscreen,
                         toScene = key,
                         progress = progress,
-                        isUserInputDriven = false,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             sceneInteractor.setTransitionState(transitionState)
@@ -881,7 +886,8 @@
                         fromScene = SceneKey.Lockscreen,
                         toScene = key,
                         progress = progress,
-                        isUserInputDriven = true,
+                        isInitiatedByUserInput = true,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             sceneInteractor.setTransitionState(transitionState)
@@ -918,7 +924,8 @@
                         fromScene = key,
                         toScene = SceneKey.Lockscreen,
                         progress = progress,
-                        isUserInputDriven = false,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             sceneInteractor.setTransitionState(transitionState)
@@ -955,7 +962,8 @@
                         fromScene = key,
                         toScene = SceneKey.Lockscreen,
                         progress = progress,
-                        isUserInputDriven = true,
+                        isInitiatedByUserInput = true,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             sceneInteractor.setTransitionState(transitionState)
@@ -990,8 +998,9 @@
                     ObservableTransitionState.Transition(
                         fromScene = SceneKey.Lockscreen,
                         toScene = SceneKey.QuickSettings,
-                        progress = progress,
-                        isUserInputDriven = true,
+                        progress = MutableStateFlow(0f),
+                        isInitiatedByUserInput = true,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             sceneInteractor.setTransitionState(transitionState)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
index f98267b..02d15de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
@@ -89,7 +90,8 @@
                         fromScene = SceneKey.Shade,
                         toScene = SceneKey.QuickSettings,
                         progress = MutableStateFlow(0.5f),
-                        isUserInputDriven = false,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             )
@@ -107,7 +109,8 @@
                         fromScene = SceneKey.QuickSettings,
                         toScene = SceneKey.Shade,
                         progress = MutableStateFlow(0.5f),
-                        isUserInputDriven = false,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             )
@@ -125,7 +128,8 @@
                         fromScene = SceneKey.Gone,
                         toScene = SceneKey.Shade,
                         progress = MutableStateFlow(0.5f),
-                        isUserInputDriven = false,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
                     )
                 )
             )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTilePackageUpdatesRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTilePackageUpdatesRepository.kt
new file mode 100644
index 0000000..8f972f5
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTilePackageUpdatesRepository.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.systemui.qs.tiles.impl.custom.data.repository
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+
+class FakeCustomTilePackageUpdatesRepository : CustomTilePackageUpdatesRepository {
+
+    private val mutablePackageChanges = MutableSharedFlow<Unit>()
+
+    override val packageChanges: Flow<Unit>
+        get() = mutablePackageChanges
+
+    suspend fun emitPackageChange() {
+        mutablePackageChanges.emit(Unit)
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index bdddc04..6beb513 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -131,6 +131,7 @@
             SceneKey.Lockscreen,
             SceneKey.Bouncer,
             SceneKey.Gone,
+            SceneKey.Communal,
         )
     }
 
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 7dbf61b..a14f3fe 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -183,6 +183,7 @@
         "android.hardware.power.stats-V2-java",
         "android.hidl.manager-V1.2-java",
         "cbor-java",
+        "com.android.media.audio-aconfig-java",
         "dropbox_flags_lib",
         "icu4j_calendar_astronomer",
         "android.security.aaid_aidl-java",
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 638abdb..4f32220 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -300,14 +300,6 @@
             @UserIdInt int userId, @NonNull String[] packageNames, boolean suspended);
 
     /**
-     * Suspend or unsuspend packages in a profile when quiet mode is toggled.
-     *
-     * @param userId The target user.
-     * @param suspended Whether the packages should be suspended or unsuspended.
-     */
-    public abstract void setPackagesSuspendedForQuietMode(@UserIdInt int userId, boolean suspended);
-
-    /**
      * Get the information describing the dialog to be shown to the user when they try to launch a
      * suspended application.
      *
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index db89cac..c4cb816 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -179,6 +179,15 @@
 3130 pm_snapshot_stats (build_count|1|1),(reuse_count|1|1),(big_builds|1|1),(short_lived|1|1),(max_build_time|1|3),(cumm_build_time|2|3)
 # Snapshot rebuild instance
 3131 pm_snapshot_rebuild (build_time|1|3),(lifetime|1|3)
+# Caller information to clear application data
+1003160 pm_clear_app_data_caller (pid|1),(uid|1),(package|3)
+# ---------------------------
+# Installer.java
+# ---------------------------
+# Caller Information to clear application data
+1003200 installer_clear_app_data_caller (pid|1),(uid|1),(package|3),(flags|1)
+# Call stack to clear application data
+1003201 installer_clear_app_data_call_stack (method|3),(class|3),(file|3),(line|1)
 
 # ---------------------------
 # InputMethodManagerService.java
diff --git a/services/core/java/com/android/server/MasterClearReceiver.java b/services/core/java/com/android/server/MasterClearReceiver.java
index 5a15f17..2b30c01 100644
--- a/services/core/java/com/android/server/MasterClearReceiver.java
+++ b/services/core/java/com/android/server/MasterClearReceiver.java
@@ -88,6 +88,9 @@
         mWipeEsims = intent.getBooleanExtra(Intent.EXTRA_WIPE_ESIMS, false);
         final boolean forceWipe = intent.getBooleanExtra(Intent.EXTRA_FORCE_MASTER_CLEAR, false)
                 || intent.getBooleanExtra(Intent.EXTRA_FORCE_FACTORY_RESET, false);
+        // This is ONLY used by TestHarnessService within System Server, so we don't add a proper
+        // API constant in Intent for this.
+        final boolean keepMemtagMode = intent.getBooleanExtra("keep_memtag_mode", false);
 
         // TODO(b/189938391): properly handle factory reset on headless system user mode.
         final int sendingUserId = getSendingUserId();
@@ -110,9 +113,11 @@
                 try {
                     Slog.i(TAG, "Calling RecoverySystem.rebootWipeUserData(context, "
                             + "shutdown=" + shutdown + ", reason=" + reason
-                            + ", forceWipe=" + forceWipe + ", wipeEsims=" + mWipeEsims + ")");
+                            + ", forceWipe=" + forceWipe + ", wipeEsims=" + mWipeEsims
+                            + ", keepMemtagMode=" + keepMemtagMode + ")");
                     RecoverySystem
-                            .rebootWipeUserData(context, shutdown, reason, forceWipe, mWipeEsims);
+                            .rebootWipeUserData(
+                                context, shutdown, reason, forceWipe, mWipeEsims, keepMemtagMode);
                     Slog.wtf(TAG, "Still running after master clear?!");
                 } catch (IOException e) {
                     Slog.e(TAG, "Can't perform master clear/factory reset", e);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9677248..1566113 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3548,6 +3548,7 @@
         enforceNotIsolatedCaller("clearApplicationUserData");
         int uid = Binder.getCallingUid();
         int pid = Binder.getCallingPid();
+        EventLog.writeEvent(EventLogTags.AM_CLEAR_APP_DATA_CALLER, pid, uid, packageName);
         final int resolvedUserId = mUserController.handleIncomingUser(pid, uid, userId, false,
                 ALLOW_FULL_ONLY, "clearApplicationUserData", null);
 
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 9e9db6a..931914f 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -129,3 +129,6 @@
 
 # Intent Sender redirect for UserHandle.USER_CURRENT
 30110 am_intent_sender_redirect_user (userId|1|5)
+
+# Caller information to clear application data
+1030002 am_clear_app_data_caller (pid|1),(uid|1),(package|3)
diff --git a/services/core/java/com/android/server/am/LmkdStatsReporter.java b/services/core/java/com/android/server/am/LmkdStatsReporter.java
index 1e4dd64..507fd9e 100644
--- a/services/core/java/com/android/server/am/LmkdStatsReporter.java
+++ b/services/core/java/com/android/server/am/LmkdStatsReporter.java
@@ -107,6 +107,8 @@
                 return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__LOW_MEM_AND_SWAP_UTIL;
             case LOW_FILECACHE_AFTER_THRASHING:
                 return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__LOW_FILECACHE_AFTER_THRASHING;
+            case LOW_MEM:
+                return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__LOW_MEM;
             default:
                 return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__UNKNOWN;
         }
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index c1a2482..e6cdbb5 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -128,6 +128,7 @@
         "biometrics",
         "biometrics_framework",
         "biometrics_integration",
+        "camera_hal",
         "camera_platform",
         "car_framework",
         "car_perception",
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index ae62a7a..87633e9 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -76,7 +76,6 @@
 import android.app.IStopUserCallback;
 import android.app.IUserSwitchObserver;
 import android.app.KeyguardManager;
-import android.app.admin.DevicePolicyManagerInternal;
 import android.app.usage.UsageEvents;
 import android.appwidget.AppWidgetManagerInternal;
 import android.content.Context;
@@ -1497,10 +1496,8 @@
 
     private boolean shouldStartWithParent(UserInfo user) {
         final UserProperties properties = getUserProperties(user.id);
-        DevicePolicyManagerInternal dpmi =
-                LocalServices.getService(DevicePolicyManagerInternal.class);
         return (properties != null && properties.getStartWithParent())
-                && (!user.isQuietModeEnabled() || dpmi.isKeepProfilesRunningEnabled());
+                && !user.isQuietModeEnabled();
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index cbaf05b..a770b66 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -22,3 +22,10 @@
     description: "Detect abusive FGS behavior for certain types (camera, mic, media, location)."
     bug: "295545575"
 }
+
+flag {
+    name: "fgs_boot_completed"
+    namespace: "backstage_power"
+    description: "Disable BOOT_COMPLETED broadcast FGS start for certain types"
+    bug: "296558535"
+}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index a8fa313..b209fb0 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -37,6 +37,7 @@
 import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
 import static android.provider.Settings.Secure.VOLUME_HUSH_VIBRATE;
 
+import static com.android.media.audio.Flags.bluetoothMacAddressAnonymization;
 import static com.android.server.audio.SoundDoseHelper.ACTION_CHECK_MUSIC_ACTIVE;
 import static com.android.server.utils.EventLogger.Event.ALOGE;
 import static com.android.server.utils.EventLogger.Event.ALOGI;
@@ -203,7 +204,6 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.Preconditions;
-import com.android.media.audio.flags.Flags;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -10509,7 +10509,7 @@
     }
 
     private boolean isBluetoothPrividged() {
-        if (!Flags.bluetoothMacAddressAnonymization()) {
+        if (!bluetoothMacAddressAnonymization()) {
             return true;
         }
         return PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
diff --git a/services/core/java/com/android/server/audio/HardeningEnforcer.java b/services/core/java/com/android/server/audio/HardeningEnforcer.java
index c7556da..4ceb83b2 100644
--- a/services/core/java/com/android/server/audio/HardeningEnforcer.java
+++ b/services/core/java/com/android/server/audio/HardeningEnforcer.java
@@ -15,7 +15,7 @@
  */
 package com.android.server.audio;
 
-import static com.android.media.audio.flags.Flags.autoPublicVolumeApiHardening;
+import static android.media.audio.Flags.autoPublicVolumeApiHardening;
 
 import android.Manifest;
 import android.content.Context;
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index 3cf0786..330818e 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -27,6 +27,7 @@
 import android.content.ServiceConnection;
 import android.media.IMediaRoute2ProviderService;
 import android.media.IMediaRoute2ProviderServiceCallback;
+import android.media.MediaRoute2Info;
 import android.media.MediaRoute2ProviderInfo;
 import android.media.MediaRoute2ProviderService;
 import android.media.RouteDiscoveryPreference;
@@ -641,6 +642,15 @@
 
         @Override
         public void notifyProviderUpdated(MediaRoute2ProviderInfo providerInfo) {
+            for (MediaRoute2Info route : providerInfo.getRoutes()) {
+                if (route.isSystemRoute()) {
+                    throw new SecurityException(
+                            "Only the system is allowed to publish system routes. "
+                                    + "Disallowed route: "
+                                    + route);
+                }
+            }
+
             Connection connection = mConnectionRef.get();
             if (connection != null) {
                 connection.postProviderUpdated(providerInfo);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b2f00a2..1bdd402 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -128,12 +128,12 @@
 import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
 import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
 import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER;
+import static com.android.server.notification.Flags.expireBitmaps;
 import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_ANIM_BUFFER;
 import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
 import static com.android.server.utils.PriorityDump.PRIORITY_ARG;
 import static com.android.server.utils.PriorityDump.PRIORITY_ARG_CRITICAL;
 import static com.android.server.utils.PriorityDump.PRIORITY_ARG_NORMAL;
-import static com.android.server.notification.Flags.expireBitmaps;
 
 import android.Manifest;
 import android.Manifest.permission;
@@ -178,7 +178,6 @@
 import android.app.role.OnRoleHoldersChangedListener;
 import android.app.role.RoleManager;
 import android.app.usage.UsageEvents;
-import android.app.usage.UsageStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.companion.ICompanionDeviceManager;
 import android.compat.annotation.ChangeId;
@@ -206,8 +205,6 @@
 import android.content.pm.VersionedPackage;
 import android.content.res.Resources;
 import android.database.ContentObserver;
-import android.graphics.Bitmap;
-import android.graphics.drawable.Icon;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.AudioManagerInternal;
@@ -320,7 +317,6 @@
 import com.android.server.job.JobSchedulerInternal;
 import com.android.server.lights.LightsManager;
 import com.android.server.lights.LogicalLight;
-import com.android.server.notification.Flags;
 import com.android.server.notification.ManagedServices.ManagedServiceInfo;
 import com.android.server.notification.ManagedServices.UserProfiles;
 import com.android.server.notification.toast.CustomToastRecord;
@@ -1935,7 +1931,7 @@
             } else if (
                     isProfileUnavailable(action)) {
                 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
-                if (userHandle >= 0 && !mDpm.isKeepProfilesRunningEnabled()) {
+                if (userHandle >= 0) {
                     cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, userHandle,
                             REASON_PROFILE_TURNED_OFF);
                     mSnoozeHelper.clearData(userHandle);
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 4ed3163..d5471cb0 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -24,6 +24,7 @@
 import android.annotation.UserIdInt;
 import android.content.Context;
 import android.content.pm.PackageStats;
+import android.os.Binder;
 import android.os.Build;
 import android.os.CreateAppDataArgs;
 import android.os.CreateAppDataResult;
@@ -35,9 +36,11 @@
 import android.os.ServiceManager;
 import android.os.storage.CrateMetadata;
 import android.text.format.DateUtils;
+import android.util.EventLog;
 import android.util.Slog;
 
 import com.android.internal.os.BackgroundThread;
+import com.android.server.EventLogTags;
 import com.android.server.SystemService;
 
 import dalvik.system.BlockGuard;
@@ -441,6 +444,26 @@
         if (!checkBeforeRemote()) return;
         try {
             mInstalld.clearAppData(uuid, packageName, userId, flags, ceDataInode);
+
+            final StackTraceElement[] elements = Thread.currentThread().getStackTrace();
+            String className;
+            String methodName;
+            String fileName;
+            int lineNumber;
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            EventLog.writeEvent(EventLogTags.INSTALLER_CLEAR_APP_DATA_CALLER, pid, uid, packageName,
+                    flags);
+            // Skip the first two elements since they are always the same, ie
+            // Thread#getStackTrace() and VMStack#getThreadStackTrace()
+            for (int i = 2; i < elements.length; i++) {
+                className = elements[i].getClassName();
+                methodName = elements[i].getMethodName();
+                fileName = elements[i].getFileName();
+                lineNumber = elements[i].getLineNumber();
+                EventLog.writeEvent(EventLogTags.INSTALLER_CLEAR_APP_DATA_CALL_STACK, methodName,
+                        className, fileName, lineNumber);
+            }
         } catch (Exception e) {
             throw InstallerException.from(e);
         }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 305e353..a4d8632 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -58,6 +58,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.VersionedPackage;
+import android.content.pm.parsing.FrameworkParsingPackageUtils;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Binder;
@@ -666,17 +667,22 @@
 
         // App package name and label length is restricted so that really long strings aren't
         // written to disk.
-        if (params.appPackageName != null
-                && params.appPackageName.length() > SessionParams.MAX_PACKAGE_NAME_LENGTH) {
+        if (params.appPackageName != null && !isValidPackageName(params.appPackageName)) {
             params.appPackageName = null;
         }
 
         params.appLabel = TextUtils.trimToSize(params.appLabel,
                 PackageItemInfo.MAX_SAFE_LABEL_LENGTH);
 
-        String requestedInstallerPackageName = (params.installerPackageName != null
-                && params.installerPackageName.length() < SessionParams.MAX_PACKAGE_NAME_LENGTH)
-                ? params.installerPackageName : installerPackageName;
+        // Validate installer package name.
+        if (params.installerPackageName != null && !isValidPackageName(
+                params.installerPackageName)) {
+            params.installerPackageName = null;
+        }
+
+        var requestedInstallerPackageName =
+                params.installerPackageName != null ? params.installerPackageName
+                        : installerPackageName;
 
         if (PackageManagerServiceUtils.isRootOrShell(callingUid)
                 || PackageInstallerSession.isSystemDataLoaderInstallation(params)
@@ -1105,6 +1111,19 @@
         return Integer.parseInt(sessionId);
     }
 
+    private static boolean isValidPackageName(@NonNull String packageName) {
+        if (packageName.length() > SessionParams.MAX_PACKAGE_NAME_LENGTH) {
+            return false;
+        }
+        // "android" is a valid package name
+        var errorMessage = FrameworkParsingPackageUtils.validateName(
+                packageName, /* requireSeparator= */ false, /* requireFilename */ true);
+        if (errorMessage != null) {
+            return false;
+        }
+        return true;
+    }
+
     private File getTmpSessionDir(String volumeUuid) {
         return Environment.getDataAppDirectory(volumeUuid);
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 638bcbe..e5f7962 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2085,8 +2085,8 @@
                 mUserNeedsBadging, () -> mResolveInfo, () -> mInstantAppInstallerActivity,
                 injector.getBackgroundHandler());
         mDexOptHelper = new DexOptHelper(this);
-        mSuspendPackageHelper = new SuspendPackageHelper(this, mInjector, mUserManager,
-                mBroadcastHelper, mProtectedPackages);
+        mSuspendPackageHelper = new SuspendPackageHelper(this, mInjector, mBroadcastHelper,
+                mProtectedPackages);
         mDistractingPackageHelper = new DistractingPackageHelper(this, mBroadcastHelper,
                 mSuspendPackageHelper);
         mStorageEventHelper = new StorageEventHelper(this, mDeletePackageHelper,
@@ -4658,6 +4658,9 @@
                 throw new SecurityException("Cannot clear data for a protected package: "
                         + packageName);
             }
+            final int callingPid = Binder.getCallingPid();
+            EventLog.writeEvent(EventLogTags.PM_CLEAR_APP_DATA_CALLER, callingPid, callingUid,
+                    packageName);
 
             // Queue up an async operation since the package deletion may take a little while.
             mHandler.post(new Runnable() {
@@ -4791,6 +4794,9 @@
                     /* checkShell= */ false, "delete application cache files");
             final int hasAccessInstantApps = mContext.checkCallingOrSelfPermission(
                     android.Manifest.permission.ACCESS_INSTANT_APPS);
+            final int callingPid = Binder.getCallingPid();
+            EventLog.writeEvent(EventLogTags.PM_CLEAR_APP_DATA_CALLER, callingPid, callingUid,
+                    packageName);
 
             // Queue up an async operation since the package deletion may take a little while.
             mHandler.post(() -> {
@@ -6145,7 +6151,7 @@
             }
             return mSuspendPackageHelper.setPackagesSuspended(snapshot, packageNames, suspended,
                     appExtras, launcherExtras, dialogInfo, callingPackage, userId, callingUid,
-                    false /* forQuietMode */, quarantined);
+                    quarantined);
         }
 
         @Override
@@ -6602,12 +6608,6 @@
         }
 
         @Override
-        public void setPackagesSuspendedForQuietMode(int userId, boolean suspended) {
-            mSuspendPackageHelper.setPackagesSuspendedForQuietMode(
-                    snapshotComputer(), userId, suspended);
-        }
-
-        @Override
         public void setDeviceAndProfileOwnerPackages(
                 int deviceOwnerUserId, String deviceOwnerPackage,
                 SparseArray<String> profileOwnerPackages) {
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index e8cebef..71f6c0d 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -16,8 +16,6 @@
 
 package com.android.server.pm;
 
-import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
-import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 import static android.os.Process.SYSTEM_UID;
 
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
@@ -27,9 +25,7 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.AppOpsManager;
-import android.app.admin.DevicePolicyManagerInternal;
 import android.content.Intent;
-import android.content.pm.PackageInfo;
 import android.content.pm.SuspendDialogInfo;
 import android.os.Binder;
 import android.os.Bundle;
@@ -45,7 +41,6 @@
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
-import com.android.server.LocalServices;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.PackageUserStateInternal;
@@ -54,10 +49,8 @@
 import com.android.server.utils.WatchedArrayMap;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
-import java.util.Set;
 import java.util.function.Predicate;
 
 public final class SuspendPackageHelper {
@@ -68,7 +61,6 @@
     private final PackageManagerService mPm;
     private final PackageManagerServiceInjector mInjector;
 
-    private final UserManagerService mUserManager;
     private final BroadcastHelper mBroadcastHelper;
     private final ProtectedPackages mProtectedPackages;
 
@@ -76,10 +68,8 @@
      * Constructor for {@link PackageManagerService}.
      */
     SuspendPackageHelper(PackageManagerService pm, PackageManagerServiceInjector injector,
-            UserManagerService userManager, BroadcastHelper broadcastHelper,
-            ProtectedPackages protectedPackages) {
+            BroadcastHelper broadcastHelper, ProtectedPackages protectedPackages) {
         mPm = pm;
-        mUserManager = userManager;
         mInjector = injector;
         mBroadcastHelper = broadcastHelper;
         mProtectedPackages = protectedPackages;
@@ -102,7 +92,6 @@
      * @param callingPackage The caller's package name.
      * @param userId The user where packages reside.
      * @param callingUid The caller's uid.
-     * @param forQuietMode Whether suspension is for quiet mode, in which case no apps are exempt.
      * @return The names of failed packages.
      */
     @Nullable
@@ -110,11 +99,11 @@
             boolean suspended, @Nullable PersistableBundle appExtras,
             @Nullable PersistableBundle launcherExtras, @Nullable SuspendDialogInfo dialogInfo,
             @NonNull String callingPackage, @UserIdInt int userId, int callingUid,
-            boolean forQuietMode, boolean quarantined) {
+            boolean quarantined) {
         if (ArrayUtils.isEmpty(packageNames)) {
             return packageNames;
         }
-        if (suspended && !quarantined && !forQuietMode && !isSuspendAllowedForUser(snapshot, userId,
+        if (suspended && !quarantined && !isSuspendAllowedForUser(snapshot, userId,
                 callingUid)) {
             Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId);
             return packageNames;
@@ -130,7 +119,7 @@
         final ArraySet<String> changedPackagesList = new ArraySet<>(packageNames.length);
         final IntArray changedUids = new IntArray(packageNames.length);
 
-        final boolean[] canSuspend = suspended && !forQuietMode
+        final boolean[] canSuspend = suspended
                 ? canSuspendPackageForUser(snapshot, packageNames, userId, callingUid)
                 : null;
         for (int i = 0; i < packageNames.length; i++) {
@@ -620,92 +609,10 @@
      */
     public String[] setPackagesSuspendedByAdmin(
             Computer snapshot, int userId, String[] packageNames, boolean suspend) {
-        final Set<String> toSuspend = new ArraySet<>(packageNames);
-        List<String> unsuspendable = new ArrayList<>();
-
-        if (mUserManager.isQuietModeEnabled(userId)) {
-            // If the user is in quiet mode, most apps will already be suspended, we shouldn't
-            // re-suspend or unsuspend them.
-            final Set<String> quiet = packagesToSuspendInQuietMode(snapshot, userId);
-            quiet.retainAll(toSuspend);
-            if (!quiet.isEmpty()) {
-                Slog.i(TAG, "Ignoring quiet packages: " + String.join(", ", quiet));
-                toSuspend.removeAll(quiet);
-            }
-
-            // Some of the already suspended packages might not be suspendable by the admin
-            // (e.g. current dialer package), we need to report it back as unsuspendable the same
-            // way as if quiet mode wasn't enabled. In that latter case they'd be returned by
-            // setPackagesSuspended below after unsuccessful attempt to suspend them.
-            if (suspend) {
-                unsuspendable = getUnsuspendablePackages(snapshot, userId, quiet);
-            }
-        }
-        if (!toSuspend.isEmpty()) {
-            unsuspendable.addAll(Arrays.asList(
-                    setPackagesSuspended(
-                            snapshot, toSuspend.toArray(new String[0]), suspend,
-                            null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */,
-                            PackageManagerService.PLATFORM_PACKAGE_NAME, userId, Process.SYSTEM_UID,
-                            false /* forQuietMode */, false /* quarantined */)));
-        }
-        return unsuspendable.toArray(String[]::new);
-    }
-
-    private List<String> getUnsuspendablePackages(
-            Computer snapshot, int userId, Set<String> packages) {
-        final String[] toSuspendArray = packages.toArray(String[]::new);
-        final boolean[] mask =
-                canSuspendPackageForUser(snapshot, toSuspendArray, userId, Process.SYSTEM_UID);
-        final List<String> result = new ArrayList<>();
-        for (int i = 0; i < mask.length; i++) {
-            if (!mask[i]) {
-                result.add(toSuspendArray[i]);
-            }
-        }
-        return result;
-    }
-
-    /**
-     * Suspends or unsuspends all packages in the given user when quiet mode is toggled to prevent
-     * usage while quiet mode is enabled.
-     */
-    public void setPackagesSuspendedForQuietMode(
-            Computer snapshot, int userId, boolean suspend) {
-        final Set<String> toSuspend = packagesToSuspendInQuietMode(snapshot, userId);
-        if (!suspend) {
-            final DevicePolicyManagerInternal dpm =
-                    LocalServices.getService(DevicePolicyManagerInternal.class);
-            if (dpm != null) {
-                toSuspend.removeAll(dpm.getPackagesSuspendedByAdmin(userId));
-            } else {
-                Slog.wtf(TAG,
-                        "DevicePolicyManager unavailable while suspending apps for quiet mode");
-            }
-        }
-
-        if (toSuspend.isEmpty()) {
-            return;
-        }
-
-        setPackagesSuspended(snapshot, toSuspend.toArray(new String[0]),
-                suspend, null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */,
+        return setPackagesSuspended(snapshot, packageNames, suspend,
+                null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */,
                 PackageManagerService.PLATFORM_PACKAGE_NAME, userId, Process.SYSTEM_UID,
-                true /* forQuietMode */, false /* quarantined */);
-    }
-
-    private Set<String> packagesToSuspendInQuietMode(Computer snapshot, int userId) {
-        final List<PackageInfo> pkgInfos = snapshot.getInstalledPackages(
-                MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId).getList();
-        final Set<String> result = new ArraySet<>();
-        for (PackageInfo info : pkgInfos) {
-            result.add(info.packageName);
-        }
-
-        // Role holder may be null, but ArraySet handles it correctly.
-        result.remove(mPm.getDevicePolicyManagementRoleHolderPackageName(userId));
-
-        return result;
+                false /* quarantined */);
     }
 
     private String getKnownPackageName(@NonNull Computer snapshot,
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index c97fbda..81a570f 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -46,7 +46,6 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityManagerNative;
-import android.app.AppOpsManager;
 import android.app.BroadcastOptions;
 import android.app.IActivityManager;
 import android.app.IStopUserCallback;
@@ -55,7 +54,6 @@
 import android.app.StatsManager;
 import android.app.admin.DevicePolicyEventLogger;
 import android.app.admin.DevicePolicyManagerInternal;
-import android.app.trust.TrustManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.IIntentReceiver;
@@ -302,19 +300,6 @@
     private static final String TRON_USER_CREATED = "users_user_created";
     private static final String TRON_DEMO_CREATED = "users_demo_created";
 
-    // App ops that should be restricted in quiet mode
-    private static final int[] QUIET_MODE_RESTRICTED_APP_OPS = {
-            AppOpsManager.OP_COARSE_LOCATION,
-            AppOpsManager.OP_FINE_LOCATION,
-            AppOpsManager.OP_GPS,
-            AppOpsManager.OP_BODY_SENSORS,
-            AppOpsManager.OP_ACTIVITY_RECOGNITION,
-            AppOpsManager.OP_BLUETOOTH_SCAN,
-            AppOpsManager.OP_NEARBY_WIFI_DEVICES,
-            AppOpsManager.OP_RECORD_AUDIO,
-            AppOpsManager.OP_CAMERA,
-    };
-
     private final Context mContext;
     private final PackageManagerService mPm;
 
@@ -339,7 +324,6 @@
     private final File mUserListFile;
 
     private final IBinder mUserRestrictionToken = new Binder();
-    private final IBinder mQuietModeToken = new Binder();
 
     /** Installs system packages based on user-type. */
     private final UserSystemPackageInstaller mSystemPackageInstaller;
@@ -702,7 +686,6 @@
 
         @Override
         public void onUserStarting(@NonNull TargetUser targetUser) {
-            boolean isProfileInQuietMode = false;
             synchronized (mUms.mUsersLock) {
                 final UserData user = mUms.getUserDataLU(targetUser.getUserIdentifier());
                 if (user != null) {
@@ -710,14 +693,9 @@
                     if (targetUser.getUserIdentifier() == UserHandle.USER_SYSTEM
                             && targetUser.isFull()) {
                         mUms.setLastEnteredForegroundTimeToNow(user);
-                    } else if (user.info.isManagedProfile() && user.info.isQuietModeEnabled()) {
-                        isProfileInQuietMode = true;
                     }
                 }
             }
-            if (isProfileInQuietMode) {
-                mUms.setAppOpsRestrictedForQuietMode(targetUser.getUserIdentifier(), true);
-            }
         }
 
         @Override
@@ -1516,43 +1494,21 @@
         synchronized (mPackagesLock) {
             writeUserLP(profileUserData);
         }
-        if (getDevicePolicyManagerInternal().isKeepProfilesRunningEnabled()) {
-            // New behavior: when quiet mode is enabled, profile user is running, but apps are
-            // suspended.
-            getPackageManagerInternal().setPackagesSuspendedForQuietMode(userId, enableQuietMode);
-            setAppOpsRestrictedForQuietMode(userId, enableQuietMode);
 
-            if (enableQuietMode
-                    && !mLockPatternUtils.isManagedProfileWithUnifiedChallenge(userId)) {
-                mContext.getSystemService(TrustManager.class).setDeviceLockedForUser(userId, true);
+        try {
+            if (enableQuietMode) {
+                ActivityManager.getService().stopUser(userId, /* force= */ true, null);
+                LocalServices.getService(ActivityManagerInternal.class)
+                        .killForegroundAppsForUser(userId);
+            } else {
+                IProgressListener callback = target != null
+                        ? new DisableQuietModeUserUnlockedCallback(target)
+                        : null;
+                ActivityManager.getService().startProfileWithListener(userId, callback);
             }
-
-            if (!enableQuietMode && target != null) {
-                try {
-                    mContext.startIntentSender(target, null, 0, 0, 0);
-                } catch (IntentSender.SendIntentException e) {
-                    Slog.e(LOG_TAG, "Failed to start intent after disabling quiet mode", e);
-                }
-            }
-        } else {
-            // Old behavior: when quiet is enabled, profile user is stopped.
-            // Old quiet mode behavior: profile user is stopped.
-            // TODO(b/265683382) Remove once rollout complete.
-            try {
-                if (enableQuietMode) {
-                    ActivityManager.getService().stopUser(userId, /* force= */ true, null);
-                    LocalServices.getService(ActivityManagerInternal.class)
-                            .killForegroundAppsForUser(userId);
-                } else {
-                    IProgressListener callback = target != null
-                            ? new DisableQuietModeUserUnlockedCallback(target)
-                            : null;
-                    ActivityManager.getService().startProfileWithListener(userId, callback);
-                }
-            } catch (RemoteException e) {
-                // Should not happen, same process.
-                e.rethrowAsRuntimeException();
-            }
+        } catch (RemoteException e) {
+            // Should not happen, same process.
+            e.rethrowAsRuntimeException();
         }
 
         logQuietModeEnabled(userId, enableQuietMode, callingPackage);
@@ -1569,17 +1525,6 @@
         }
     }
 
-    private void setAppOpsRestrictedForQuietMode(@UserIdInt int userId, boolean restrict) {
-        for (int opCode : QUIET_MODE_RESTRICTED_APP_OPS) {
-            try {
-                mAppOpsService.setUserRestriction(
-                        opCode, restrict, mQuietModeToken, userId, /* excludedPackageTags= */ null);
-            } catch (RemoteException e) {
-                Slog.w(LOG_TAG, "Unable to limit app ops", e);
-            }
-        }
-    }
-
     private void logQuietModeEnabled(@UserIdInt int userId, boolean enableQuietMode,
             @Nullable String callingPackage) {
         Slogf.i(LOG_TAG,
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 2ad8bcf..9e20805 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -379,10 +379,7 @@
         ai.privateFlags |= flag(state.isInstantApp(), ApplicationInfo.PRIVATE_FLAG_INSTANT)
                 | flag(state.isVirtualPreload(), ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD)
                 | flag(state.isHidden(), ApplicationInfo.PRIVATE_FLAG_HIDDEN);
-        if ((flags & PackageManager.MATCH_QUARANTINED_COMPONENTS) == 0
-                && state.isQuarantined()) {
-            ai.enabled = false;
-        } else  if (state.getEnabledState() == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+        if (state.getEnabledState() == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
             ai.enabled = true;
         } else if (state.getEnabledState()
                 == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java
index 4ebd402..5fd787a 100644
--- a/services/core/java/com/android/server/storage/StorageSessionController.java
+++ b/services/core/java/com/android/server/storage/StorageSessionController.java
@@ -126,10 +126,10 @@
                 connection = new StorageUserConnection(mContext, userId, this);
                 mConnections.put(userId, connection);
             }
-            Slog.i(TAG, "Creating and starting session with id: " + sessionId);
-            connection.startSession(sessionId, deviceFd, vol.getPath().getPath(),
-                    vol.getInternalPath().getPath());
         }
+        Slog.i(TAG, "Creating and starting session with id: " + sessionId);
+        connection.startSession(sessionId, deviceFd, vol.getPath().getPath(),
+                vol.getInternalPath().getPath());
     }
 
     /**
diff --git a/services/core/java/com/android/server/testharness/TestHarnessModeService.java b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
index 9a9b836..1f884ba 100644
--- a/services/core/java/com/android/server/testharness/TestHarnessModeService.java
+++ b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
@@ -71,6 +71,7 @@
 public class TestHarnessModeService extends SystemService {
     public static final String TEST_HARNESS_MODE_PROPERTY = "persist.sys.test_harness";
     private static final String TAG = TestHarnessModeService.class.getSimpleName();
+    private boolean mEnableKeepMemtagMode = false;
 
     private PersistentDataBlockManagerInternal mPersistentDataBlockManagerInternal;
 
@@ -298,6 +299,18 @@
             switch (cmd) {
                 case "enable":
                 case "restore":
+                    String opt;
+                    while ((opt = getNextOption()) != null) {
+                        switch (opt) {
+                        case "--keep-memtag":
+                            mEnableKeepMemtagMode = true;
+                            break;
+                        default:
+                            getErrPrintWriter().println("Invalid option: " + opt);
+                            return 1;
+                        }
+                    }
+
                     checkPermissions();
                     final long originalId = Binder.clearCallingIdentity();
                     try {
@@ -357,6 +370,7 @@
             i.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
             i.putExtra(Intent.EXTRA_REASON, TAG);
             i.putExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, true);
+            i.putExtra("keep_memtag_mode", mEnableKeepMemtagMode);
             getContext().sendBroadcastAsUser(i, UserHandle.SYSTEM);
             return 0;
         }
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 25c42b4..f9d344b 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -285,10 +285,6 @@
             return false;
         }
 
-        if (isKeepProfilesRunningEnabled() && !isPackageSuspended()) {
-            return false;
-        }
-
         IntentSender target = createIntentSenderForOriginalIntent(mCallingUid,
                 FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT);
 
@@ -521,12 +517,6 @@
                 && (mAInfo.applicationInfo.flags & FLAG_SUSPENDED) != 0;
     }
 
-    private static boolean isKeepProfilesRunningEnabled() {
-        DevicePolicyManagerInternal dpmi =
-                LocalServices.getService(DevicePolicyManagerInternal.class);
-        return dpmi == null || dpmi.isKeepProfilesRunningEnabled();
-    }
-
     /**
      * Called when an activity is successfully launched.
      */
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 275396f..1462878 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -150,8 +150,8 @@
 
         /**
          * A unique token associated with the embedded window that can be used by the host window
-         * to request focus transfer to the embedded. This is not the input token since we don't
-         * want to give clients access to each others input token.
+         * to request focus transfer and gesture transfer to the embedded. This is not the input
+         * token since we don't want to give clients access to each others input token.
          */
         private final IBinder mInputTransferToken;
 
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index a756847..0c55d8a 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -964,6 +964,23 @@
     }
 
     @Override
+    public boolean transferHostTouchGestureToEmbedded(IWindow hostWindow,
+            IBinder inputTransferToken) {
+        if (hostWindow == null) {
+            return false;
+        }
+
+        final long identity = Binder.clearCallingIdentity();
+        boolean didTransfer;
+        try {
+            didTransfer = mService.transferHostTouchGestureToEmbedded(this, hostWindow,
+                    inputTransferToken);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+        return didTransfer;
+    }
+    @Override
     public void generateDisplayHash(IWindow window, Rect boundsInWindow, String hashAlgorithm,
             RemoteCallback callback) {
         final long origId = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9eb3389..6e3d24b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8960,6 +8960,43 @@
         }
     }
 
+    boolean transferHostTouchGestureToEmbedded(Session session, IWindow hostWindow,
+            IBinder inputTransferToken) {
+        final IBinder hostInputChannel, embeddedInputChannel;
+        synchronized (mGlobalLock) {
+            final WindowState hostWindowState = windowForClientLocked(session, hostWindow, false);
+            if (hostWindowState == null) {
+                Slog.w(TAG, "Attempt to transfer touch gesture with invalid host window");
+                return false;
+            }
+
+            final EmbeddedWindowController.EmbeddedWindow ew =
+                    mEmbeddedWindowController.getByInputTransferToken(inputTransferToken);
+            if (ew == null || ew.mHostWindowState == null) {
+                Slog.w(TAG, "Attempt to transfer touch gesture to non-existent embedded window");
+                return false;
+            }
+            if (ew.mHostWindowState.mClient.asBinder() != hostWindow.asBinder()) {
+                Slog.w(TAG, "Attempt to transfer touch gesture to embedded window not associated"
+                        + " with host window");
+                return false;
+            }
+            embeddedInputChannel = ew.getInputChannelToken();
+            if (embeddedInputChannel == null) {
+                Slog.w(TAG, "Attempt to transfer touch focus from embedded window with no input"
+                        + " channel");
+                return false;
+            }
+            hostInputChannel = hostWindowState.mInputChannelToken;
+            if (hostInputChannel == null) {
+                Slog.w(TAG,
+                        "Attempt to transfer touch focus to a host window with no input channel");
+                return false;
+            }
+            return mInputManager.transferTouchFocus(hostInputChannel, embeddedInputChannel);
+        }
+    }
+
     private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid,
             int displayId, SurfaceControl surface, String name,
             InputApplicationHandle applicationHandle, int flags,
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 176bc283..a7d7730 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -86,6 +86,8 @@
 
 namespace android {
 
+static const bool ENABLE_POINTER_CHOREOGRAPHER = input_flags::enable_pointer_choreographer();
+
 // The exponent used to calculate the pointer speed scaling factor.
 // The scaling factor is calculated as 2 ^ (speed * exponent),
 // where the speed ranges from -7 to + 7 and is supplied by the user.
@@ -327,6 +329,8 @@
     TouchAffineTransformation getTouchAffineTransformation(JNIEnv* env, jfloatArray matrixArr);
     void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override;
     bool isInputMethodConnectionActive() override;
+    std::optional<DisplayViewport> getPointerViewportForAssociatedDisplay(
+            int32_t associatedDisplayId) override;
 
     /* --- InputDispatcherPolicyInterface implementation --- */
 
@@ -374,8 +378,10 @@
     virtual PointerIconStyle getCustomPointerIconId();
     virtual void onPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position);
 
-    /* --- PointerControllerPolicyInterface implementation --- */
-    std::shared_ptr<PointerControllerInterface> createPointerController() override;
+    /* --- PointerChoreographerPolicyInterface implementation --- */
+    std::shared_ptr<PointerControllerInterface> createPointerController(
+            PointerControllerInterface::ControllerType type) override;
+    void notifyPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position) override;
 
 private:
     sp<InputManagerInterface> mInputManager;
@@ -492,7 +498,9 @@
         dump += StringPrintf(INDENT "Pointer Capture: %s, seq=%" PRIu32 "\n",
                              mLocked.pointerCaptureRequest.enable ? "Enabled" : "Disabled",
                              mLocked.pointerCaptureRequest.seq);
-        forEachPointerControllerLocked([&dump](PointerController& pc) { dump += pc.dump(); });
+        if (auto pc = mLocked.legacyPointerController.lock(); pc) {
+            dump += pc->dump();
+        }
     } // release lock
     dump += "\n";
 
@@ -537,6 +545,9 @@
                 [&viewports](PointerController& pc) { pc.onDisplayViewportsUpdated(viewports); });
     } // release lock
 
+    if (ENABLE_POINTER_CHOREOGRAPHER) {
+        mInputManager->getChoreographer().setDisplayViewports(viewports);
+    }
     mInputManager->getReader().requestRefreshConfiguration(
             InputReaderConfiguration::Change::DISPLAY_INFO);
 }
@@ -721,6 +732,7 @@
             continue;
         }
         apply(*pc);
+        it++;
     }
 }
 
@@ -735,9 +747,6 @@
     if (controller == nullptr) {
         ensureSpriteControllerLocked();
 
-        static const bool ENABLE_POINTER_CHOREOGRAPHER =
-                input_flags::enable_pointer_choreographer();
-
         // Disable the functionality of the legacy PointerController if PointerChoreographer is
         // enabled.
         controller = PointerController::create(this, mLooper, *mLocked.spriteController,
@@ -749,17 +758,43 @@
     return controller;
 }
 
-std::shared_ptr<PointerControllerInterface> NativeInputManager::createPointerController() {
+std::shared_ptr<PointerControllerInterface> NativeInputManager::createPointerController(
+        PointerControllerInterface::ControllerType type) {
     std::scoped_lock _l(mLock);
     ensureSpriteControllerLocked();
     std::shared_ptr<PointerController> pc =
-            PointerController::create(this, mLooper, *mLocked.spriteController, /*enabled=*/true);
+            PointerController::create(this, mLooper, *mLocked.spriteController, /*enabled=*/true,
+                                      type);
     mLocked.pointerControllers.emplace_back(pc);
     return pc;
 }
 
 void NativeInputManager::onPointerDisplayIdChanged(int32_t pointerDisplayId,
                                                    const FloatPoint& position) {
+    if (ENABLE_POINTER_CHOREOGRAPHER) {
+        return;
+    }
+    JNIEnv* env = jniEnv();
+    env->CallVoidMethod(mServiceObj, gServiceClassInfo.onPointerDisplayIdChanged, pointerDisplayId,
+                        position.x, position.y);
+    checkAndClearExceptionFromCallback(env, "onPointerDisplayIdChanged");
+}
+
+void NativeInputManager::notifyPointerDisplayIdChanged(int32_t pointerDisplayId,
+                                                       const FloatPoint& position) {
+    // Notify the Reader so that devices can be reconfigured.
+    { // acquire lock
+        std::scoped_lock _l(mLock);
+        if (mLocked.pointerDisplayId == pointerDisplayId) {
+            return;
+        }
+        mLocked.pointerDisplayId = pointerDisplayId;
+        ALOGI("%s: pointer displayId set to: %d", __func__, pointerDisplayId);
+    } // release lock
+    mInputManager->getReader().requestRefreshConfiguration(
+            InputReaderConfiguration::Change::DISPLAY_INFO);
+
+    // Notify the system.
     JNIEnv* env = jniEnv();
     env->CallVoidMethod(mServiceObj, gServiceClassInfo.onPointerDisplayIdChanged, pointerDisplayId,
                         position.x, position.y);
@@ -1118,19 +1153,23 @@
 }
 
 void NativeInputManager::setPointerDisplayId(int32_t displayId) {
-    { // acquire lock
-        std::scoped_lock _l(mLock);
+    if (ENABLE_POINTER_CHOREOGRAPHER) {
+        mInputManager->getChoreographer().setDefaultMouseDisplayId(displayId);
+    } else {
+        { // acquire lock
+            std::scoped_lock _l(mLock);
 
-        if (mLocked.pointerDisplayId == displayId) {
-            return;
-        }
+            if (mLocked.pointerDisplayId == displayId) {
+                return;
+            }
 
-        ALOGI("Setting pointer display id to %d.", displayId);
-        mLocked.pointerDisplayId = displayId;
-    } // release lock
+            ALOGI("Setting pointer display id to %d.", displayId);
+            mLocked.pointerDisplayId = displayId;
+        } // release lock
 
-    mInputManager->getReader().requestRefreshConfiguration(
-            InputReaderConfiguration::Change::DISPLAY_INFO);
+        mInputManager->getReader().requestRefreshConfiguration(
+                InputReaderConfiguration::Change::DISPLAY_INFO);
+    }
 }
 
 void NativeInputManager::setPointerSpeed(int32_t speed) {
@@ -1356,6 +1395,11 @@
     return result;
 }
 
+std::optional<DisplayViewport> NativeInputManager::getPointerViewportForAssociatedDisplay(
+        int32_t associatedDisplayId) {
+    return mInputManager->getChoreographer().getViewportForPointerDevice(associatedDisplayId);
+}
+
 bool NativeInputManager::filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) {
     ATRACE_CALL();
     JNIEnv* env = jniEnv();
@@ -1689,6 +1733,9 @@
 }
 
 FloatPoint NativeInputManager::getMouseCursorPosition() {
+    if (ENABLE_POINTER_CHOREOGRAPHER) {
+        return mInputManager->getChoreographer().getMouseCursorPosition(ADISPLAY_ID_NONE);
+    }
     std::scoped_lock _l(mLock);
     const auto pc = mLocked.legacyPointerController.lock();
     if (!pc) return {AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION};
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
index d960439..395ea91 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
@@ -16,8 +16,6 @@
 
 package com.android.server.devicepolicy;
 
-import static com.android.server.devicepolicy.DevicePolicyManagerService.DEFAULT_KEEP_PROFILES_RUNNING_FLAG;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -200,7 +198,7 @@
      * Effective state of the feature flag. It is updated to the current configuration value
      * during boot and doesn't change value after than unless overridden by test code.
      */
-    boolean mEffectiveKeepProfilesRunning = DEFAULT_KEEP_PROFILES_RUNNING_FLAG;
+    boolean mEffectiveKeepProfilesRunning = false;
 
     DevicePolicyData(@UserIdInt int userId) {
         mUserId = userId;
@@ -401,7 +399,7 @@
                 out.endTag(null, TAG_BYPASS_ROLE_QUALIFICATIONS);
             }
 
-            if (policyData.mEffectiveKeepProfilesRunning != DEFAULT_KEEP_PROFILES_RUNNING_FLAG) {
+            if (policyData.mEffectiveKeepProfilesRunning) {
                 out.startTag(null, TAG_KEEP_PROFILES_RUNNING);
                 out.attributeBoolean(null, ATTR_VALUE, policyData.mEffectiveKeepProfilesRunning);
                 out.endTag(null, TAG_KEEP_PROFILES_RUNNING);
@@ -592,7 +590,7 @@
                     policy.mCurrentRoleHolder = parser.getAttributeValue(null, ATTR_VALUE);
                 } else if (TAG_KEEP_PROFILES_RUNNING.equals(tag)) {
                     policy.mEffectiveKeepProfilesRunning = parser.getAttributeBoolean(
-                            null, ATTR_VALUE, DEFAULT_KEEP_PROFILES_RUNNING_FLAG);
+                            null, ATTR_VALUE, false);
                 // Deprecated tags below
                 } else if (TAG_PROTECTED_PACKAGES.equals(tag)) {
                     if (policy.mUserControlDisabledPackages == null) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 1ff117e..93dc219 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -873,9 +873,6 @@
             "enable_permission_based_access";
     private static final boolean DEFAULT_VALUE_PERMISSION_BASED_ACCESS_FLAG = false;
 
-    // TODO(b/265683382) remove the flag after rollout.
-    public static final boolean DEFAULT_KEEP_PROFILES_RUNNING_FLAG = false;
-
     // TODO(b/266831522) remove the flag after rollout.
     private static final String APPLICATION_EXEMPTIONS_FLAG = "application_exemptions";
     private static final boolean DEFAULT_APPLICATION_EXEMPTIONS_FLAG = true;
@@ -2178,13 +2175,29 @@
         return packageNameAndSignature;
     }
 
-    private void suspendAppsForQuietProfiles(boolean toSuspend) {
+    private void unsuspendAppsForQuietProfiles() {
         PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
         List<UserInfo> users = mUserManagerInternal.getUsers(true /* excludeDying */);
+
         for (UserInfo user : users) {
-            if (user.isManagedProfile() && user.isQuietModeEnabled()) {
-                pmi.setPackagesSuspendedForQuietMode(user.id, toSuspend);
+            if (!user.isManagedProfile() || !user.isQuietModeEnabled()) {
+                continue;
             }
+            int userId = user.id;
+            var suspendedByAdmin = getPackagesSuspendedByAdmin(userId);
+            var packagesToUnsuspend = mInjector.getPackageManager(userId)
+                    .getInstalledPackages(PackageManager.PackageInfoFlags.of(
+                            MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE))
+                    .stream()
+                    .map(packageInfo -> packageInfo.packageName)
+                    .filter(pkg -> !suspendedByAdmin.contains(pkg))
+                    .toArray(String[]::new);
+
+            Slogf.i(LOG_TAG, "Unsuspending work apps for user %d", userId);
+            // When app suspension was used for quiet mode, the apps were suspended by platform
+            // package, just like when admin suspends them. So although it wasn't admin who
+            // suspended, this method will remove the right suspension record.
+            pmi.setPackagesSuspendedByAdmin(userId, packagesToUnsuspend, false /* suspended */);
         }
     }
 
@@ -3436,9 +3449,9 @@
             }
         }
 
-        // In case flag value has changed, we apply it during boot to avoid doing it concurrently
-        // with user toggling quiet mode.
-        setKeepProfileRunningEnabledUnchecked(isKeepProfilesRunningFlagEnabled());
+        // Check whether work apps were paused via suspension and unsuspend if necessary.
+        // TODO: move it into PolicyVersionUpgrader so that it is executed only once.
+        unsuspendWorkAppsIfNecessary();
     }
 
     // TODO(b/230841522) Make it static.
@@ -11039,9 +11052,6 @@
                             (size == 1 ? "" : "s"));
                 }
                 pw.println();
-                pw.println("Keep profiles running: "
-                        + getUserData(UserHandle.USER_SYSTEM).mEffectiveKeepProfilesRunning);
-                pw.println();
 
                 mPolicyCache.dump(pw);
                 pw.println();
@@ -15539,11 +15549,6 @@
         }
 
         @Override
-        public Set<String> getPackagesSuspendedByAdmin(@UserIdInt int userId) {
-            return DevicePolicyManagerService.this.getPackagesSuspendedByAdmin(userId);
-        }
-
-        @Override
         public void notifyUnsafeOperationStateChanged(DevicePolicySafetyChecker checker, int reason,
                 boolean isSafe) {
             // TODO(b/178494483): use EventLog instead
@@ -15571,11 +15576,6 @@
             }
         }
 
-        @Override
-        public boolean isKeepProfilesRunningEnabled() {
-            return getUserDataUnchecked(UserHandle.USER_SYSTEM).mEffectiveKeepProfilesRunning;
-        }
-
         private @Mode int findInteractAcrossProfilesResetMode(String packageName) {
             return getDefaultCrossProfilePackages().contains(packageName)
                     ? AppOpsManager.MODE_ALLOWED
@@ -23028,32 +23028,22 @@
                 DEFAULT_VALUE_PERMISSION_BASED_ACCESS_FLAG);
     }
 
-    private static boolean isKeepProfilesRunningFlagEnabled() {
-        return DEFAULT_KEEP_PROFILES_RUNNING_FLAG;
-    }
-
     private boolean isUnicornFlagEnabled() {
         return false;
     }
 
-    private void setKeepProfileRunningEnabledUnchecked(boolean keepProfileRunning) {
+    private void unsuspendWorkAppsIfNecessary() {
         synchronized (getLockObject()) {
             DevicePolicyData policyData = getUserDataUnchecked(UserHandle.USER_SYSTEM);
-            if (policyData.mEffectiveKeepProfilesRunning == keepProfileRunning) {
+            if (!policyData.mEffectiveKeepProfilesRunning) {
                 return;
             }
-            policyData.mEffectiveKeepProfilesRunning = keepProfileRunning;
+            policyData.mEffectiveKeepProfilesRunning = false;
             saveSettingsLocked(UserHandle.USER_SYSTEM);
         }
-        suspendAppsForQuietProfiles(keepProfileRunning);
-    }
 
-    @Override
-    public void setOverrideKeepProfilesRunning(boolean enabled) {
-        Preconditions.checkCallAuthorization(
-                hasCallingOrSelfPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS));
-        setKeepProfileRunningEnabledUnchecked(enabled);
-        Slog.i(LOG_TAG, "Keep profiles running overridden to: " + enabled);
+        Slog.w(LOG_TAG, "Work apps may have been paused via suspension previously.");
+        unsuspendAppsForQuietProfiles();
     }
 
     public void setMtePolicy(int flags, String callerPackageName) {
diff --git a/services/midi/Android.bp b/services/midi/Android.bp
index a385fe3..2ea28a31 100644
--- a/services/midi/Android.bp
+++ b/services/midi/Android.bp
@@ -20,6 +20,5 @@
     srcs: [":services.midi-sources"],
     libs: [
         "services.core",
-        "aconfig_midi_flags_java_lib",
     ],
 }
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index 2f47cc7..39aaab2 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -16,7 +16,7 @@
 
 package com.android.server.midi;
 
-import static com.android.media.midi.flags.Flags.virtualUmp;
+import static android.media.midi.Flags.virtualUmp;
 
 import android.Manifest;
 import android.annotation.NonNull;
diff --git a/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt b/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt
index 93530cf..acaec21 100644
--- a/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt
@@ -16,7 +16,6 @@
 
 package com.android.server.permission.access
 
-import android.app.admin.DevicePolicyManagerInternal
 import android.content.Context
 import android.content.pm.PackageManager
 import android.content.pm.PackageManagerInternal
@@ -75,7 +74,7 @@
 
         val userIds = MutableIntSet(userManagerService.userIdsIncludingPreCreated)
         val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates
-        val knownPackages = packageManagerInternal.getKnownPackages(packageStates)
+        val knownPackages = packageManagerInternal.knownPackages
         val isLeanback = systemConfig.isLeanback
         val configPermissions = systemConfig.permissions
         val privilegedPermissionAllowlistPackages =
@@ -152,7 +151,7 @@
         isSystemUpdated: Boolean
     ) {
         val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates
-        val knownPackages = packageManagerInternal.getKnownPackages(packageStates)
+        val knownPackages = packageManagerInternal.knownPackages
         mutateState {
             with(policy) {
                 onStorageVolumeMounted(
@@ -169,7 +168,7 @@
 
     internal fun onPackageAdded(packageName: String) {
         val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates
-        val knownPackages = packageManagerInternal.getKnownPackages(packageStates)
+        val knownPackages = packageManagerInternal.knownPackages
         mutateState {
             with(policy) {
                 onPackageAdded(
@@ -184,7 +183,7 @@
 
     internal fun onPackageRemoved(packageName: String, appId: Int) {
         val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates
-        val knownPackages = packageManagerInternal.getKnownPackages(packageStates)
+        val knownPackages = packageManagerInternal.knownPackages
         mutateState {
             with(policy) {
                 onPackageRemoved(
@@ -200,7 +199,7 @@
 
     internal fun onPackageInstalled(packageName: String, userId: Int) {
         val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates
-        val knownPackages = packageManagerInternal.getKnownPackages(packageStates)
+        val knownPackages = packageManagerInternal.knownPackages
         mutateState {
             with(policy) {
                 onPackageInstalled(
@@ -216,7 +215,7 @@
 
     internal fun onPackageUninstalled(packageName: String, appId: Int, userId: Int) {
         val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates
-        val knownPackages = packageManagerInternal.getKnownPackages(packageStates)
+        val knownPackages = packageManagerInternal.knownPackages
         mutateState {
             with(policy) {
                 onPackageUninstalled(
@@ -232,69 +231,50 @@
     }
 
     internal fun onSystemReady() {
-        val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates
-        val knownPackages = packageManagerInternal.getKnownPackages(packageStates)
-        mutateState {
-            with(policy) {
-                onSystemReady(packageStates, disabledSystemPackageStates, knownPackages)
-            }
-        }
+        mutateState { with(policy) { onSystemReady() } }
     }
 
     private val PackageManagerLocal.allPackageStates:
         Pair<Map<String, PackageState>, Map<String, PackageState>>
         get() = withUnfilteredSnapshot().use { it.packageStates to it.disabledSystemPackageStates }
 
-    private fun PackageManagerInternal.getKnownPackages(
-        packageStates: Map<String, PackageState>
-    ): IntMap<Array<String>> =
-        MutableIntMap<Array<String>>().apply {
-            this[KnownPackages.PACKAGE_INSTALLER] =
-                getKnownPackageNames(KnownPackages.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM)
-            this[KnownPackages.PACKAGE_PERMISSION_CONTROLLER] =
-                getKnownPackageNames(
-                    KnownPackages.PACKAGE_PERMISSION_CONTROLLER,
-                    UserHandle.USER_SYSTEM
+    private val PackageManagerInternal.knownPackages: IntMap<Array<String>>
+        get() =
+            MutableIntMap<Array<String>>().apply {
+                this[KnownPackages.PACKAGE_INSTALLER] = getKnownPackageNames(
+                    KnownPackages.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM
                 )
-            this[KnownPackages.PACKAGE_VERIFIER] =
-                getKnownPackageNames(KnownPackages.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM)
-            this[KnownPackages.PACKAGE_SETUP_WIZARD] =
-                getKnownPackageNames(KnownPackages.PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM)
-            this[KnownPackages.PACKAGE_SYSTEM_TEXT_CLASSIFIER] =
-                getKnownPackageNames(
-                    KnownPackages.PACKAGE_SYSTEM_TEXT_CLASSIFIER,
-                    UserHandle.USER_SYSTEM
+                this[KnownPackages.PACKAGE_PERMISSION_CONTROLLER] = getKnownPackageNames(
+                    KnownPackages.PACKAGE_PERMISSION_CONTROLLER, UserHandle.USER_SYSTEM
                 )
-            this[KnownPackages.PACKAGE_CONFIGURATOR] =
-                getKnownPackageNames(KnownPackages.PACKAGE_CONFIGURATOR, UserHandle.USER_SYSTEM)
-            this[KnownPackages.PACKAGE_INCIDENT_REPORT_APPROVER] =
-                getKnownPackageNames(
-                    KnownPackages.PACKAGE_INCIDENT_REPORT_APPROVER,
-                    UserHandle.USER_SYSTEM
+                this[KnownPackages.PACKAGE_VERIFIER] = getKnownPackageNames(
+                    KnownPackages.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM
                 )
-            this[KnownPackages.PACKAGE_APP_PREDICTOR] =
-                getKnownPackageNames(KnownPackages.PACKAGE_APP_PREDICTOR, UserHandle.USER_SYSTEM)
-            this[KnownPackages.PACKAGE_COMPANION] =
-                getKnownPackageNames(KnownPackages.PACKAGE_COMPANION, UserHandle.USER_SYSTEM)
-            this[KnownPackages.PACKAGE_RETAIL_DEMO] =
-                getKnownPackageNames(KnownPackages.PACKAGE_RETAIL_DEMO, UserHandle.USER_SYSTEM)
-                    .filter { isProfileOwner(it, packageStates) }
-                    .toTypedArray()
-            this[KnownPackages.PACKAGE_RECENTS] =
-                getKnownPackageNames(KnownPackages.PACKAGE_RECENTS, UserHandle.USER_SYSTEM)
-        }
-
-    private fun isProfileOwner(
-        packageName: String,
-        packageStates: Map<String, PackageState>
-    ): Boolean {
-        val appId = packageStates[packageName]?.appId ?: return false
-        val devicePolicyManagerInternal =
-            LocalServices.getService(DevicePolicyManagerInternal::class.java) ?: return false
-        // TODO(b/169395065): Figure out if this flow makes sense in Device Owner mode.
-        return devicePolicyManagerInternal.isActiveProfileOwner(appId) ||
-            devicePolicyManagerInternal.isActiveDeviceOwner(appId)
-    }
+                this[KnownPackages.PACKAGE_SETUP_WIZARD] = getKnownPackageNames(
+                    KnownPackages.PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM
+                )
+                this[KnownPackages.PACKAGE_SYSTEM_TEXT_CLASSIFIER] = getKnownPackageNames(
+                    KnownPackages.PACKAGE_SYSTEM_TEXT_CLASSIFIER, UserHandle.USER_SYSTEM
+                )
+                this[KnownPackages.PACKAGE_CONFIGURATOR] = getKnownPackageNames(
+                    KnownPackages.PACKAGE_CONFIGURATOR, UserHandle.USER_SYSTEM
+                )
+                this[KnownPackages.PACKAGE_INCIDENT_REPORT_APPROVER] = getKnownPackageNames(
+                    KnownPackages.PACKAGE_INCIDENT_REPORT_APPROVER, UserHandle.USER_SYSTEM
+                )
+                this[KnownPackages.PACKAGE_APP_PREDICTOR] = getKnownPackageNames(
+                    KnownPackages.PACKAGE_APP_PREDICTOR, UserHandle.USER_SYSTEM
+                )
+                this[KnownPackages.PACKAGE_COMPANION] = getKnownPackageNames(
+                    KnownPackages.PACKAGE_COMPANION, UserHandle.USER_SYSTEM
+                )
+                this[KnownPackages.PACKAGE_RETAIL_DEMO] = getKnownPackageNames(
+                    KnownPackages.PACKAGE_RETAIL_DEMO, UserHandle.USER_SYSTEM
+                )
+                this[KnownPackages.PACKAGE_RECENTS] = getKnownPackageNames(
+                    KnownPackages.PACKAGE_RECENTS, UserHandle.USER_SYSTEM
+                )
+            }
 
     @OptIn(ExperimentalContracts::class)
     internal inline fun <T> getState(action: GetStateScope.() -> T): T {
diff --git a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
index 754f77ec..29fe95c 100644
--- a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
@@ -262,17 +262,8 @@
         forEachSchemePolicy { with(it) { onPackageUninstalled(packageName, appId, userId) } }
     }
 
-    fun MutateStateScope.onSystemReady(
-        packageStates: Map<String, PackageState>,
-        disabledSystemPackageStates: Map<String, PackageState>,
-        knownPackages: IntMap<Array<String>>
-    ) {
-        newState.mutateExternalState().apply {
-            setPackageStates(packageStates)
-            setDisabledSystemPackageStates(disabledSystemPackageStates)
-            setKnownPackages(knownPackages)
-            setSystemReady(true)
-        }
+    fun MutateStateScope.onSystemReady() {
+        newState.mutateExternalState().setSystemReady(true)
         forEachSchemePolicy { with(it) { onSystemReady() } }
     }
 
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index 08ba753..010604f 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -1448,15 +1448,6 @@
             // Special permissions for the system companion device manager.
             return true
         }
-        if (
-            permission.isRetailDemo &&
-                packageName in knownPackages[KnownPackages.PACKAGE_RETAIL_DEMO]!!
-        ) {
-            // Special permission granted only to the OEM specified retail demo app.
-            // Note that the original code was passing app ID as UID, so this behavior is kept
-            // unchanged.
-            return true
-        }
         if (permission.isRecents && packageName in knownPackages[KnownPackages.PACKAGE_RECENTS]!!) {
             // Special permission for the recents app.
             return true
@@ -1511,27 +1502,6 @@
     }
 
     override fun MutateStateScope.onSystemReady() {
-        // HACK: PACKAGE_USAGE_STATS is the only permission with the retailDemo protection flag,
-        // and we have to wait until DevicePolicyManagerService is started to know whether the
-        // retail demo package is a profile owner so that it can have the permission.
-        // Since there's no simple callback for profile owner change, and we are deprecating and
-        // removing the retailDemo protection flag in favor of a proper role soon, we can just
-        // re-evaluate the permission here, which is also how the old implementation has been
-        // working.
-        // TODO: Partially revert ag/22690114 once we can remove support for the retailDemo
-        //  protection flag.
-        val externalState = newState.externalState
-        for (packageName in externalState.knownPackages[KnownPackages.PACKAGE_RETAIL_DEMO]!!) {
-            val appId = externalState.packageStates[packageName]?.appId ?: continue
-            newState.userStates.forEachIndexed { _, userId, _ ->
-                evaluatePermissionState(
-                    appId,
-                    userId,
-                    Manifest.permission.PACKAGE_USAGE_STATS,
-                    null
-                )
-            }
-        }
         if (!privilegedPermissionAllowlistViolations.isEmpty()) {
             throw IllegalStateException(
                 "Signature|privileged permissions not in privileged" +
diff --git a/services/tests/mockingservicestests/src/com/android/server/MasterClearReceiverTest.java b/services/tests/mockingservicestests/src/com/android/server/MasterClearReceiverTest.java
index cc97b8f..76a1c3c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/MasterClearReceiverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/MasterClearReceiverTest.java
@@ -154,10 +154,11 @@
         intent.putExtra(Intent.EXTRA_REASON, "Self destruct");
         intent.putExtra(Intent.EXTRA_FORCE_FACTORY_RESET, true);
         intent.putExtra(Intent.EXTRA_WIPE_ESIMS, true);
+        intent.putExtra("keep_memtag_mode", true);
         mReceiver.onReceive(mContext, intent);
 
         verifyRebootWipeUserData(/* shutdown= */ true, /* reason= */ "Self destruct",
-                /* force= */ true, /* wipeEuicc= */ true);
+                /* force= */ true, /* wipeEuicc= */ true, /* keepMemtagMode= */ true);
         verifyWipeExternalData();
     }
 
@@ -211,7 +212,7 @@
             mRebootWipeUserDataLatch.countDown();
             return null;
         }).when(() -> RecoverySystem
-                .rebootWipeUserData(any(), anyBoolean(), any(), anyBoolean(), anyBoolean()));
+                .rebootWipeUserData(any(), anyBoolean(), any(), anyBoolean(), anyBoolean(), anyBoolean()));
     }
 
     private void expectWipeExternalData() {
@@ -244,11 +245,16 @@
 
     private void verifyRebootWipeUserData(boolean shutdown, String reason, boolean force,
             boolean wipeEuicc) throws Exception {
+        verifyRebootWipeUserData(shutdown, reason, force, wipeEuicc, /* keepMemtagMode= */ false);
+    }
+
+    private void verifyRebootWipeUserData(boolean shutdown, String reason, boolean force,
+            boolean wipeEuicc, boolean keepMemtagMode) throws Exception {
         boolean called = mRebootWipeUserDataLatch.await(5, TimeUnit.SECONDS);
         assertWithMessage("rebootWipeUserData not called in 5s").that(called).isTrue();
 
         verify(()-> RecoverySystem.rebootWipeUserData(same(mContext), eq(shutdown), eq(reason),
-                eq(force), eq(wipeEuicc)));
+                eq(force), eq(wipeEuicc), eq(keepMemtagMode)));
     }
 
     private void verifyNoRebootWipeUserData() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java b/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java
index 4ffa0fb..5f9a17c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java
@@ -88,7 +88,7 @@
             Log.d(TAG, "Mocking " + inv);
             return null;
         }).when(() -> RecoverySystem.rebootWipeUserData(any(), anyBoolean(), any(),
-                anyBoolean(), anyBoolean()));
+                anyBoolean(), anyBoolean(), anyBoolean()));
     }
 
     @After
@@ -270,17 +270,20 @@
 
     private void verifyRebootWipeUserDataMinimumArgsCalled() {
         verify(() -> RecoverySystem.rebootWipeUserData(mContext, /* shutdown= */ false,
-                /* reason= */ null, /* force= */ false, /* wipeEuicc= */ false));
+                /* reason= */ null, /* force= */ false, /* wipeEuicc= */ false,
+                /* keepMemtagMode= */ false));
     }
 
     private void verifyRebootWipeUserDataMinimumArgsButForceCalled() {
         verify(() -> RecoverySystem.rebootWipeUserData(mContext, /* shutdown= */ false,
-                /* reason= */ null, /* force= */ true, /* wipeEuicc= */ false));
+                /* reason= */ null, /* force= */ true, /* wipeEuicc= */ false,
+                /* keepMemtagMode= */ false));
     }
 
     private void verifyRebootWipeUserDataAllArgsCalled() {
         verify(() -> RecoverySystem.rebootWipeUserData(mContext, /* shutdown= */ true,
-                /* reason= */ REASON, /* force= */ true, /* wipeEuicc= */ true));
+                /* reason= */ REASON, /* force= */ true, /* wipeEuicc= */ true,
+                /* keepMemtagMode= */ false));
     }
 
     private void verifyWipeAdoptableStorageNotCalled() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index dd23d9f..e5291d3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -577,8 +577,14 @@
         JobStatus jobUIDT = createJobStatus("testGetMaxJobExecutionTimeMs",
                 createJobInfo(10)
                         .setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY));
+        JobStatus jobEj = createJobStatus("testGetMaxJobExecutionTimeMs",
+                createJobInfo(2).setExpedited(true));
+        JobStatus jobReg = createJobStatus("testGetMaxJobExecutionTimeMs",
+                createJobInfo(3));
         spyOn(jobUIDT);
         when(jobUIDT.shouldTreatAsUserInitiatedJob()).thenReturn(true);
+        spyOn(jobEj);
+        when(jobEj.shouldTreatAsExpeditedJob()).thenReturn(true);
 
         QuotaController quotaController = mService.getQuotaController();
         spyOn(quotaController);
@@ -595,6 +601,11 @@
         grantRunUserInitiatedJobsPermission(false);
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobUIDT));
+
+        assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
+                mService.getMaxJobExecutionTimeMs(jobEj));
+        assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+                mService.getMaxJobExecutionTimeMs(jobReg));
     }
 
     @Test
@@ -636,7 +647,7 @@
         grantRunUserInitiatedJobsPermission(false);
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobUij));
-        assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+        assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
                 mService.getMaxJobExecutionTimeMs(jobEj));
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobReg));
@@ -649,7 +660,7 @@
         grantRunUserInitiatedJobsPermission(false);
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobUij));
-        assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+        assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
                 mService.getMaxJobExecutionTimeMs(jobEj));
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobReg));
@@ -664,7 +675,7 @@
         grantRunUserInitiatedJobsPermission(false);
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobUij));
-        assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+        assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
                 mService.getMaxJobExecutionTimeMs(jobEj));
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobReg));
@@ -677,7 +688,7 @@
         grantRunUserInitiatedJobsPermission(false);
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobUij));
-        assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+        assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
                 mService.getMaxJobExecutionTimeMs(jobEj));
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobReg));
@@ -692,7 +703,7 @@
         grantRunUserInitiatedJobsPermission(false);
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobUij));
-        assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+        assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
                 mService.getMaxJobExecutionTimeMs(jobEj));
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobReg));
@@ -705,7 +716,7 @@
         grantRunUserInitiatedJobsPermission(false);
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobUij));
-        assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+        assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
                 mService.getMaxJobExecutionTimeMs(jobEj));
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobReg));
@@ -720,7 +731,7 @@
         grantRunUserInitiatedJobsPermission(false);
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobUij));
-        assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+        assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
                 mService.getMaxJobExecutionTimeMs(jobEj));
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobReg));
@@ -765,7 +776,7 @@
         grantRunUserInitiatedJobsPermission(false);
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobUij));
-        assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+        assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
                 mService.getMaxJobExecutionTimeMs(jobEj));
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobReg));
@@ -778,7 +789,7 @@
         grantRunUserInitiatedJobsPermission(false);
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobUij));
-        assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+        assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
                 mService.getMaxJobExecutionTimeMs(jobEj));
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobReg));
@@ -792,7 +803,7 @@
         grantRunUserInitiatedJobsPermission(false);
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobUij));
-        assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+        assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
                 mService.getMaxJobExecutionTimeMs(jobEj));
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobReg));
@@ -807,7 +818,7 @@
         grantRunUserInitiatedJobsPermission(false);
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobUij));
-        assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+        assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
                 mService.getMaxJobExecutionTimeMs(jobEj));
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobReg));
@@ -820,7 +831,7 @@
         grantRunUserInitiatedJobsPermission(false);
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobUij));
-        assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+        assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
                 mService.getMaxJobExecutionTimeMs(jobEj));
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobReg));
@@ -834,7 +845,7 @@
         grantRunUserInitiatedJobsPermission(false);
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobUij));
-        assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+        assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
                 mService.getMaxJobExecutionTimeMs(jobEj));
         assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                 mService.getMaxJobExecutionTimeMs(jobReg));
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/idle/DeviceIdlenessTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/idle/DeviceIdlenessTrackerTest.java
new file mode 100644
index 0000000..09935f2
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/idle/DeviceIdlenessTrackerTest.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.server.job.controllers.idle;
+
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+import static com.android.server.job.controllers.idle.DeviceIdlenessTracker.KEY_INACTIVITY_IDLE_THRESHOLD_MS;
+import static com.android.server.job.controllers.idle.DeviceIdlenessTracker.KEY_INACTIVITY_STABLE_POWER_IDLE_THRESHOLD_MS;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.app.AlarmManager;
+import android.app.UiModeManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.provider.DeviceConfig;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.AppSchedulingModuleThread;
+import com.android.server.LocalServices;
+import com.android.server.job.JobSchedulerService;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.time.Clock;
+import java.time.Duration;
+import java.time.ZoneOffset;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceIdlenessTrackerTest {
+    private DeviceIdlenessTracker mDeviceIdlenessTracker;
+    private JobSchedulerService.Constants mConstants = new JobSchedulerService.Constants();
+    private BroadcastReceiver mBroadcastReceiver;
+    private DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder =
+            new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER);;
+
+    private MockitoSession mMockingSession;
+    @Mock
+    private AlarmManager mAlarmManager;
+    @Mock
+    private Context mContext;
+    @Mock
+    private JobSchedulerService mJobSchedulerService;
+    @Mock
+    private PowerManager mPowerManager;
+    @Mock
+    private Resources mResources;
+
+    @Before
+    public void setUp() {
+        mMockingSession = mockitoSession()
+                .initMocks(this)
+                .strictness(Strictness.LENIENT)
+                .spyStatic(DeviceConfig.class)
+                .mockStatic(LocalServices.class)
+                .startMocking();
+
+        // Called in StateController constructor.
+        when(mJobSchedulerService.getTestableContext()).thenReturn(mContext);
+        when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService);
+        when(mJobSchedulerService.getConstants()).thenReturn(mConstants);
+        // Called in DeviceIdlenessTracker.startTracking.
+        when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager);
+        when(mContext.getSystemService(UiModeManager.class)).thenReturn(mock(UiModeManager.class));
+        when(mContext.getResources()).thenReturn(mResources);
+        doReturn((int) (31 * MINUTE_IN_MILLIS)).when(mResources).getInteger(
+                com.android.internal.R.integer.config_jobSchedulerInactivityIdleThreshold);
+        doReturn((int) (17 * MINUTE_IN_MILLIS)).when(mResources).getInteger(
+                com.android.internal.R.integer
+                        .config_jobSchedulerInactivityIdleThresholdOnStablePower);
+        doReturn(mPowerManager).when(() -> LocalServices.getService(PowerManager.class));
+
+        // Freeze the clocks at 24 hours after this moment in time. Several tests create sessions
+        // in the past, and QuotaController sometimes floors values at 0, so if the test time
+        // causes sessions with negative timestamps, they will fail.
+        JobSchedulerService.sSystemClock =
+                getAdvancedClock(Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC),
+                        24 * HOUR_IN_MILLIS);
+        JobSchedulerService.sUptimeMillisClock = getAdvancedClock(
+                Clock.fixed(SystemClock.uptimeClock().instant(), ZoneOffset.UTC),
+                24 * HOUR_IN_MILLIS);
+        JobSchedulerService.sElapsedRealtimeClock = getAdvancedClock(
+                Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC),
+                24 * HOUR_IN_MILLIS);
+
+        // Initialize real objects.
+        // Capture the listeners.
+        ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        mDeviceIdlenessTracker = new DeviceIdlenessTracker();
+        mDeviceIdlenessTracker.startTracking(mContext,
+                mJobSchedulerService, mock(IdlenessListener.class));
+
+        verify(mContext).registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
+        mBroadcastReceiver = broadcastReceiverCaptor.getValue();
+    }
+
+    @After
+    public void tearDown() {
+        if (mMockingSession != null) {
+            mMockingSession.finishMocking();
+        }
+    }
+
+    private Clock getAdvancedClock(Clock clock, long incrementMs) {
+        return Clock.offset(clock, Duration.ofMillis(incrementMs));
+    }
+
+    private void advanceElapsedClock(long incrementMs) {
+        JobSchedulerService.sElapsedRealtimeClock = getAdvancedClock(
+                JobSchedulerService.sElapsedRealtimeClock, incrementMs);
+    }
+
+    private void setBatteryState(boolean isCharging, boolean isBatteryNotLow) {
+        doReturn(isCharging).when(mJobSchedulerService).isBatteryCharging();
+        doReturn(isBatteryNotLow).when(mJobSchedulerService).isBatteryNotLow();
+        mDeviceIdlenessTracker.onBatteryStateChanged(isCharging, isBatteryNotLow);
+    }
+
+    private void setDeviceConfigLong(String key, long val) {
+        mDeviceConfigPropertiesBuilder.setLong(key, val);
+        mDeviceIdlenessTracker.processConstant(mDeviceConfigPropertiesBuilder.build(), key);
+    }
+
+    @Test
+    public void testThresholdChangeWithStablePowerChange() {
+        setDeviceConfigLong(KEY_INACTIVITY_IDLE_THRESHOLD_MS, 10 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(KEY_INACTIVITY_STABLE_POWER_IDLE_THRESHOLD_MS, 5 * MINUTE_IN_MILLIS);
+        setBatteryState(false, false);
+
+        Intent screenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF);
+        mBroadcastReceiver.onReceive(mContext, screenOffIntent);
+
+        final long nowElapsed = sElapsedRealtimeClock.millis();
+        long expectedUnstableAlarmElapsed = nowElapsed + 10 * MINUTE_IN_MILLIS;
+        long expectedStableAlarmElapsed = nowElapsed + 5 * MINUTE_IN_MILLIS;
+
+        InOrder inOrder = inOrder(mAlarmManager);
+        inOrder.verify(mAlarmManager)
+                .setWindow(anyInt(), eq(expectedUnstableAlarmElapsed), anyLong(), anyString(),
+                        eq(AppSchedulingModuleThread.getExecutor()), any());
+
+        // Advanced the clock a little to make sure the tracker continues to use the original time.
+        advanceElapsedClock(MINUTE_IN_MILLIS);
+
+        // Charging isn't enough for stable power.
+        setBatteryState(true, false);
+        inOrder.verify(mAlarmManager, never())
+                .setWindow(anyInt(), anyLong(), anyLong(), anyString(),
+                        eq(AppSchedulingModuleThread.getExecutor()), any());
+
+        // Now on stable power.
+        setBatteryState(true, true);
+        inOrder.verify(mAlarmManager)
+                .setWindow(anyInt(), eq(expectedStableAlarmElapsed), anyLong(), anyString(),
+                        eq(AppSchedulingModuleThread.getExecutor()), any());
+
+        // Battery-not-low isn't enough for stable power. Go back to unstable timing.
+        setBatteryState(false, true);
+        inOrder.verify(mAlarmManager)
+                .setWindow(anyInt(), eq(expectedUnstableAlarmElapsed), anyLong(), anyString(),
+                        eq(AppSchedulingModuleThread.getExecutor()), any());
+
+        // Still not on stable power.
+        setBatteryState(false, false);
+        inOrder.verify(mAlarmManager, never())
+                .setWindow(anyInt(), anyLong(), anyLong(), anyString(),
+                        eq(AppSchedulingModuleThread.getExecutor()), any());
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt
index eb00164..a6ba5d4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt
@@ -87,8 +87,8 @@
                 TEST_PACKAGE_1, TEST_PACKAGE_2, DEVICE_OWNER_PACKAGE, DEVICE_ADMIN_PACKAGE,
                 DEFAULT_HOME_PACKAGE, DIALER_PACKAGE, INSTALLER_PACKAGE, UNINSTALLER_PACKAGE,
                 VERIFIER_PACKAGE, PERMISSION_CONTROLLER_PACKAGE))
-        suspendPackageHelper = SuspendPackageHelper(pms, rule.mocks().injector,
-                rule.mocks().userManagerService, broadcastHelper, protectedPackages)
+        suspendPackageHelper = SuspendPackageHelper(
+                pms, rule.mocks().injector, broadcastHelper, protectedPackages)
         defaultAppProvider = rule.mocks().defaultAppProvider
         testHandler = rule.mocks().handler
         packageSetting1 = pms.snapshotComputer().getPackageStateInternal(TEST_PACKAGE_1)!!
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
index 4240373..7b381ce 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
@@ -39,7 +39,7 @@
         val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 targetPackages, true /* suspended */, null /* appExtras */,
                 null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
-                TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
+                TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
         testHandler.flush()
 
         verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
@@ -56,14 +56,14 @@
         var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 null /* packageNames */, true /* suspended */, null /* appExtras */,
                 null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
-                TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
+                TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
 
         assertThat(failedNames).isNull()
 
         failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 arrayOfNulls(0), true /* suspended */, null /* appExtras */,
                 null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
-                TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
+                TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
 
         assertThat(failedNames).isEmpty()
     }
@@ -73,7 +73,7 @@
         val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */,
                 null /* launcherExtras */, null /* dialogInfo */, TEST_PACKAGE_1, TEST_USER_ID,
-                Binder.getCallingUid(), false /* forQuietMode */, false /* quarantined */)
+                Binder.getCallingUid(), false /* quarantined */)
 
         assertThat(failedNames).asList().hasSize(1)
         assertThat(failedNames).asList().contains(TEST_PACKAGE_2)
@@ -84,7 +84,7 @@
         val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 arrayOf(DEVICE_OWNER_PACKAGE), true /* suspended */, null /* appExtras */,
                 null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
-                TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
+                TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
 
         assertThat(failedNames).asList().hasSize(1)
         assertThat(failedNames).asList().contains(DEVICE_OWNER_PACKAGE)
@@ -95,7 +95,7 @@
         val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 arrayOf(NONEXISTENT_PACKAGE), true /* suspended */, null /* appExtras */,
                 null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
-                TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
+                TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
 
         assertThat(failedNames).asList().hasSize(1)
         assertThat(failedNames).asList().contains(NONEXISTENT_PACKAGE)
@@ -108,7 +108,7 @@
         val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 knownPackages, true /* suspended */, null /* appExtras */,
                 null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
-                TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)!!
+                TEST_USER_ID, deviceOwnerUid, false /* quarantined */)!!
 
         assertThat(failedNames.size).isEqualTo(knownPackages.size)
         for (pkg in knownPackages) {
@@ -117,33 +117,19 @@
     }
 
     @Test
-    fun setPackagesSuspended_forQuietMode() {
-        val knownPackages = arrayOf(DEVICE_ADMIN_PACKAGE, DEFAULT_HOME_PACKAGE, DIALER_PACKAGE,
-                INSTALLER_PACKAGE, UNINSTALLER_PACKAGE, VERIFIER_PACKAGE,
-                PERMISSION_CONTROLLER_PACKAGE, MGMT_ROLE_HOLDER_PACKAGE)
-        val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
-                knownPackages, true /* suspended */, null /* appExtras */,
-                null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
-                TEST_USER_ID, deviceOwnerUid, true /* forQuietMode */, false /* quarantined */)!!
-
-        assertThat(failedNames.size).isEqualTo(1)
-        assertThat(failedNames[0]).isEqualTo(MGMT_ROLE_HOLDER_PACKAGE)
-    }
-
-    @Test
     fun setPackagesUnsuspended() {
         val targetPackages = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
         var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 targetPackages, true /* suspended */, null /* appExtras */,
                 null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
-                TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
+                TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
         testHandler.flush()
         Mockito.clearInvocations(broadcastHelper)
         assertThat(failedNames).isEmpty()
         failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 targetPackages, false /* suspended */, null /* appExtras */,
                 null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
-                TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
+                TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
         testHandler.flush()
 
         verify(pms, times(2)).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
@@ -191,7 +177,7 @@
         var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 arrayOf(TEST_PACKAGE_1), true /* suspended */, appExtras, null /* launcherExtras */,
                 null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid,
-                false /* forQuietMode */, false /* quarantined */)
+                false /* quarantined */)
         testHandler.flush()
         assertThat(failedNames).isEmpty()
 
@@ -209,7 +195,7 @@
         var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 targetPackages, true /* suspended */, appExtras, null /* launcherExtras */,
                 null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid,
-                false /* forQuietMode */, false /* quarantined */)
+                false /* quarantined */)
         testHandler.flush()
         Mockito.clearInvocations(broadcastHelper)
         assertThat(failedNames).isEmpty()
@@ -250,7 +236,7 @@
         var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */, launcherExtras,
                 null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid,
-                false /* forQuietMode */, false /* quarantined */)
+                false /* quarantined */)
         testHandler.flush()
         assertThat(failedNames).isEmpty()
 
@@ -265,7 +251,7 @@
         var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 arrayOf(TEST_PACKAGE_1), true /* suspended */, null /* appExtras */,
                 null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
-                TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
+                TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
         testHandler.flush()
         assertThat(failedNames).isEmpty()
 
@@ -280,7 +266,7 @@
         var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */, launcherExtras,
                 null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid,
-                false /* forQuietMode */, false /* quarantined */)
+                false /* quarantined */)
         testHandler.flush()
         assertThat(failedNames).isEmpty()
 
@@ -295,7 +281,7 @@
         var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 arrayOf(TEST_PACKAGE_1), true /* suspended */, null /* appExtras */,
                 null /* launcherExtras */, dialogInfo, DEVICE_OWNER_PACKAGE, TEST_USER_ID,
-                deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
+                deviceOwnerUid, false /* quarantined */)
         testHandler.flush()
         assertThat(failedNames).isEmpty()
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 568471d..526201f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -247,39 +247,9 @@
     }
 
     @Test
-    public void testInterceptQuietProfile_keepProfilesRunningEnabled() {
-        // GIVEN that the user the activity is starting as is currently in quiet mode and
-        // profiles are kept running when in quiet mode.
+    public void testInterceptQuietProfile() {
+        // GIVEN that the user the activity is starting as is currently in quiet mode
         when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true);
-        when(mDevicePolicyManager.isKeepProfilesRunningEnabled()).thenReturn(true);
-
-        // THEN calling intercept returns false because package also has to be suspended.
-        assertFalse(
-                mInterceptor.intercept(null, null, mAInfo, null, null,  null, 0, 0, null, null));
-    }
-
-    @Test
-    public void testInterceptQuietProfile_keepProfilesRunningDisabled() {
-        // GIVEN that the user the activity is starting as is currently in quiet mode and
-        // profiles are stopped when in quiet mode (pre-U behavior, no profile app suspension).
-        when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true);
-        when(mDevicePolicyManager.isKeepProfilesRunningEnabled()).thenReturn(false);
-
-        // THEN calling intercept returns true
-        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null,  null, 0, 0, null, null));
-
-        // THEN the returned intent is the quiet mode intent
-        assertTrue(UnlaunchableAppActivity.createInQuietModeDialogIntent(TEST_USER_ID)
-                .filterEquals(mInterceptor.mIntent));
-    }
-
-    @Test
-    public void testInterceptQuietProfileWhenPackageSuspended_keepProfilesRunningEnabled() {
-        // GIVEN that the user the activity is starting as is currently in quiet mode,
-        // the package is suspended and profiles are kept running while in quiet mode.
-        suspendPackage("com.test.suspending.package");
-        when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true);
-        when(mDevicePolicyManager.isKeepProfilesRunningEnabled()).thenReturn(true);
 
         // THEN calling intercept returns true
         assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null));
@@ -290,12 +260,10 @@
     }
 
     @Test
-    public void testInterceptQuietProfileWhenPackageSuspended_keepProfilesRunningDisabled() {
-        // GIVEN that the user the activity is starting as is currently in quiet mode,
-        // the package is suspended and profiles are stopped while in quiet mode.
+    public void testInterceptQuietProfileWhenPackageSuspended() {
         suspendPackage("com.test.suspending.package");
+        // GIVEN that the user the activity is starting as is currently in quiet mode
         when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true);
-        when(mDevicePolicyManager.isKeepProfilesRunningEnabled()).thenReturn(false);
 
         // THEN calling intercept returns true
         assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null));
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index e413663..f64ab22 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -57,6 +57,7 @@
 import android.app.usage.IUsageStatsManager;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageEvents.Event;
+import android.app.usage.UsageEventsQuery;
 import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManager;
 import android.app.usage.UsageStatsManager.StandbyBuckets;
@@ -113,6 +114,8 @@
 import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
 import com.android.server.utils.AlarmQueue;
 
+import libcore.util.EmptyArray;
+
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
 import java.io.File;
@@ -1478,6 +1481,14 @@
      * Called by the Binder stub.
      */
     UsageEvents queryEvents(int userId, long beginTime, long endTime, int flags) {
+        return queryEventsWithTypes(userId, beginTime, endTime, flags, EmptyArray.INT);
+    }
+
+    /**
+     * Called by the Binder stub.
+     */
+    UsageEvents queryEventsWithTypes(int userId, long beginTime, long endTime, int flags,
+            int[] eventTypeFilter) {
         synchronized (mLock) {
             if (!mUserUnlockedStates.contains(userId)) {
                 Slog.w(TAG, "Failed to query events for locked user " + userId);
@@ -1488,7 +1499,7 @@
             if (service == null) {
                 return null; // user was stopped or removed
             }
-            return service.queryEvents(beginTime, endTime, flags);
+            return service.queryEvents(beginTime, endTime, flags, eventTypeFilter);
         }
     }
 
@@ -2123,7 +2134,7 @@
 
     private final class BinderService extends IUsageStatsManager.Stub {
 
-        private boolean hasPermission(String callingPackage) {
+        private boolean hasQueryPermission(String callingPackage) {
             final int callingUid = Binder.getCallingUid();
             if (callingUid == Process.SYSTEM_UID) {
                 return true;
@@ -2203,10 +2214,37 @@
             return uid == Process.SYSTEM_UID;
         }
 
+        private UsageEvents queryEventsHelper(int userId, long beginTime, long endTime,
+                String callingPackage, int[] eventTypeFilter) {
+            final int callingUid = Binder.getCallingUid();
+            final int callingPid = Binder.getCallingPid();
+            final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller(
+                    callingUid, userId);
+
+            final long token = Binder.clearCallingIdentity();
+            try {
+                final boolean hideShortcutInvocationEvents = shouldHideShortcutInvocationEvents(
+                        userId, callingPackage, callingPid, callingUid);
+                final boolean hideLocusIdEvents = shouldHideLocusIdEvents(callingPid, callingUid);
+                final boolean obfuscateNotificationEvents = shouldObfuscateNotificationEvents(
+                        callingPid, callingUid);
+                int flags = UsageEvents.SHOW_ALL_EVENT_DATA;
+                if (obfuscateInstantApps) flags |= UsageEvents.OBFUSCATE_INSTANT_APPS;
+                if (hideShortcutInvocationEvents) flags |= UsageEvents.HIDE_SHORTCUT_EVENTS;
+                if (hideLocusIdEvents) flags |= UsageEvents.HIDE_LOCUS_EVENTS;
+                if (obfuscateNotificationEvents) flags |= UsageEvents.OBFUSCATE_NOTIFICATION_EVENTS;
+
+                return UsageStatsService.this.queryEventsWithTypes(userId, beginTime, endTime,
+                        flags, eventTypeFilter);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
         @Override
         public ParceledListSlice<UsageStats> queryUsageStats(int bucketType, long beginTime,
                 long endTime, String callingPackage, int userId) {
-            if (!hasPermission(callingPackage)) {
+            if (!hasQueryPermission(callingPackage)) {
                 return null;
             }
 
@@ -2234,7 +2272,7 @@
         @Override
         public ParceledListSlice<ConfigurationStats> queryConfigurationStats(int bucketType,
                 long beginTime, long endTime, String callingPackage) throws RemoteException {
-            if (!hasPermission(callingPackage)) {
+            if (!hasQueryPermission(callingPackage)) {
                 return null;
             }
 
@@ -2256,7 +2294,7 @@
         @Override
         public ParceledListSlice<EventStats> queryEventStats(int bucketType,
                 long beginTime, long endTime, String callingPackage) throws RemoteException {
-            if (!hasPermission(callingPackage)) {
+            if (!hasQueryPermission(callingPackage)) {
                 return null;
             }
 
@@ -2277,32 +2315,25 @@
 
         @Override
         public UsageEvents queryEvents(long beginTime, long endTime, String callingPackage) {
-            if (!hasPermission(callingPackage)) {
+            if (!hasQueryPermission(callingPackage)) {
                 return null;
             }
 
-            final int userId = UserHandle.getCallingUserId();
-            final int callingUid = Binder.getCallingUid();
-            final int callingPid = Binder.getCallingPid();
-            final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller(
-                    callingUid, userId);
+            return queryEventsHelper(UserHandle.getCallingUserId(), beginTime, endTime,
+                    callingPackage, /* eventTypeFilter= */ EmptyArray.INT);
+        }
 
-            final long token = Binder.clearCallingIdentity();
-            try {
-                final boolean hideShortcutInvocationEvents = shouldHideShortcutInvocationEvents(
-                        userId, callingPackage, callingPid, callingUid);
-                final boolean hideLocusIdEvents = shouldHideLocusIdEvents(callingPid, callingUid);
-                final boolean obfuscateNotificationEvents = shouldObfuscateNotificationEvents(
-                        callingPid, callingUid);
-                int flags = UsageEvents.SHOW_ALL_EVENT_DATA;
-                if (obfuscateInstantApps) flags |= UsageEvents.OBFUSCATE_INSTANT_APPS;
-                if (hideShortcutInvocationEvents) flags |= UsageEvents.HIDE_SHORTCUT_EVENTS;
-                if (hideLocusIdEvents) flags |= UsageEvents.HIDE_LOCUS_EVENTS;
-                if (obfuscateNotificationEvents) flags |= UsageEvents.OBFUSCATE_NOTIFICATION_EVENTS;
-                return UsageStatsService.this.queryEvents(userId, beginTime, endTime, flags);
-            } finally {
-                Binder.restoreCallingIdentity(token);
+        @Override
+        public UsageEvents queryEventsWithFilter(@NonNull UsageEventsQuery query,
+                @NonNull String callingPackage) {
+            Objects.requireNonNull(query);
+            Objects.requireNonNull(callingPackage);
+
+            if (!hasQueryPermission(callingPackage)) {
+                return null;
             }
+            return queryEventsHelper(UserHandle.getCallingUserId(), query.getBeginTimeMillis(),
+                    query.getEndTimeMillis(), callingPackage, query.getEventTypeFilter());
         }
 
         @Override
@@ -2312,7 +2343,7 @@
             final int callingUserId = UserHandle.getUserId(callingUid);
 
             checkCallerIsSameApp(callingPackage);
-            final boolean includeTaskRoot = hasPermission(callingPackage);
+            final boolean includeTaskRoot = hasQueryPermission(callingPackage);
 
             final long token = Binder.clearCallingIdentity();
             try {
@@ -2326,7 +2357,7 @@
         @Override
         public UsageEvents queryEventsForUser(long beginTime, long endTime, int userId,
                 String callingPackage) {
-            if (!hasPermission(callingPackage)) {
+            if (!hasQueryPermission(callingPackage)) {
                 return null;
             }
 
@@ -2337,33 +2368,14 @@
                         "No permission to query usage stats for this user");
             }
 
-            final int callingUid = Binder.getCallingUid();
-            final int callingPid = Binder.getCallingPid();
-            final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller(
-                    callingUid, callingUserId);
-
-            final long token = Binder.clearCallingIdentity();
-            try {
-                final boolean hideShortcutInvocationEvents = shouldHideShortcutInvocationEvents(
-                        userId, callingPackage, callingPid, callingUid);
-                final boolean obfuscateNotificationEvents = shouldObfuscateNotificationEvents(
-                        callingPid, callingUid);
-                boolean hideLocusIdEvents = shouldHideLocusIdEvents(callingPid, callingUid);
-                int flags = UsageEvents.SHOW_ALL_EVENT_DATA;
-                if (obfuscateInstantApps) flags |= UsageEvents.OBFUSCATE_INSTANT_APPS;
-                if (hideShortcutInvocationEvents) flags |= UsageEvents.HIDE_SHORTCUT_EVENTS;
-                if (hideLocusIdEvents) flags |= UsageEvents.HIDE_LOCUS_EVENTS;
-                if (obfuscateNotificationEvents) flags |= UsageEvents.OBFUSCATE_NOTIFICATION_EVENTS;
-                return UsageStatsService.this.queryEvents(userId, beginTime, endTime, flags);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
+            return queryEventsHelper(userId, beginTime, endTime, callingPackage,
+                    /* eventTypeFilter= */ EmptyArray.INT);
         }
 
         @Override
         public UsageEvents queryEventsForPackageForUser(long beginTime, long endTime,
                 int userId, String pkg, String callingPackage) {
-            if (!hasPermission(callingPackage)) {
+            if (!hasQueryPermission(callingPackage)) {
                 return null;
             }
             if (userId != UserHandle.getCallingUserId()) {
@@ -2404,7 +2416,7 @@
                 if (actualCallingUid != callingUid) {
                     return false;
                 }
-            } else if (!hasPermission(callingPackage)) {
+            } else if (!hasQueryPermission(callingPackage)) {
                 return false;
             }
             final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller(
@@ -2454,7 +2466,7 @@
             final int packageUid = mPackageManagerInternal.getPackageUid(packageName, 0, userId);
             // If the calling app is asking about itself, continue, else check for permission.
             final boolean sameApp = packageUid == callingUid;
-            if (!sameApp && !hasPermission(callingPackage)) {
+            if (!sameApp && !hasQueryPermission(callingPackage)) {
                 throw new SecurityException("Don't have permission to query app standby bucket");
             }
 
@@ -2502,7 +2514,7 @@
             } catch (RemoteException re) {
                 throw re.rethrowFromSystemServer();
             }
-            if (!hasPermission(callingPackageName)) {
+            if (!hasQueryPermission(callingPackageName)) {
                 throw new SecurityException(
                         "Don't have permission to query app standby bucket");
             }
@@ -2556,7 +2568,7 @@
             final int packageUid = mPackageManagerInternal.getPackageUid(packageName, 0, userId);
             // If the calling app is asking about itself, continue, else check for permission.
             if (packageUid != callingUid) {
-                if (!hasPermission(callingPackage)) {
+                if (!hasQueryPermission(callingPackage)) {
                     throw new SecurityException(
                             "Don't have permission to query min app standby bucket");
                 }
@@ -2900,7 +2912,7 @@
             if (!hasPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS)) {
                 throw new SecurityException("Caller doesn't have INTERACT_ACROSS_USERS permission");
             }
-            if (!hasPermission(callingPackage)) {
+            if (!hasQueryPermission(callingPackage)) {
                 throw new SecurityException("Don't have permission to query usage stats");
             }
             synchronized (mLock) {
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index ddb2796..9b67ab6 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -70,7 +70,7 @@
  * in UsageStatsService.
  */
 class UserUsageStatsService {
-    private static final String TAG = "UsageStatsService";
+    private static final String TAG = UsageStatsService.TAG;
     private static final boolean DEBUG = UsageStatsService.DEBUG;
     private static final SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
     private static final int sDateFormatFlags =
@@ -535,10 +535,23 @@
         return queryStats(bucketType, beginTime, endTime, sEventStatsCombiner, true);
     }
 
-    UsageEvents queryEvents(final long beginTime, final long endTime, int flags) {
+    UsageEvents queryEvents(final long beginTime, final long endTime, int flags,
+            int[] eventTypeFilter) {
         if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
             return null;
         }
+
+        // Ensure valid event type filter.
+        final boolean isQueryForAllEvents = ArrayUtils.isEmpty(eventTypeFilter);
+        final boolean[] queryEventFilter = new boolean[Event.MAX_EVENT_TYPE + 1];
+        if (!isQueryForAllEvents) {
+            for (int eventType : eventTypeFilter) {
+                if (eventType < Event.NONE || eventType > Event.MAX_EVENT_TYPE) {
+                    throw new IllegalArgumentException("invalid event type: " + eventType);
+                }
+                queryEventFilter[eventType] = true;
+            }
+        }
         final ArraySet<String> names = new ArraySet<>();
         List<Event> results = queryStats(INTERVAL_DAILY,
                 beginTime, endTime, new StatCombiner<Event>() {
@@ -547,6 +560,7 @@
                             List<Event> accumulatedResult) {
                         final int startIndex = stats.events.firstIndexOnOrAfter(beginTime);
                         final int size = stats.events.size();
+
                         for (int i = startIndex; i < size; i++) {
                             Event event = stats.events.get(i);
                             if (event.mTimeStamp >= endTime) {
@@ -554,6 +568,10 @@
                             }
 
                             final int eventType = event.mEventType;
+                            if (!isQueryForAllEvents && !queryEventFilter[eventType]) {
+                                continue;
+                            }
+
                             if (eventType == Event.SHORTCUT_INVOCATION
                                     && (flags & HIDE_SHORTCUT_EVENTS) == HIDE_SHORTCUT_EVENTS) {
                                 continue;
diff --git a/tests/FlickerTests/AndroidTestTemplate.xml b/tests/FlickerTests/AndroidTestTemplate.xml
index 85709c9..ed71531 100644
--- a/tests/FlickerTests/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/AndroidTestTemplate.xml
@@ -28,7 +28,13 @@
         <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
         <!-- b/307664397 - Ensure camera has the correct permissions and doesn't show a dialog -->
         <option name="run-command"
+                value="pm grant com.google.android.GoogleCamera android.permission.CAMERA"/>
+        <option name="run-command"
+                value="pm grant com.google.android.GoogleCamera android.permission.RECORD_AUDIO"/>
+        <option name="run-command"
                 value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_FINE_LOCATION"/>
+        <option name="run-command"
+                value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_COARSE_LOCATION"/>
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="test-user-token" value="%TEST_USER%"/>